简介
- 在 Java 5.0 之前,协调共享对象的访问时可以使用的机
制只有synchronized 和volatile 。Java 5.0 后增加了一些
新的机制,但并不是一种替代内置锁的方法,而是当内
置锁不适用时,作为一种可选择的高级功能。 - ReentrantLock 实现了* Lock 接口*,并提供了与
synchronized 相同的互斥性和内存可见性。但相较于
synchronized 提供了更高的处理锁的灵活性。
注意:
是一个显示锁,lock()上锁,必须通过unlock()解锁
必须保证解锁的必须执行,通常放在finally{unlock()}中
例子
创建一个TestRunnable类实现Runnable接口,在run方法中循环打印num的值,然后num++,知道num>=5
class TestRunnable implements Runnable{
private volatile int num = 0;
@Override
public void run() {
while(num<5){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread()+"->"+num++);
}
}
}
测试类:
public class TestLock {
public static void main(String[] args) {
TestRunnable tr = new TestRunnable();
for(int i =0;i<10;i++){
new Thread(tr).start();
}
}
}
输出结果:
Thread[Thread-0,5,main]->0
Thread[Thread-2,5,main]->1
Thread[Thread-9,5,main]->4
Thread[Thread-6,5,main]->5
Thread[Thread-7,5,main]->4
Thread[Thread-3,5,main]->3
Thread[Thread-5,5,main]->2
Thread[Thread-8,5,main]->8
Thread[Thread-1,5,main]->7
Thread[Thread-4,5,main]->6
Thread[Thread-0,5,main]->9
结果出现意外了,明明在run中while条件是<5,但是打印的结果却大于5
分析
以输出的结果为例,在num值为4的时候,Thread-5,Thread-6,Thread-7,Thread-8等几个线程拿到了num的值,此时他们都是4,这个时候,Thread-5的run方法打印完毕,num++为5,虽然num被volatile修饰但是因为是分为三部的,所以在Thread-6,Thread-7内部num的值变为5了,此时如果Thread-6限制性操作,就会打印出5,然后num++之后为6,这个时候Thread-7获得cpu执行权然后打印6,再次对num++操作,此时Thread-8,获得cpu执行权,然后打印num的值是7,再次对num++以此类推,导致我们看到了测试的那样的结果,这个之前的原子性有点类似。
解决方法
class TestRunnable implements Runnable{
private volatile int num = 0;
//创建锁
private Lock lock = new ReentrantLock();
@Override
public void run() {
lock.lock();//上锁
try{
while(num<5){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread()+"->"+num++);
}
}finally{
lock.unlock();
}
}
}
测试结果:
Thread[Thread-0,5,main]->0
Thread[Thread-0,5,main]->1
Thread[Thread-0,5,main]->2
Thread[Thread-0,5,main]->3
Thread[Thread-0,5,main]->4