synchronized的三种应用方式:java
实例方法
,当前实例加锁,进入同步代码前要得到当前实例的锁;静态方法
,当前类加锁,进去同步代码前要得到当前类对象的锁;代码块
,这须要指定加锁的对象,对所给的指定对象加锁,进入同步代码前要得到指定对象的锁。用法总结以下:安全
注:并发
synchronized不能够被继承
,父类某个方法加了synchronized,若子类覆写了该方法,子类要想同步还得在子类方法上加上synchronized关键字。【问题】实现一个容器,提供两个方法,add,size。写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束。
public class MyContainer {app
private static List<Integer> lists = new ArrayList<>(); public static void main(String[] args) { final Object lock = new Object(); //监控线程 new Thread(()->{ synchronized (lock) { System.out.println("thread 2 start..."); if(lists.size() != 5) { try { lock.wait(); } catch (Exception e) { e.printStackTrace(); } } System.out.println("thread 2 end."); lock.notify(); } }, "t2").start(); new Thread(()->{ synchronized (lock) { for(int i = 0; i < 10; i++) { System.out.println("thread 1, add " + i); lists.add(i); if(lists.size() == 5) { lock.notify(); try { lock.wait(); } catch (Exception e) { e.printStackTrace(); } } try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } } } }, "t1").start(); }
}性能
注:wait()会马上释放synchronized(obj)中的obj锁,以便其余线程能够执行obj.notify(),可是notify()不会马上马上释放sycronized(obj)中的obj锁,必需要等notify()所在线程执行完synchronized(obj)块中的全部代码才会释放这把锁
。因此,在t1中,调用了lock对象的notify方法以后,再调用lock的wait方法释放锁,而在t2被唤醒以后,继续执行,最后还要调用lock对象的notify方法去唤醒此时处在wait状态的t1优化
先经过下面简单的例子看下:spa
public class Synchronize {操作系统
public static void main(String[] args) { synchronized (Synchronize.class){ System.out.println("Synchronize"); } }
}.net
使用 javap -c Synchronize
能够查看编译以后的具体信息线程
从编译后的结果能够看到:在同步方法调用前加了一个 monitorenter
指令,在退出方法和异常处插入了 monitorexit
的指令
实现原理:JVM 是经过进入、退出对象监视器( Monitor )
来实现对方法、同步块的同步的,具体实现是在编译以后同步代码块采用添加moniterenter、moniterexit
,同步方法使用ACC_SYNCHRONIZED
标记符隐式实现。每一个对象都有一个monitor与之关联
,运行到moniterenter时尝试获取对应monitor的全部权,获取成功就将monitor的进入数加1(因此是可重入锁
,也被称为重量级锁
),不然就阻塞,拥有monitor的线程运行到moniterexit时进入数减1,为0时释放monitor。其本质就是对一个对象监视器( Monitor )进行获取,而这个获取过程具备排他性从而达到了同一时刻只能一个线程访问的目的
。Java内置的synchronized关键字能够认为是管程模型中的MESA模型
的简化版。
Java对象与Monitor关联关系示意图以下:
JVM堆中存放的是对象实例,每个对象都有对象头,对象头里有Mark Word,里面存储着对象的hashCode、GC分代年龄以及锁信息。如图所示,重量级锁中存有指向monitor的指针。
其中ObjectMonitor中几个关键字段的含义以下:
_count:记录owner线程获取锁的次数。这句话很好理解,这也决定了synchronized是可重入的。
_owner:指向拥有该对象的线程
_WaitSet:主要存放全部wait的线程的对象,也就是说若是有线程处于wait状态,将被挂入这个队列,调用了wait()方法线程会进入该队列
。
_EntryList:全部在等待获取锁的线程的对象,也就是说若是有线程处于等待获取锁的状态的时候,将被挂入这个队列。
详情请参考:https://www.jianshu.com/p/32e...
详细过程请参考:https://www.hollischuang.com/...
Java内存模型:
Java内存模型虽然有助加快执行速度,可是也带来了新的问题。不一样线程之间是没法之间访问对方工做内存中的变量,线程间的变量值传递均须要经过主内存来完成,那线程的操做结果怎么让其余线程可见呢?这便须要先行发生
(happens-before
)原则来保证了。
先行发生规则中有以下2条:
即:若是有2个线程A和B,则根据规则1,A线程释放锁 happens-before B线程获取锁,根据规则2,那A线程的操做结果对B线程是可见的。
从上图能够看出,线程A会首先先从主内存中读取共享变量a=0的值而后将该变量拷贝到本身的本地内存,进行加1操做后,再将该值刷新到主内存,整个过程即为线程A 加锁-->执行临界区代码-->释放锁相对应的内存语义。线程B获取锁的时候一样会从主内存中读取共享变量a的值,这个时候就是最新的值1,而后将该值拷贝到线程B的工做内存中去,释放锁的时候一样会重写到主内存中。
即:释放锁的时候会将值刷新到主内存中,其余线程获取锁时会强制从主内存中获取最新的值
。这也验证了A happens-before B,A的执行结果对B是可见的。
详情请参考:https://www.jianshu.com/p/151...
高效并发是从JDK 1.5 到 JDK 1.6的一个重要改进,HotSpot虚拟机开发团队在这个版本中花费了很大的精力去对Java中的锁进行优化,如适应性自旋、锁消除、锁粗化、轻量级锁和偏向锁
等。这些技术都是为了在线程之间更高效的共享数据,以及解决竞争问题。但对Java开发者而言,只须要知道想在加锁的时候使用synchronized就能够了,具体的锁的优化是虚拟机根据竞争状况自行决定的
。
因为Java的线程是映射到操做系统原生线程之上的,若是要阻塞或唤醒一个线程就须要操做系统的帮忙,这就要从用户态转换到内核态,所以状态转换须要花费不少的处理器时间,因此优化的想法主要是能不阻塞线程就不阻塞。
线程不停地执行循环体
,不进行线程状态的改变。若是在锁被占用的时间很短的状况下,自旋等待的效果会很是好,反之,若是锁被占用的时间很长,自旋就会浪费CPU,因此自旋要有必定限度。在JDK1.6后,自旋的时间再也不固定
,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定,这即是自适应自旋了。逃逸分析
,判断出代码块中不存在多个线程共享的数据,便会在编译后将锁去掉
。好比:咱们常常在代码中使用StringBuffer做为局部变量,而StringBuffer中的append是线程安全的,有synchronized修饰的,可是做为局部变量并不须要共享,因此这个时候便会进行锁消除的优化。加锁的范围扩大
。
会被粗化成:
经过CAS操做尝试把monitor的_owner字段设置为当前线程
,若是更新成功了,那么代表这个线程就拥有了该对象的锁,并将对象头的Mark Word的锁标志位转变为"00",即表明此对象处于轻量级锁定状态。若是更新失败,则膨胀为重量级锁,等待锁的线程须要进入阻塞状态。经过ObjectMonitor类的源码能够看出:锁会偏向于第一个得到它的线程,若是在接下来执行过程当中,该锁没有被其余线程获取,则持有偏向锁的线程将永远不须要再进行同步
。若是说轻量级锁是在无竞争的状况下使用CAS操做去消除同步使用的互斥量,那么偏向锁就是在无竞争的状况下把整个同步都消除掉,连CAS操做都不作了。但这一切都是在无竞争的状况下,若是有另一个线程尝试去获取这个锁,那偏向模式便宣告结束。细节请参考:https://www.hollischuang.com/...
本文从synchronized的用法开始,而后逐步深刻介绍synchronized的实现原理,其实质是对管程模型的一种实现。虽然在用的时候就是一个关键字,但背后的内容却十分丰富,写本文的过程当中,参考了许多大牛的博客,受益良多。
https://blog.csdn.net/weixin_...
https://www.jianshu.com/p/32e...
https://www.jianshu.com/p/d53...
https://www.hollischuang.com/...
https://www.hollischuang.com/...
https://www.hollischuang.com/...
https://www.jianshu.com/p/e62...
https://www.jianshu.com/p/27f...
https://www.jianshu.com/p/151...