ReentrantLock
ReentrantLock
是一个互斥锁,也是一个可重入锁(Reentrant就是再次进入的意思)。ReentrantLock
锁在同一个时间点只能被一个线程锁持有,但是它可以被单个线程多次获取,每获取一次AQS
的state
就加1,每释放一次state
就减1。还记得synchronized
嘛,它也是可重入的,一个同步方法调用另外一个同步方法是没有问题的。
在使用上无非就是获取锁和释放锁,我们完全可以用它来实现synchronized
的功能
我要实现一个程序,由两条线程去输出100到0,下面是有问题的程序代码
public class Main {
public static void main(String[] args) {
Counter counter = new Counter();
Runnable runnable = new Runnable() {
@Override
public void run() {
while(counter.getCount()>=0)
counter.desc();
}
};
new Thread(runnable).start();
new Thread(runnable).start();
}
}
class Counter{
private int count = 100;
public void desc(){
System.out.println(Thread.currentThread().getName() +"--->"+count);
count--;
}
public int getCount() {
return count;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
某次执行输出结果,很明显并没有达到我的要求。
.....
Thread-1--->6
Thread-1--->5
Thread-1--->4
Thread-1--->3
Thread-1--->2
Thread-1--->1
Thread-1--->0
Thread-0--->15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
于是我用ReentrantLock
改写一下Counter
类的desc
方法,你要注意了,千万不要傻傻地在desc
方法内部创建一个ReentrantLock
对象,这样每次线程调用的时候用的都是一个新锁,还谈什么互斥呀,就像同步方法和静态同步方法,它们的锁都不是同一个,是互斥不了的。现在运行代码是没错的了
class Counter {
private int count = 100;
private Lock lock = new ReentrantLock();
public void desc() {
lock.lock();//上锁
if (count >= 0){
System.out.println(Thread.currentThread().getName() + "--->" + count);
count--;
}
lock.unlock();//释放锁
}
public int getCount() {
return count;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
上面代码还是有问题的,那就是锁的释放,如果在上锁了,后面的代码抛出异常没能释放锁,你说完不完蛋!!?所以锁的释放一定要在try-finally块
的finally
中,就像是JDBC中释放数据库连接那样。这一点还是synchronized
比较方便,不用我们自己释放锁。
Condition
到了这里就要谈到Condition
了,它需要与 Lock
联合使用,它的作用就是代替Object
的那些监视器方法,Condition
中的await()
、signal()
和signalAll()
方法分别对应着Object
的wait()
、notify()
和notifyAll()
方法。
不过一个它比较牛逼的一点是,一个Lock
可以关联多个Condition
,这样子玩起来就很灵活了,想要各个方法按什么顺序执行都行。还是上面那个例子,我想让两个线程和谐点,你输出一个数,然后我又输出下一个数,这样子交替执行,实现代码如下
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Main {
public static void main(String[] args) {
Counter counter = new Counter();
new Thread(new Runnable() {
@Override
public void run() {
while (counter.getCount() >= 0) {
counter.desc1();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (counter.getCount() >= 0) {
counter.desc2();
}
}
}).start();
}
}
class Counter {
private int count = 100;
private Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
boolean state = true;
public void desc1() {
lock.lock();// 上锁
try {
while (state)
condition1.await();// 线程等待
if (count >= 0) {
System.out.println(Thread.currentThread().getName() + "--->" + count);
count--;
}
state = true;// 改变状态
condition2.signal();// 唤醒调用了condition2.await()线程
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();// 释放锁
}
}
public void desc2() {
lock.lock();// 上锁
try {
while (!state)
condition2.await();// 线程等待
if (count >= 0) {
System.out.println(Thread.currentThread().getName() + "--->" + count);
count--;
}
state = false;// 改变状态
condition1.signal();// 唤醒调用了condition1.await()线程
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();// 释放锁
}
}
public int getCount() {
return count;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
输出结果
.....
Thread-1--->10
Thread-0--->9
Thread-1--->8
Thread-0--->7
Thread-1--->6
Thread-0--->5
Thread-1--->4
Thread-0--->3
Thread-1--->2
Thread-0--->1
Thread-1--->0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
上面代码不难,所以不再这多解释了,不过按照上面的例子你就可以举一反三了,我记得有道线程的题目是让三个线程不断交替输出什么鬼的,t1->t2->t3->t1->t2->t3….,根据上面的例子我相信你能解决这个问题的,可以拿这个题目练一下手,熟悉一下。
最后要提一下的是ReentrantLock
有两个构造方法,默认的构造方法会让它成为一个非公平锁,而如果你想创建一个公平锁则用ReentrantLock(boolean fair)
传入一个true
创建ReentrantLock
实例
转载自: