在Java多线程中可以使用sunchronzied关键字来实现线程间同步,不过在JDK1.5中新增的ReentrantLock类也能达到同样的效果,并且在扩展功能上更加强大,比如具有嗅探锁定‘多路分支通知等功能。
下面介绍一下ReentrantLock的使用。
public class MyService {
private Lock lock = new ReentrantLock();
public void testMethod(){
lock.lock();
for (int i = 0; i < 5; i++) {
System.out.println("ThreadName = " + Thread.currentThread().getName() + " = "+(i+1));
}
lock.unlock();
}
}
调用ReentrantLock对象的lock方法获取锁,调用unlock方法释放锁,这两个方法成对出现。想要实现同步代码,把这些代码放在lock和unlock之间即可。
public class MyThread extends Thread{
private MyService service;
public MyThread(MyService service) {
this.service = service;
}
@Override
public void run(){
service.testMethod();
}
}
public class Run1 {
public static void main(String[] args) {
MyService service = new MyService();
MyThread a1 = new MyThread(service);
MyThread a2 = new MyThread(service);
MyThread a3 = new MyThread(service);
MyThread a4 = new MyThread(service);
MyThread a5 = new MyThread(service);
a1.start();
a2.start();
a3.start();
a4.start();
a5.start();
}
}
从运行结果看,当前线程打印完毕之后将锁释放,其他线程才可以继续抢锁并打印,每个线程打印的数据是有序的。即从1到5。因为当前线程已经持有锁,具有互斥排他性,而线程之间打印的顺序是随机的,所以谁抢到锁,谁打印。
public class MyService {
private Lock lock = new ReentrantLock();
public void methodA(){
try {
lock.lock();
System.out.println("methodA开始执行,线程名= " + Thread.currentThread().getName() + ",开始时间 = " + Utils.data(System.currentTimeMillis()));
Thread.sleep(5000);
System.out.println("methodA执行完成,线程名= " + Thread.currentThread().getName() + ",结束时间 = " + Utils.data(System.currentTimeMillis()));
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void methodB(){
try {
lock.lock();
System.out.println("methodB 开始执行,线程名= " + Thread.currentThread().getName() + ",开始时间 = " + Utils.data(System.currentTimeMillis()));
Thread.sleep(5000);
System.out.println("methodB 执行完成,线程名= " + Thread.currentThread().getName() + ",结束时间 = " + Utils.data(System.currentTimeMillis()));
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class ThreadA extends Thread{
private MyService myService;
public ThreadA(MyService myService) {
this.myService = myService;
}
@Override
public void run(){
myService.methodA();
}
}
public class ThreadAA extends Thread{
private MyService myService;
public ThreadAA(MyService myService) {
this.myService = myService;
}
@Override
public void run(){
myService.methodA();
}
}
public class ThreadB extends Thread{
private MyService myService;
public ThreadB(MyService myService) {
this.myService = myService;
}
@Override
public void run(){
myService.methodB();
}
}
public class ThreadBB extends Thread{
private MyService myService;
public ThreadBB(MyService myService) {
this.myService = myService;
}
@Override
public void run(){
myService.methodB();
}
}
public class Run1 {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadB aa = new ThreadB(service);
aa.setName("AA");
aa.start();
Thread.sleep(100);
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
ThreadB bb = new ThreadB(service);
bb.setName("BB");
bb.start();
}
}
这个实验说明,不管在一个方法还是多个方法的环境中,哪个线程持有锁,哪个线程就执行业务,其他线程只能等待锁被释放再次争抢,抢到了才开始执行业务,运行效果和使用synchronzied关键字一样。线程之间执行的顺序是随机的。
关键字synchronzied与wait()方法和notify()/notifyAll()方法相结合,可以实现等待/通知模式,ReentrantLock类借助于Condition对象,也可以实现同样的功能。Condition类实在JDK5出现的技术,使用它可以获得更好的灵活性,比如实现多路通知功能,也就是在1个Lock对象里面创建多个Condition实例,并且线程对象注册在指定的Condition中,从而有选择性地进行线程通知,在调度 线程上更加灵活。
在使用notify/nofityAll()方法进行通知时,被通知的线程由JVM进行选择,而notifyAll()方法会通知所有的WAITING线程,没有选择权,这对运行效率有相当大的影响。使用ReentrantLock结合Condition类可以实现选择性通知,让通知更具有针对性,这个功能在condition类中是默认提供的。
Condition 对象的作用是控制并处理线程的状态,它可以是线程处于等待状态,也可以让线程继续运行。
public class MyService {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void await(){
try {
condition.await();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
await()方法的作用是使当前线程在接到通知或被中断之前,一直处于等待状态。它和wait方法一样。
public class ThreadA extends Thread{
private MyService service;
public ThreadA(MyService service) {
this.service = service;
}
@Override
public void run(){
service.await();
}
}
public class Run1 {
public static void main(String[] args) {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.start();
}
}
报错的异常信息是监视器出错,解决这个问题必须要在condition.await方法调用之前调用lock.lock方法获得锁。修改MyService.java类和Run1.java
public class MyService {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void await(){
try {
lock.lock();
System.out.println("A");
condition.await();
System.out.println("B");
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
System.out.println("锁释放了");
}
}
}
public class Run1 {
public static void main(String[] args) {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.start();
ThreadA b = new ThreadA(service);
b.start();
ThreadA c = new ThreadA(service);
c.start();
}
}
运行结果是在控制台打印了3个字母A,说明调用Condition 对象的await方法将当前执行任务的线程转换成等待状态并释放锁。
public class MyService {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void await(){
try {
lock.lock();
System.out.println("开始执行await方法,时间 = " + Utils.data(System.currentTimeMillis()));
condition.await();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signal(){
try {
lock.lock();
System.out.println("开始执行signal方法,时间 = " + Utils.data(System.currentTimeMillis()));
condition.signal();
}finally {
lock.unlock();
}
}
}
public class ThreadA extends Thread{
private MyService service;
public ThreadA(MyService service) {
this.service = service;
}
@Override
public void run(){
service.await();
}
}
public class Run1 {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.start();
Thread.sleep(3000);
service.signal();
}
}
成功实现了等待/通知模式,注意以下几点:
Object类的wait方法相当于Condition类中的await方法 。
Object类中的wait(long timeout)方法相当于Condition类中的await(long time,TimeUnit unit)
Object类的notify方法相当于Condition类中的signal方法 。
Object类中的notifyAll方法相当于Condition类中的signalAll方法
除了使用一个Condition对象来实现等待/通知模式,也可以创建多个Condition对象,那么一个Condition对象和多个Condition对象在使用上有什么区别?
public class MyService {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void awaitA(){
try {
lock.lock();
System.out.println("开始执行awaitA方法的时间 = " + Utils.data(System.currentTimeMillis()) + ";线程名= " + Thread.currentThread().getName());
condition.await();
System.out.println("执行完成awaitA方法的时间 = " + Utils.data(System.currentTimeMillis()) + ";线程名= " + Thread.currentThread().getName());
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void awaitB(){
try {
lock.lock();
System.out.println("开始执行awaitB方法的时间 = " + Utils.data(System.currentTimeMillis()) + ";线程名= " + Thread.currentThread().getName());
condition.await();
System.out.println("执行完成awaitB方法的时间 = " + Utils.data(System.currentTimeMillis()) + ";线程名= " + Thread.currentThread().getName());
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signalAll(){
try {
lock.lock();
System.out.println("执行 signalAll时间 = " + Utils.data(System.currentTimeMillis()) + ";线程名= " + Thread.currentThread().getName());
condition.signalAll();
}finally {
lock.unlock();
}
}
}
public class ThreadA extends Thread{
private MyService service;
public ThreadA(MyService service) {
this.service = service;
}
@Override
public void run(){
service.awaitA();
}
}
public class ThreadB extends Thread{
private MyService service;
public ThreadB(MyService service) {
this.service = service;
}
@Override
public void run(){
service.awaitB();
}
}
public class Run1 {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
Thread.sleep(3000);
service.signalAll();
}
}
根据运行结果发现,3秒后,线程A和线程B都被唤醒了。如果想单独唤醒部分线程该怎么处理呢。这时就有必要使用多个Condition对象了。 Condition对象可以用于唤醒部分指定线程,有助于提升程序运行的效率,可以先对线程进行分组,然后唤醒指定组中的线程。
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("开始执行awaitA方法,时间 = " + Utils.data(System.currentTimeMillis()) + ";线程名 = " + Thread.currentThread().getName());
conditionA.await();
System.out.println("执行完成awaitA方法,时间 = " + Utils.data(System.currentTimeMillis()) + ";线程名 = " + Thread.currentThread().getName());
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void awaitB(){
try {
lock.lock();
System.out.println("开始执行awaitB方法,时间 = " + Utils.data(System.currentTimeMillis()) + ";线程名 = " + Thread.currentThread().getName());
conditionA.await();
System.out.println("执行完成awaitB方法,时间 = " + Utils.data(System.currentTimeMillis()) + ";线程名 = " + Thread.currentThread().getName());
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signalAll_A(){
try {
lock.lock();
System.out.println("开始执行 signalAll_A 方法,时间 = " + Utils.data(System.currentTimeMillis()) + ";线程名 = " + Thread.currentThread().getName());
conditionA.signalAll();
}finally {
lock.unlock();
}
}
public void signalAll_B(){
try {
lock.lock();
System.out.println("开始执行 signalAll_B 方法,时间 = " + Utils.data(System.currentTimeMillis()) + ";线程名 = " + Thread.currentThread().getName());
conditionA.signalAll();
}finally {
lock.unlock();
}
}
}
public class ThreadA extends Thread{
private MyService service;
public ThreadA(MyService service) {
this.service = service;
}
@Override
public void run(){
service.awaitA();
}
}
public class ThreadB extends Thread{
private MyService service;
public ThreadB(MyService service) {
this.service = service;
}
@Override
public void run(){
service.awaitB();
}
}
public class Run1 {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
Thread.sleep(3000);
service.signalAll_A();
}
}
通过运行结果可以看出,3秒后只有线程A被唤醒了,线程B没有被唤醒。
使用Condition对象可以唤醒执行种类的线程,这是控制部分线程行为的便捷方法。
public class MyService {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private boolean hasValue = false;
public void set(){
try {
lock.lock();
if(hasValue == true){
condition.await();
}
System.out.println("打印A");
hasValue = true;
condition.signal();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void get(){
try {
lock.lock();
if(hasValue == false){
condition.await();
}
System.out.println("打印B");
hasValue = false;
condition.signal();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class ThreadA extends Thread{
private MyService service = new MyService();
public ThreadA(MyService service) {
this.service = service;
}
@Override
public void run(){
for (int i = 0; i < 20; i++) {
service.set();
}
}
}
public class ThreadB extends Thread{
private MyService service;
public ThreadB(MyService service) {
this.service = service;
}
@Override
public void run(){
for (int i = 0; i < 20; i++) {
service.get();
}
}
}
public class Run1 {
public static void main(String[] args) {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.start();
ThreadB b = new ThreadB(service);
b.start();
}
}
通过使用Condition对象,实现生产者/消费者交替执行的效果。