1. 程式人生 > >Java Thread系列(三)線程安全

Java Thread系列(三)線程安全

AI 資源 習慣 get string tar rup end 就是

Java Thread系列(三)線程安全

一、什麽是線程安全

線程安全概念:當多個線程訪問某一個類(對象或方法)時,這個類始終都能表現出正確的行為,那麽這個類(對象或方法)就是線程安全的。

線程安全來說,需要滿足以下兩個特性:

  • 原子性

  • 可見性

public class MyThread extends Thread {

    private int count = 5;

    //synchronized加鎖 同步鎖
    public /*synchronized*/ void run () {
        System.out.printf("線程%s執行,count:%s\n
", this.currentThread().getName(),--count); } public static void main(String[] args) { /** * 當多個線程訪問 MyThread 的 run 方法時,以排隊的方式進行處理(這裏的排序是按照 CPU 分配的先後順序而定的) * 一個線程要執行 synchronized 修飾的方法時 * 1. 嘗試獲得鎖,如果拿到鎖則執行該方法 * 2. 這個線程就會不斷嘗試獲得這把鎖,直到拿到為止,而且是多個線程同時去競爭這把鎖。(也就是會有鎖競爭的問題)
*/ MyThread thread = new MyThread(); new Thread(thread, "t1").start(); new Thread(thread, "t2").start(); new Thread(thread, "t3").start(); new Thread(thread, "t4").start(); new Thread(thread, "t5").start(); new Thread(thread, "t6").start
(); } }

執行結果:

//MyThread 的 run() 方法不加鎖 synchronized
線程t1執行,count:4
線程t4執行,count:1
線程t5執行,count:1
線程t3執行,count:2
線程t2執行,count:3

//run() 加鎖 synchronized,期待的結果 
線程t1執行,count:4
線程t2執行,count:3
線程t5執行,count:2
線程t4執行,count:1
線程t3執行,count:0

由此可見:

  1. 多個線程要執行 synchronized 修飾的方法時,必須獲取對象鎖,如果得不到這把鎖,就處於等待狀態,直到獲取這把鎖才執行這個方法。

二、什麽是鎖:對象鎖和類鎖

多個線程多個鎖,多個線程,每個線程都可以拿到自己指定的鎖,分別獲得鎖後執行 synchronized 修辭的方法。

  • 同步(synchronized)的概念就是共享,我們要牢牢記住“共享”這兩個字,如果不是共享的資源,就沒有必要進行同步。

  • 異步(asynchronized)的概念就是獨立,相互之間不受任何制約。eg: Ajax

public class MutiThread {

    private /*static*/ int num = 0;

    /**
     * synchronized:對象鎖,兩個對象,線程獲得的就是兩個不同的鎖,互不影響
     * static synchronized:表示類級別的鎖,即便多個對象也是相同的鎖
     */
    public /*static*/ synchronized void printNum (String tag) {
        try {
            if("a".equalsIgnoreCase(tag)) {
                System.out.printf("tag %s 設置成功\n", tag);
                num = 100;
                Thread.sleep(1000);
            } else {
                System.out.printf("tag %s 設置成功\n", tag);
                num = 200;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.printf("tag %s , num=%s\n", tag, num);
    }

    public static void main(String[] args) {
        
        final MutiThread m1 = new MutiThread();
        final MutiThread m2 = new MutiThread();
        new Thread(new Runnable() {
            public void run() {
                m1.printNum("a");
            }
        }).start();
        new Thread(new Runnable() {
            public void run() {
                m1.printNum("b");
            }
        }).start();
    }
}

執行結果:

//run()方法用synchronized修辭
tag b 設置成功
tag b , num=200
tag a 設置成功
tag a , num=100

//run()方法用static synchronized修辭
tag a 設置成功
tag a , num=100
tag b 設置成功
tag b , num=200

由此可見:

  1. static synchronized 修辭的方法,屬於類級別的鎖,多個對象共享同一把鎖。

  2. synchronized 修辭的方法,屬於對象鎖,一個對象一把鎖。

三、臟讀

對於對象的同步和異步的方法,一定要考慮問題的整體性,不然就會出現數據不一致的錯誤,很經典的錯誤就是臟讀(dirtyread)

public class DirtyRead {

    private String username;
    private String password;

    public /*synchronized*/ void setValue(String username, String password) {
        this.username = username;

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            //e.printStackTrace();
        }
        this.password = password;
        System.out.printf("username=%s; password=%s\n", username, password);
    }

    public /*synchronized*/ void getValue() {
        System.out.printf("username=%s; password=%s\n", username, password);
    }

    public static void main(String[] args) throws InterruptedException {
        final DirtyRead dirtyRead = new DirtyRead();
        new Thread(new Runnable() {
            public void run() {
                dirtyRead.setValue("admin", "admin");
            }
        }).start();
        Thread.sleep(1000);
        dirtyRead.getValue();
        //username=admin; password=null 不加鎖,產生臟讀
        //username=admin; password=admin 加鎖
    }
}

執行結果:

  1. setValue() 執行要 2s,而主程序 1s 時調用 getValue() ,這時username 已經賦值,而 password 仍未賦值,產生了臟讀,username=admin; password=null

  2. setValue() 和 getValue() 都對 username 和 password 操作,所以要避免產生臟讀,需要對這兩個方法都加鎖 synchronized。


每天用心記錄一點點。內容也許不重要,但習慣很重要!

Java Thread系列(三)線程安全