java多线程synchronized

48次阅读

共计 4128 个字符,预计需要花费 11 分钟才能阅读完成。

synchronize 可以在多个线程操作同一个成员变量或者方法时,实现同步 (或者互斥) 的效果。
synchronized 可以作用于方法,以及方法内部的代码块。

//1 
synchronized void method(){}
   //2
static synchronized void method(){}
//3
synchronized void method(){synchronized(锁对象){}}
//4
static synchronized void method(){synchronized(锁对象){}}

锁对象

那么在上面的示例中,它们分别持有的锁对象是谁?
synchronized 作用于非静态方法以及非静态方法内部的代码块,持有的是当前类的对象的锁,并且是同一个锁。作用于静态方法及其内部的代码块,持有的是当前类的 Class 对象的锁,并且和非静态方法不是同一个锁。
通过代码来验证。

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();}
}

执行结果

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 作为锁

字符串常量作为锁,会有什么结果?


        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();

打印结果

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){}}

正文完
 0