java多線程:synchronized

synchronize能夠在多個線程操做同一個成員變量或者方法時,實現同步(或者互斥)的效果。
synchronized能夠做用於方法,以及方法內部的代碼塊。異步

//1 
synchronized void method(){}
   //2
static synchronized void method(){}
//3
synchronized void method(){
    synchronized(鎖對象){
    }
}
//4
static synchronized void method(){
    synchronized(鎖對象){
    }
}

鎖對象

那麼在上面的示例中,它們分別持有的鎖對象是誰?
synchronized做用於非靜態方法以及非靜態方法內部的代碼塊,持有的是當前類的對象的鎖,而且是同一個鎖。做用於靜態方法及其內部的代碼塊,持有的是當前類的Class對象的鎖,而且和非靜態方法不是同一個鎖。
經過代碼來驗證。ide

public class SynchronizedTest {

    private synchronized void test1(){
        for (int x = 0; x < 5; x++) {

            System.out.println("test1---"+x);
        }
    }
    private  void test2(){
        synchronized(this) {
            for (int x = 0; x < 5; x++) {

                System.out.println("---test2---"+x);
            }
        }
    }

    private static synchronized void test3(){
        for (int x = 0; x < 5; x++) {

            System.out.println("------test3---"+x);
        }
    }

    private static  void test4(){
        synchronized (SynchronizedTest.class){
            for (int x = 0; x < 5; x++) {

                System.out.println("---------test4---"+x);
            }
        }
    }

    public static void main(String[] args) {
        SynchronizedTest synchronizedTest = new SynchronizedTest();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronizedTest.test1();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronizedTest.test2();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                test3();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                test4();
            }
        }).start();
    }
}

執行結果this

test1---0
------test3---0
test1---1
------test3---1
test1---2
test1---3
------test3---2
test1---4
------test3---3
------test3---4
---test2---0
---test2---1
---test2---2
---test2---3
---test2---4
---------test4---0
---------test4---1
---------test4---2
---------test4---3
---------test4---4

test1和test2不會交叉執行,test3和test4也不會交叉執行。非靜態方法以及方法內部的代碼塊持有的是同一個對象鎖,它們是同步執行的。靜態方法和內部的代碼塊持有的是當前類的Class對象鎖,它們是同步執行的。而靜態方法和非靜態方法持有的不是同一個鎖,它們是異步的。線程

String做爲鎖

字符串常量做爲鎖,會有什麼結果?code

final String a = "100";
        final String b = "100";
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (a){
                    while (true){
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName());
                    }
                }
            }
        },"thread-a").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (b){
                    while (true){
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName());
                    }
                }
            }
        },"thread-b").start();

這裏字符串a和b雖然是兩個對象,可是聲明b時,會將字符串常量池中已存在的a的值直接賦給b。這樣a和b實際上是同樣的。這樣線程thread-a和thread-b同時搶佔同一個鎖,一旦一個線程搶到該鎖,另外一個線程就再也獲取不到該鎖。對象

synchronized不具備繼承性

本身覆蓋了父類被synchronized修飾的方法,子類方法若是須要同步性,也須要用synchronized修飾。
定義子類Sub繼承自SynchronizedTest繼承

class Sub extends SynchronizedTest{
    @Override
    public void test2() {
        //super.test2();
        for (int x = 0; x < 15; x++) {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
        }
    }
}

而後開啓兩個線程調用Sub的test2()方法。字符串

final  Sub sub = new Sub();
       new Thread(new Runnable() {
           @Override
           public void run() {
               sub.test2();
           }
       },"Sub---A").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                sub.test2();
            }
        },"Sub---B").start();

打印結果get

Sub---A
Sub---B
Sub---A
Sub---B
Sub---A
Sub---B
Sub---A
Sub---B

可見Sub的test2()方法並無同步性。也就是synchronized不能被繼承。同步

可重入鎖

使用synchronized時,當一個線程請求一個對象鎖時,再次請求該鎖是能夠當即獲得的。

public class SynchronizedTest {

    private synchronized void test1(){
        for (int x = 0; x < 5; x++) {

            System.out.println("test1---"+x);
        }
        test2();
    }
    public  void test2(){
        synchronized(this) {
            for (int x = 0; x < 5; x++) {

                System.out.println("---test2---"+x);
            }
        }
    }
    
    public static void main(String[] args) {
        SynchronizedTest synchronizedTest = new SynchronizedTest();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronizedTest.test1();
            }
        }).start();
        
    }
 }

在方法test1()中調用方法test2(),並不須要等待鎖,而是當即獲取到鎖。
把子類Sub的test2()方法也改爲synchronized修飾。並在其內部調用父類的test2()方法。能得到鎖嗎?

class Sub extends SynchronizedTest{
    @Override
    public synchronized void test2() {
        //調用父類的同步方法
        super.test2();
        for (int x = 0; x < 15; x++) {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
        }
    }
}

打印結果

---test2---0
---test2---1
---test2---2
---test2---3
---test2---4
Sub---A
Sub---A
Sub---A
Sub---A

能夠看到父類方法執行完畢,子類的方法當即執行。可見,在子父類的繼承關係中,也支持可重入鎖這個特性。

出現異常,會自動釋放鎖

同步方法與同步代碼塊

synchronized做用於整個方法,可能引發方法執行效率降低。建議將方法內部須要同步的代碼用synchronized修飾,也就是synchronized代碼塊。

多個同步鎖對象

在一個類中假若有多個同步方法,它們之間並不須要互斥。那麼使用同一個鎖,會大大下降效率。能夠定義多個同步鎖對象。

Object obj1 = new Object();
 Object obj2 = new Object();
    
 public void method1(){
  synchronized(obj1){

}
}

 public void method2(){
  synchronized(obj2){

}
}
相關文章
相關標籤/搜索