为了解决多线程安全问题
在 Java 5.0 之前,协调共享对象的访问时可以使用的机制只有 synchronized 和 volatile 。
Java 5.0 后增加了一些新的机制,但并不是一种替代内置锁的方法,而是当内置锁不适用时,作为一种可选择的高级功能。
隐式锁
synchronized
- 同步代码块;
- 同步方法。
显示锁 Lock
jdk 1.5 后:同步锁 Lock 需要通过 lock() 方法上锁,必须通过 unlock() 方法进行释放锁。
ReentrantLock 实现了 Lock 接口,并提供了与synchronized 相同的互斥性和内存可见性。但相较于synchronized 提供了更高的处理锁的灵活性。
Lock使用方式:
Lock lock = new ReentrantLock();
lock.lock();
try {
} finally {
lock.unlock();
}
相关API:
- void lock() 获取锁。
- void lockInterruptibly() 如果当前线程未被中断,则获取锁。
- Condition newCondition() 返回绑定到此 Lock 实例的新 Condition 实例。
- boolean tryLock() 仅在调用时锁为空闲状态才获取该锁。
- boolean tryLock(long time, TimeUnit unit) 如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁。
- void unlock()释放锁。
不使用锁出现多线程安全问题demo:
package com.company; public class TestLock { public static void main(String[] args) {
Ticket ticket = new Ticket(); new Thread(ticket, "1号窗口").start();
new Thread(ticket, "2号窗口").start();
new Thread(ticket, "3号窗口").start();
} } class Ticket implements Runnable{ private int tick = 100; @Override
public void run() {
while(true){
if(tick > 0){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
} System.out.println(Thread.currentThread().getName() + " 完成售票,余票为:" + --tick);
} }
} }
看出现的问题(截取结果中一部分):
3号窗口 完成售票,余票为:10
1号窗口 完成售票,余票为:8
2号窗口 完成售票,余票为:8
3号窗口 完成售票,余票为:8
2号窗口 完成售票,余票为:7
1号窗口 完成售票,余票为:7
3号窗口 完成售票,余票为:7
1号窗口 完成售票,余票为:6
2号窗口 完成售票,余票为:5
3号窗口 完成售票,余票为:4
3号窗口 完成售票,余票为:3
1号窗口 完成售票,余票为:3
2号窗口 完成售票,余票为:3
3号窗口 完成售票,余票为:2
1号窗口 完成售票,余票为:0
2号窗口 完成售票,余票为:1
现在我们用Lock来进行处理:
package com.company; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class TestLock { public static void main(String[] args) {
Ticket ticket = new Ticket(); new Thread(ticket, "1号窗口").start();
new Thread(ticket, "2号窗口").start();
new Thread(ticket, "3号窗口").start();
} } class Ticket implements Runnable { private int tick = 100;
private Lock lock = new ReentrantLock(); @Override
public void run() {
while (true) {
try {
lock.lock();
if (tick > 0) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
} System.out.println(Thread.currentThread().getName() + " 完成售票,余票为:" + --tick);
}
} finally {
lock.unlock();
} }
} }
看下结果(当然也是一部分),很完美解决多线程安全问题:
2号窗口 完成售票,余票为:20
2号窗口 完成售票,余票为:19
2号窗口 完成售票,余票为:18
2号窗口 完成售票,余票为:17
2号窗口 完成售票,余票为:16
2号窗口 完成售票,余票为:15
2号窗口 完成售票,余票为:14
2号窗口 完成售票,余票为:13
2号窗口 完成售票,余票为:12
2号窗口 完成售票,余票为:11
2号窗口 完成售票,余票为:10
2号窗口 完成售票,余票为:9
2号窗口 完成售票,余票为:8
2号窗口 完成售票,余票为:7
2号窗口 完成售票,余票为:6
2号窗口 完成售票,余票为:5
2号窗口 完成售票,余票为:4
2号窗口 完成售票,余票为:3
2号窗口 完成售票,余票为:2
2号窗口 完成售票,余票为:1
2号窗口 完成售票,余票为:0