java-锁 synchroinized & lock

线程安全问题:多线程对有状态可变的共享变量的修改,导至的问题。

需要使用锁来解决线程安全问题。

锁: 线程互斥性,内存可见性。

        线程互斥性:保证了对有状态可变的共享变量的顺序访问。

                 只有线程获得了锁才能对变量访问,线程释放锁之后,其它线程才能获得锁。

这是线程获得锁-》读写变量--》释放锁--下一个线程  --获得锁-》读写变量--》的过程 。

内存可见性: 进入获得锁后,变量从主内存读取,而不是缓存中读取,锁释放前,修改的变量写回主内存。

            下一条线程获得锁后,从主内存中取得的变量的有效数据。

 一、相同点

1.互斥性及可见性 

synchroinized:java 语法,可重入锁

   1.jvm控制线程互斥

   2.jvm控制内存可见性 (同步代码块中修改过的变量被写回主存)

  

 ReentrantLock : java类,可重入锁 

   1.使用Unsafe.park 或 Unsafe.unPack 阻塞线程或唤醒线程 ,控制线程的顺序执行。

   2.可见性保证:内部持有一个volatile 变量state:

           lock时,读state,让缓存失效,数据都从主内中读取。

           unlock时,写state,缓存中的数据写回主存。

   

2.都是可重入锁

3.支持非公平锁(ReentrantLock 支持非公平锁和公平锁,synchronized 是非公平锁。非公平锁性能比公平锁高一些)

二、不同点

1.写法

   a. synchronized 可以在静态方法,实例方法上,代码块上,不需要主机释放锁

       lock 代码块上,需要在 finally 里主动释放锁 lock.unlock()

2.公平性

       ReentrantLock 支持公平和非公平锁,synchronized 是非公平锁

3.锁等待及中断

      synchronized  :在锁被占用时,一直等待直到获得锁。--不支持中断

      Lock.lock() :在锁被占用时,一直等待直到获得锁。--不支持中断

      Lock.tryLock()  : 在锁被占用时,立即返回false.--不支持中断

      Lock.tryLock(long time, TimeUnit unit):在锁被占用时,等待指定时间,如果还未获得锁,则返回false

                                                                     --支持中断。

      Lock.lockInterruptibly()   : :在锁被占用时,一直等待直到获得锁.--支持中断

  

 4.锁定范围

       synchronized  :同一个监控器对象,可以是类实例,静态类。(内存地址相同的)

       Lock :同一个Lock实例

    

性能对比:

8个逻辑CPU,启用多个线程 从0开始+1 计数

竞争非常激烈:(锁内执行是时间约 0.05ms)  目标总值为:1000000000
16条线程:
使用synchronized:  use time:132秒 ,总值:1000000000,执行其间打印线程状态 :1条线程运行,15线程阻塞 java.lang.Thread.State: BLOCKED (on object monitor)
使用ReentrantLock: use time:39秒  ,总值:1000000000,执行其间打印线程状态 :1条线程运行,15线程阻塞 java.lang.Thread.State: WAITING (parking)
使用CAS自旋锁:     没有执行,原因:会执行很长时间,8个cpu 100%,怕把cpu烧坏了。


4条线程:
使用synchronized:  use time:139秒 ,总值:1000000000,执行其间打印线程状态 :4个cpu 70%左右,1条线程运行,3线程阻塞 java.lang.Thread.State: BLOCKED (on object monitor)
使用ReentrantLock: use time:43秒  ,总值:1000000000,执行其间打印线程状态 :4个cpu 40%左右,1条线程运行,3线程阻塞 java.lang.Thread.State: WAITING (parking)
使用CAS自旋锁:     use time:很长时间, 总值:1000000000,执行其间打印线程状态 :4个cpu 100%,    4条线程运行,

1条线程:
使用synchronized:  use time:29秒 , 总值:1000000000,执行其间打印线程状态 :1个cpu 100%,1条线程运行, 
使用ReentrantLock: use time:23秒  ,总值:1000000000,执行其间打印线程状态 :1个cpu 100%,1条线程运行,
使用CAS自旋锁:     use time:17秒  ,总值:1000000000,执行其间打印线程状态 :1个cpu 100%,1条线程运行,


竞争不激烈:(锁内代执行是时间约 0.05ms,锁内sleep(1ms),两个锁间sleep(1ms))  目标总值为:100000
4条线程:(相当于2线程抢一个锁)
使用synchronized:  use time:111秒  ,总值:100000,cpu非常低
使用ReentrantLock: use time:112秒  ,总值:100000,cpu非常低
使用CAS自旋锁:     use time:110秒  ,总值:100000,4个cpu 50%

16条线程:(相当于8个线程抢一个锁)
使用synchronized:  use time:112秒  ,总值:100000,cpu非常低
使用ReentrantLock: use time:112秒  ,总值:100000,cpu非常低
 


32条线程:(相当于16个线程抢一个锁)
使用synchronized:  use time:112秒  ,总值:100000,cpu非常低
使用ReentrantLock: use time:112秒  ,总值:100000,cpu非常低
 

256条线程:(相当于128个线程抢一个锁)
使用synchronized:  use time:112秒  ,总值:100000,cpu非常低
使用ReentrantLock: use time:112秒  ,总值:100000,cpu非常低

性能分析:
CAS自旋锁 :极少情况有竞争,锁内执行时间1ms以内--情况使用。要小心!
一般情况下,synchronized与ReentrantLock 性能相当。
在竞争极其激烈,线程极短时间内频繁阻塞唤醒,情况下使用ReentrantLock,性能好些。

猜你喜欢

转载自java12345678.iteye.com/blog/2398591