在Java多线程中,可以使用synchronized关键字来实现线程之间的同步互斥,但在JDK1.5中新增了Lock锁,同样可以实现同样的效果,并且扩展功能上也更加强大,比如嗅探锁定、多路分支通知等功能,而且使用上也更灵活。
Lock锁分为两类:ReentrantLock和ReentrantReadWriteLock。
这里介绍ReentrantLock类的基础用法。
ReentrantLock具有完全的排他性效果,即同一时刻只有一个线程拥有Lock对象锁。
一个ReentrantLock对象可以创建多个Condition对象,可以根据不同的Condition,进行针对唤醒和等待Lock锁。
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
lock.lock();
.
.//需要同步的代码
.
lock.unlock();
condition.await();//进入等待状态,释放lock锁
condition.signal();//随机唤醒一个等待lock锁的线程
condition.signalAll();//唤醒所有condition条件下等待lock的线程
具体例子一(一对一生产与消费):
1、创建一个服务类,
package com.cjs.ConditionTestMantToMany;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyService {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private boolean hasValue;//判断是否有值
public void set() {
try {
lock.lock();
while (hasValue == true) {
condition.await();//进入等待,相当于wait()
}
System.out.println("set Print");
hasValue = true;
condition.signalAll();//唤醒所有正在等待的线程,相当于notifiAll()
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void get() {
try {
lock.lock();
while (hasValue == false) {
condition.await();//进入等待,相当于wait()
}
System.out.println("get Print");
hasValue = false;
condition.signalAll();//唤醒所有正在等待的线程,相当于notifiAll()
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
2、创建两个线程类,调用服务类的方法,
package com.cjs.ConditionTest;
public class MyThreadA extends Thread {
private MyService myService;
public MyThreadA(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
for (int i=0; i<100;i++) {
myService.set();
}
}
}
package com.cjs.ConditionTest;
public class MyThreadB extends Thread {
private MyService myService;
public MyThreadB(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
for (int i=0; i<100;i++) {
myService.get();
}
}
}
3、创建Main类,用来启动程序,
package com.cjs.ConditionTest;
public class Run {
public static void main(String[] args) {
MyService myService = new MyService();
MyThreadA a = new MyThreadA(myService);
a.start();
MyThreadB b = new MyThreadB(myService);
b.start();
}
}
4、运行,查看控制台,get和set是交替打印,即一方面证明生产与消费属于一对一关系,另一方面说明ReentrantLock是互斥的。
具体例子二(多Condition条件的使用)
1、创建一个服务类
package com.cjs.MustUseMoreCondition_OK;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyService {
private Lock lock = new ReentrantLock();
public Condition conditionA = lock.newCondition();
public Condition conditionB = lock.newCondition();
public void awaitA() {
try {
lock.lock();
System.out.println("begin awaitA时间为:"+ System.currentTimeMillis() + "ThreadName=" + Thread.currentThread().getName());
conditionA.await();
System.out.println("end awaitA时间为:"+System.currentTimeMillis()+"ThreadName=" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void awaitB() {
try {
lock.lock();
System.out.println("begin awaitB时间为:"+ System.currentTimeMillis() + ", ThreadName=" + Thread.currentThread().getName());
conditionB.await();
System.out.println("end awaitB时间为:"+System.currentTimeMillis()+", ThreadName=" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signalAll_A() {
try {
lock.lock();
System.out.println("signalAll_A时间为:" + System.currentTimeMillis() + ", ThreadName=" + Thread.currentThread().getName());
conditionA.signalAll();
} finally {
lock.unlock();
}
}
public void signalAll_B() {
try {
lock.lock();
System.out.println("signalAll_B时间为:" + System.currentTimeMillis() + ", ThreadName=" + Thread.currentThread().getName());
conditionB.signalAll();
} finally {
lock.unlock();
}
}
}
这里创建了两个Condition对象,分别是conditionA和conditionB。
2、创建两个线程类
package com.cjs.MustUseMoreCondition_OK;
public class ThreadA extends Thread {
private MyService myService;
public ThreadA(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.awaitA();
}
}
package com.cjs.MustUseMoreCondition_OK;
public class ThreadB extends Thread {
private MyService myService;
public ThreadB(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.awaitB();
}
}
3、创建一个Main类,
package com.cjs.MustUseMoreCondition_OK;
public class Run {
public static void main(String[] args) {
try {
MyService myService = new MyService();
ThreadA a = new ThreadA(myService);
a.setName("A");
a.start();
ThreadB b = new ThreadB(myService);
b.setName("B");
b.start();
Thread.sleep(3000);
myService.signalAll_A();
//myService.signalAll_B();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
上面注释了myService.signalAll_B();表示不换醒conditionB条件的线程。
4、运行程序
运行结果中,只有conditionA的线程被唤醒,同时执行结束;但是程序并没有结束,还有红色的方框显示出来,说明还有其他线程正在等待,这些等待的线程就是conditionB条件的线程。这时去掉myService.signalAll_B();前面的注释,再运行一次程序。
另外补充,ReentranrLock类还有两种锁,分别是:公平锁和非公平锁
1、公平锁:Lock lock = new ReentrantLock(true);
表示获取锁的顺序是几乎按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。
2、非公平锁:Lock lock = new ReentrantLock(false);
表示一种获取锁的抢占机制,就是随机获取锁。