关于synchronized随笔记录

程序的改良优化需要建立在有坚实的基础,如果在不了解其内部机制,改良也仅仅是“形式主义”。

结合开始CodeReview的例子:

你的同事在CodeReview时,要求你将实例方法上的synchronized,改为效率更高的同步代码块方式。在你不清楚同步代码的用法时,网上搜到了一段synchronized(this){}代码,复制下来发现也能用,此时你以为你改良优化了代码。但实际上,你可能只是做了一点形式主义上的优化。

 

为什么这么说?这需要清楚地认识同步代码块到底应该怎么用。

3.1)synchronized(this){...}

this关键字所代表的意思是该对象实例,换句话说,这种用法synchronized锁住的仍然是对象实例,他和public synchronized void demo(){}可以说仅仅是做了语法上的改变。 

复制代码
 1 /**
 2 * 2019-06-13
 3 * Created with OKevin.
 4 **/
 5 public class Demo {
 6   
 7    public synchronized void demo1() {
 8        while (true) {  //死循环目的是为了让线程一直持有该锁
 9            System.out.println(Thread.currentThread());
10        }
11    }
12 
13    public synchronized void demo2() {
14        while (true) {
15            System.out.println(Thread.currentThread());
16        }
17    }
18 }
复制代码

改为以下方式: 

复制代码
 1 /**
 2 * Description:
 3 * synchronized同步代码块对本实例加锁(this)
 4 * 假设demo1与demo2方法不相关,此时两个线程对同一个对象实例分别调用demo1与demo2,只要其中一个线程获取到了锁即执行了demo1或者demo2,此时另一个线程会永远处于阻塞状态
 5 * 2019-06-13
 6 * Created with OKevin.
 7 */
 8 public class Demo {
 9 
10    public void demo1() {
11        synchronized (this) {
12            while (true) {  //死循环目的是为了让线程一直持有该锁
13                System.out.println(Thread.currentThread());
14            }
15        }
16    }
17 
18    public void demo2() {
19        synchronized (this) {
20            while (true) {
21                System.out.println(Thread.currentThread());
22            }
23        }
24    }
25 }
复制代码

也许后者在JVM中可能会做一些特殊的优化,但从代码分析上来讲,两者并没有做到很大的优化,线程1执行demo1,线程2执行demo2,由于两个方法均是抢占对象实例的锁,只要有一个线程获取到锁,另外一个线程只能阻塞等待,即使两个方法不相关。

3.2)private Object obj = new Object();    synchronized(obj){...}

复制代码
 1 /**
 2 * Description:
 3 * synchronized同步代码块对对象内部的实例加锁
 4 * 假设demo1与demo2方法不相关,此时两个线程对同一个对象实例分别调用demo1与demo2,均能获取各自的锁
 5 * 2019-06-13
 6 * Created with OKevin.
 7 */
 8 public class Demo {
 9    private Object lock1 = new Object();
10    private Object lock2 = new Object();
11 
12    public void demo1() {
13        synchronized (lock1) {
14            while (true) {  //死循环目的是为了让线程一直持有该锁
15                System.out.println(Thread.currentThread());
16            }
17        }
18    }
19 
20    public void demo2() {
21        synchronized (lock2) {
22            while (true) {
23                System.out.println(Thread.currentThread());
24            }
25        }
26    }
27 }
复制代码

经过上面的分析,看到这里,你可能会开始懂了,可以看到demo1方法中的同步代码块锁住的是lock1对象实例,demo2方法中的同步代码块锁住的是lock2对象实例。如果线程1执行demo1,线程2执行demo2,由于两个方法抢占的是不同的对象实例锁,也就是说两个线程均能获取到锁执行各自的方法(当然前提是两个方法互不相关,才不会出现逻辑错误)。

 

3.3)synchronized(Demo.class){...}

这种形式等同于抢占获取类锁,这种方式,同样和3.1一样,收效甚微。

3.1和3.2在Java上差得多。3.1就是普通的在实例方法上加锁,如果一个类里存在两个同步方法,两个方法都是synchronized(this),那么在这两个方法同时只能被一个线程执行,换句话说,线程1如果执行了1个方法,那么另外1个方法此时就不能被其他线程执行;3.2的话因为锁住的对象实例不同,如果同步方法1是synchronize(obj1),同步方法2是synchronized(obj2),那么他们可以同时被两个线程执行,互不相关。

posted @ 2020-03-30 14:58  _dreamShadow  阅读(24)  评论(0)    收藏  举报