多線程之線程可見性synchronized

synchronized的規定

  • 線程解鎖前,必須把共享變量刷新到主內存
  • 線程加鎖前將清空工做內存共享變量的值,須要從主存中獲取共享變量的值。
加鎖(synchronized 同步)的功能不單單侷限於互斥行爲,同時還存在另一個重要的方面:內存可見性。咱們不只但願防止某個線程正在使用對象狀態而另外一個線程在同時修改該狀態,並且還但願確保當一個線程修改了對象狀態後,其餘線程可以看到該變化。而線程的同步偏偏也可以實現這一點。

內置鎖能夠用於確保某個線程以一種可預測的方式來查看另外一個線程的執行結果。爲了確保全部的線程都能看到共享變量的最新值,能夠在全部執行讀操做或寫操做的線程上加上同一把鎖。下圖示例了同步的可見性保證。java

img

當線程 A 執行某個同步代碼塊時,線程 B 隨後進入由同一個鎖保護的同步代碼塊,這種狀況下能夠保證,當鎖被釋放前,A 看到的全部變量值(鎖釋放前,A 看到的變量包括 y 和 x)在 B 得到同一個鎖後一樣能夠由 B 看到。換句話說,當線程 B 執行由鎖保護的同步代碼塊時,能夠看到線程 A 以前在同一個鎖保護的同步代碼塊中的全部操做結果。若是在線程 A unlock M 以後,線程 B 才進入 lock M,那麼線程 B 均可以看到線程 A unlock M 以前的操做,能夠獲得 i=1,j=1。若是在線程 B unlock M 以後,線程 A 才進入 lock M,那麼線程 B 就不必定能看到線程 A 中的操做,所以 j 的值就不必定是 1。

synchronized線程可見性安全案例

package com.keytech.task;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class SynchronizedTestOne {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        Rumenzz r=new Rumenzz();
        //線程1
        executorService.execute(()->{
              r.setAge(200);
        });
        //線程2
        executorService.execute(()->{
            System.out.println(r.getAge());
        });

        executorService.shutdown();
    }
}


class Rumenzz{
    private Integer age=0;

    public synchronized Integer getAge() {
        return age;
    }

    public synchronized void setAge(Integer age) {
        this.age = age;
    }
}
以上代碼是線程安全的,輸出 0200,由於線程1和線程2的執行順序不同。爲了保證結果的一致性,須要控制線程的執行順序。
package com.keytech.task;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @className: SynchronizedTestOne
 * @description: TODO 類描述
 * @author: mac
 * @date: 2021/1/1
 **/
public class SynchronizedTestOne {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        Rumenzz r=new Rumenzz();
        CountDownLatch c=new CountDownLatch(1);

        executorService.execute(()->{
            try {
                c.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(r.getAge());


        });
        executorService.execute(()->{
            try {
                Thread.sleep(5000);
                c.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            r.setAge(200);
        });

        //關閉線程池
        executorService.shutdown();
    }
}


class Rumenzz{
    private Integer age=0;

    public synchronized Integer getAge() {
        return age;
    }

    public synchronized void setAge(Integer age) {
        this.age = age;
    }
}
線程安全輸出 200

相關文章
相關標籤/搜索