本篇文章已授权微信公众号 顾林海 独家发布
在JDK1.5中新增ReentrantLock类,效果类似于使用synchronized关键字实现线程间同步互斥,并且在扩展功能上也更加强大,比如具有嗅探锁定、多路分支通知等功能。看下面ReentrantLock的实例代码:
public class Task {
private Lock mLock=new ReentrantLock();
public void runTask(){
mLock.lock();
for(int i=0;i<3;i++){
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
mLock.unlock();
}
}
利用ReentrantLock对象的lock()方法获取锁,通过unlock()方法释放锁。
public class ThreadFirst extends Thread {
private Task mTask;
public ThreadFirst(Task task){
this.mTask=task;
}
@Override
public void run() {
super.run();
mTask.runTask();
}
}
Client:
public class Client {
public static void main(String[] args) {
Task task=new Task();
ThreadFirst threadFirst;
for (int i = 0; i < 3; i++) {
threadFirst=new ThreadFirst(task);
threadFirst.start();
}
}
}
打印:
Thread-1-->0
Thread-1-->1
Thread-1-->2
Thread-2-->0
Thread-2-->1
Thread-2-->2
Thread-0-->0
Thread-0-->1
Thread-0-->2
从打印结果看,打印是按分组来打印的,当前线程打印完毕后释放锁,其他线程才可以继续打印,
在前面文章中可以通过synchronized与wait()和notify()/notifyAll()方法相结合实现等待/通知模式,利用ReentrantLock也可以实现同样的效果,但需要借助于Condition对象。Condition可以实现多路通知功能,可以在一个Lock对象里面创建多个Condition实例,线程对象可以注册在指定的Condition中,从而可以有选择性进行线程通知。
代码示例:
public class Task {
private Lock mLock=new ReentrantLock();
private Condition mCondition=mLock.newCondition();
public void runTask(){
try {
mLock.lock();
System.out.println("打印一些信息:A");
mCondition.await();
System.out.println("打印剩余信息:B");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
mLock.unlock();
System.out.println("释放锁");
}
}
}
线程类:
public class ThreadFirst extends Thread {
private Task mTask;
public ThreadFirst(Task task){
this.mTask=task;
}
@Override
public void run() {
super.run();
mTask.runTask();
}
}
Client:
public class Client {
public static void main(String[] args) {
Task task=new Task();
ThreadFirst threadFirst=new ThreadFirst(task);
threadFirst.start();
}
}
打印:
打印一些信息:A
从打印结果看只打印了A,后面B没打印出来,这是因为调用了Condition对象的await()方法,使当前执行任务的线程进入等待WAITING状态。
补齐上面代码,在Task类中新添加一个notifyTask方法:
public void notifyTask(){
try {
mLock.lock();
System.out.println("通知task继续执行");
mCondition.signal();
}finally {
mLock.unlock();
}
}
Client修改如下:
public class Client {
public static void main(String[] args) {
Task task=new Task();
ThreadFirst threadFirst=new ThreadFirst(task);
threadFirst.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
task.notifyTask();
}
}
打印:
打印一些信息:A
通知task继续执行
打印剩余信息:B
释放锁
到这里可以发现Object类的wait()和wait(long)方法相当于Condition类中的await()和await(long)方法。notify()和notifyAll相当于Condition类中的signal()和signalAll()方法。
当然我们也可以创建多个Condition对象来实现通知部分线程,代码如下:
public class Task {
private Lock mLock = new ReentrantLock();
private Condition mConditionA = mLock.newCondition();
private Condition mConditionB = mLock.newCondition();
public void runTaskA() {
try {
mLock.lock();
System.out.println("打印一些信息:A");
mConditionA.await();
System.out.println("打印剩余信息:A");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mLock.unlock();
System.out.println("释放锁A");
}
}
public void runTaskB() {
try {
mLock.lock();
System.out.println("打印一些信息:B");
mConditionB.await();
System.out.println("打印剩余信息:B");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mLock.unlock();
System.out.println("释放锁B");
}
}
public void notifyTaskA() {
try {
mLock.lock();
System.out.println("通知A继续执行");
mConditionA.signal();
} finally {
mLock.unlock();
}
}
public void notifyTaskB() {
try {
mLock.lock();
System.out.println("通知B继续执行");
mConditionB.signal();
} finally {
mLock.unlock();
}
}
}
线程类1:
public class ThreadFirst extends Thread {
private Task mTask;
public ThreadFirst(Task task){
this.mTask=task;
}
@Override
public void run() {
super.run();
mTask.runTaskA();
}
}
线程类2:
public class ThreadSecond extends Thread {
private Task mTask;
public ThreadSecond(Task task){
this.mTask=task;
}
@Override
public void run() {
super.run();
mTask.runTaskB();
}
}
Client:
public class Client {
public static void main(String[] args) {
Task task=new Task();
ThreadFirst threadFirst=new ThreadFirst(task);
ThreadSecond threadSecond=new ThreadSecond(task);
threadFirst.start();
threadSecond.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
task.notifyTaskA();
}
}
打印:
打印一些信息:A
打印一些信息:B
通知A继续执行
打印剩余信息:A
释放锁A
从打印结果可以看出,使用ReentrantLock对象可以唤醒指定种类的线程,非常方便。
锁Lock分为“公平锁”和“非公平锁”,公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先到先得的FIFO先进先出顺序,而非公平锁就是一种获取锁的抢占机制,是随机获得的,和公平锁不一样的就是先来的不一定先得到锁,这样的话可能造成某些线程一直拿不到锁。