线程安全问题:多线程对有状态可变的共享变量的修改,导至的问题。
需要使用锁来解决线程安全问题。
锁: 线程互斥性,内存可见性。
线程互斥性:保证了对有状态可变的共享变量的顺序访问。
只有线程获得了锁才能对变量访问,线程释放锁之后,其它线程才能获得锁。
这是线程获得锁-》读写变量--》释放锁--下一个线程 --获得锁-》读写变量--》的过程 。
内存可见性: 进入获得锁后,变量从主内存读取,而不是缓存中读取,锁释放前,修改的变量写回主内存。
下一条线程获得锁后,从主内存中取得的变量的有效数据。
一、相同点
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,性能好些。