当前位置: 首页 > 后端技术 > Java

JAVA线程安全之synchronized

时间:2023-04-01 20:03:49 Java

上一篇文章分析了voliate,今天我们来分析一下sychroniazed。与voliate相比,sychronized是一种重量级的同步机制,因为sychronized需要对共享对象进行加锁。如果其他并发线程想要访问共享对象,则需要阻塞等待。阻塞和唤醒需要更多的系统资源参与,所以是重量级的同步机制。sychronized虽然是重量级的同步机制,但是比voliate要容易理解得多:*sychronized是通过内存锁来实现同步的。sychronized可以保证线程安全,可以完全避免线程安全问题。显式指定时,sychronized直接锁定对象。否则,如果sychronized作用于实例方法,则对象实例被锁定。如果sychronized作用于类方法(静态区或静态方法等),则该类对象被锁定。我们可以说下面的代码是线程安全的,在多线程环境下,调用者可以得到正确的返回结果。publicclassAccount{privatesychronizedintcounter=0;publicvoiddoAddCounter(){for(intj=0;j<100;j++){counter++;}}publicintgetCounter(){返回计数器;}}sychronized是如何工作的是的,当一个多线程应用程序访问一个sychronized共享变量时,它首先获取该变量的锁。如果变量已经有其他(非当前线程)锁,则当前线程必须阻塞等待,否则,如果变量有当前线程的锁,或者没有锁,则当前线程获得执行权并同时锁定共享变量。因此,当前线程可以多次获取共享变量的锁,对于多个锁,必须多次释放锁。JDK6之后,JAVA朝着“轻量级锁”的方向改进了同步锁机制,加入了自旋锁、锁淘汰、锁粗化、偏向锁等完善的机制。所有这些变换都基于一个原则:不能加锁就不加锁,因为加锁和释放锁都意味着线程切换,而线程切换操作需要消耗系统资源。JDK对sychronized锁机制的改造包括:自旋锁:基于大多数应用获取锁后会在短时间内完成应用处理的假设,让请求线程自旋一段时间再获取锁---cpuidling,除了等待锁被释放之外什么也不做。锁消除:代码需要加锁,但是虚拟机判断不需要加锁就不加锁。锁粗化:比如一段代码被锁在一个循环中。这种情况会导致频繁加锁。虚拟机适当扩大锁范围,变成锁,避免频繁加锁。偏向锁:锁偏向于第一个获得它的线程。如果在接下来的执行过程中,锁还没有被其他线程获取到,那么持有偏向锁的线程永远不需要重新同步。偏向锁其实只是在对象头设置了一个标志,记录了获取偏向锁的线程的ID。但是记录线程ID占用了对象头中记录对象HashCode的位置,所以请求过HashCode的对象不能使用偏向锁。总结:sychronnized通过锁实现线程安全,可以解决线程安全问题。但是sychronized是重量级的同步机制,对系统性能会有一定的影响。但是需要注意的是,sychronized只是解决了锁对象的线程安全问题。锁对象并不是一个应用程序的全部。应用程序是否存在线程安全问题是一个非常复杂的问题。具体问题需要具体分析。sychronized不是线程安全的。问题的万能钥匙并不是说只要用了sychronized就一定要解决线程安全问题。