前言

相信大家都聽說過線程安全問題,在學(xué)習(xí)操作系統(tǒng)的時(shí)候有一個(gè)知識(shí)點(diǎn)是臨界資源,簡(jiǎn)單的說就是一次只能讓一個(gè)進(jìn)程操作的資源,但是我們?cè)谑褂枚嗑€程的時(shí)候是并發(fā)操作的,并不能控制同時(shí)只對(duì)一個(gè)資源的訪問和修改,想要控制那么有幾種操作,今天我們就來講講第一種方法:線程同步塊或者線程同步方法(synchronized)

實(shí)例

  1. 下面舉一個(gè)例子說明synchronized關(guān)鍵字的使用

線程同步方法

public class Sychor {    public void insert(Thread thread) {        for (int i = 0; i < 10; i++) {
            System.out.println(thread.getName() + "輸出:  " + i);
        }

    }    public static void main(String[] args) {        final Sychor sychor = new Sychor();

        Thread t1 = new Thread() {            public void run() {
                sychor.insert(Thread.currentThread());
            };
        };

        Thread t2 = new Thread() {            public void run() {
                sychor.insert(Thread.currentThread());
            };
        };

        t1.start();
        t2.start();
    }
}

其中輸出結(jié)果為下圖

移動(dòng)開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),手機(jī)維修培訓(xùn),手機(jī)軟件培訓(xùn)

從上面的結(jié)果可以看出這里的兩個(gè)線程是同時(shí)執(zhí)行insert()方法的,下面我們?cè)谠械拇a上添加synchronized關(guān)鍵字看看效果如何,代碼如下:

public class Sychor {    public synchronized void insert(Thread thread) {        for (int i = 0; i < 10; i++) {
            System.out.println(thread.getName() + "輸出:  " + i);
        }

    }    public static void main(String[] args) {        final Sychor sychor = new Sychor();

        Thread t1 = new Thread() {            public void run() {
                sychor.insert(Thread.currentThread());
            };
        };

        Thread t2 = new Thread() {            public void run() {
                sychor.insert(Thread.currentThread());
            };
        };

        t1.start();
        t2.start();
    }
}

上面程序的運(yùn)行結(jié)果我就不列出來,自己可以試試,總之就是加上了synchronized關(guān)鍵字使得線程是一個(gè)一個(gè)的執(zhí)行的,只有先執(zhí)行完一個(gè)線程才能執(zhí)行了另外一個(gè)線程。

線程同步塊

當(dāng)然上面的我們使用的是線程同步方法,我們可以使用線程同步塊,這兩個(gè)相比線程同步塊更加靈活,只需要將需要同步的代碼放在同步塊中即可,代碼如下;

public class Sychor {    public void insert(Thread thread) {        synchronized (this) {            for (int i = 0; i < 10; i++) {
                System.out.println(thread.getName() + "輸出:  " + i);
            }
            
        }
        

    }    public static void main(String[] args) {        final Sychor sychor = new Sychor();

        Thread t1 = new Thread() {            public void run() {
                sychor.insert(Thread.currentThread());
            };
        };

        Thread t2 = new Thread() {            public void run() {
                sychor.insert(Thread.currentThread());
            };
        };

        t1.start();
        t2.start();
    }
}

從上面的代碼中可以看出這種方式更加靈活,只需要將需要同步的代碼方法在同步塊中,不需要同步的代碼放在外面

詳細(xì)原因

  1. 我們知道每一個(gè)對(duì)象都有一把鎖,當(dāng)我們使用線程同步方法或者線程同步塊的時(shí)候?qū)嶋H上獲得是對(duì)象的唯一的一把鎖,當(dāng)一個(gè)線程獲得了這唯一的鎖,那么其他的線程只能拒之門外了,注意這里我們說是一個(gè)對(duì)象,也就是說是同一個(gè)對(duì)象,如果是不同的對(duì)象,那么就不起作用了,因?yàn)椴煌瑢?duì)象有不同的對(duì)象鎖,比如我們將上面的程序改成如下:

public class Sychor {    public void insert(Thread thread) {        synchronized (this) {            for (int i = 0; i < 10; i++) {
                System.out.println(thread.getName() + "輸出:  " + i);
            }
        }

    }    public static void main(String[] args) {        //第一個(gè)線程
        Thread t1 = new Thread() {            public void run() {
                Sychor sychor = new Sychor();   //在run() 方法中創(chuàng)建一個(gè)對(duì)象
                sychor.insert(Thread.currentThread());
            };
        };        //第二個(gè)線程
        Thread t2 = new Thread() {            public void run() {
                Sychor sychor = new Sychor();  //創(chuàng)建另外的一個(gè)對(duì)象
                sychor.insert(Thread.currentThread());
            };
        };

        t1.start();
        t2.start();
    }
}

從上面的結(jié)果可知,此時(shí)線程同步塊根本不起作用,因?yàn)樗麄冋{(diào)用的是不同對(duì)象的insert方法,獲得鎖是不一樣的

  1. 上面我們已經(jīng)說過一個(gè)對(duì)象有一把鎖,線程同步方法和線程同步塊實(shí)際獲得的是對(duì)象的鎖,因此線程同步塊的括號(hào)中填入的是this,我們都知道this在一個(gè)類中的含義

  1. 一個(gè)類也有唯一的一把鎖,我們前面說的是使用對(duì)象調(diào)用成員方法,現(xiàn)在如果我們要調(diào)用類中的靜態(tài)方法,那么我們可以使用線程同步方法或者同步塊獲得類中的唯一一把鎖,那么對(duì)于多個(gè)線程同時(shí)調(diào)用同一個(gè)類中的靜態(tài)方法就可以實(shí)現(xiàn)控制了,代碼如下:

public class Sychor {    // 靜態(tài)方法
    public static synchronized void insert(Thread thread)  
    {        for(int i=0;i<10;i++)
        {
            System.out.println(thread.getName()+"輸出     "+i);
        }
    }    public static void main(String[] args) {        //第一個(gè)線程
        Thread t1 = new Thread() {            public void run() {
                Sychor.insert(Thread.currentThread());  //直接使用類調(diào)用靜態(tài)方法
            };
        };        //第二個(gè)線程
        Thread t2 = new Thread() {            public void run() {
                Sychor.insert(Thread.currentThread());   //直接使用類調(diào)用靜態(tài)方法
            };
        };

        t1.start();
        t2.start();
    }
}

注意

  1. 要想實(shí)現(xiàn)線程安全和同步控制,如果執(zhí)行的是非static同步方法或者其中的同步塊,那么一定要使用同一個(gè)對(duì)象,如果調(diào)用的是static同步方法或者其中的同步塊那么一定要使用同一個(gè)類去調(diào)用

  1. 如果一個(gè)線程訪問的是static同步方法,而另外一個(gè)線程訪問的是非static的同步方法,此時(shí)這兩個(gè)是不會(huì)發(fā)生沖突的,因?yàn)橐粋€(gè)是類的鎖,一個(gè)是對(duì)象的鎖

  1. 如果使用線程同步塊,那么同步塊中的代碼是控制訪問的,但是外面的代碼是所有線程都可以訪問的

  1. 當(dāng)一個(gè)正在執(zhí)行同步代碼塊的線程出現(xiàn)了異常,那么jvm會(huì)自動(dòng)釋放當(dāng)前線程所占用的鎖,因此不會(huì)出現(xiàn)由于異常導(dǎo)致死鎖的現(xiàn)象

參考文章

http://www.cnblogs.com/dolphin0520/p/3923737.html

更多文章請(qǐng)看本人的獨(dú)立博客https://chenjiabing666.github.io/

http://www.cnblogs.com/Chenjiabing/p/7057635.html