天天看点

Java多线程编程-ReentrantLock(1)ReentrantLock简单使用

Java多线程编程-ReentrantLock使用及其源码解析

  • ReentrantLock简单使用
    • ReentrantLock实现等待通知

在java多线程开发中,我们可以使用 synchronized关键字来实现线程之间的同步互斥,我们也可以使用 ReentrantLock实现更多的功能,如嗅探锁定。多路分支通知等功能,使用上会比synchronized更加灵活。

ReentrantLock简单使用

可重入的互斥Lock,具有与使用sync方法和语句访问的隐式监视器锁相同的基本行为和语义。

由上一次成功锁定但尚未解锁的线程所有。当该锁不属于另一个线程时,调用lock的线程将返回,并成功获取该锁。如果当前线程已经拥有该锁,则该方法将立即返回。可以使用方法 isHeldByCurrentThread和getHoldCount进行检查。

public class ReentrantLockV1Main {
    public static ReentrantLock reentrantLock = new ReentrantLock();
    public static void main(String[] args) {
        Thread thread01 = new Thread(() -> {
            reentrantLock.lock();
            try {
                for (int i = 0; i < 5; i++) {
                    System.out.println("current thread name :" + Thread.currentThread().getName() + " value:" + (i + 1));
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }, "thread01");

        Thread thread02 = new Thread(() -> {
            reentrantLock.lock();
            try {
                for (int i = 0; i < 5; i++) {
                    System.out.println("current thread name :" + Thread.currentThread().getName() + " value:" + (i + 1));
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }, "thread02");

        Thread thread03 = new Thread(() -> {
            reentrantLock.lock();
            try {
                for (int i = 0; i < 5; i++) {
                    System.out.println("current thread name :" + Thread.currentThread().getName() + " value:" + (i + 1));
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }, "thread03");
        thread01.start();
        thread02.start();
        thread03.start();
    }
}
           

运行结果:

Java多线程编程-ReentrantLock(1)ReentrantLock简单使用

使用lock方法获取锁,使用unlock方法释放锁。每个线程同步执行。

ReentrantLock实现等待通知

我们知道synchronized和wait/notify可以实现等待通知模式,ReentrantLock也可以实现,但是要借助Condition对象。在一个lock对象里面可以创建多个condition实例,线程对象可以注册在指定的condition中,从而可以有选择性的进行线程的通知。在调度线程上更加灵活。

在没有获得锁的情况下,进行wait操作会报错:

public class ReentranLockObj1 {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void waitFun() {
        try {
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
           

线程类:

public class ReentranLockObj1T1 extends Thread {

    private ReentranLockObj1 reentranLockObj1;

    public ReentranLockObj1T1(ReentranLockObj1 reentranLockObj1) {
        this.reentranLockObj1 = reentranLockObj1;
    }

    @Override
    public void run() {
        reentranLockObj1.waitFun();
    }
}
           

运行结果:

Java多线程编程-ReentrantLock(1)ReentrantLock简单使用

我们看看该类的Condition接口源码:

public interface Condition {
//使当前线程等待,直到发出信号或被中断
void await() throws InterruptedException;
//使当前线程等待,直到发出信号
void awaitUninterruptibly();
//使当前线程等待,直到发出信号或被中断或经过指定的等待时间为止
long awaitNanos(long nanosTimeout) throws InterruptedException;
//使当前线程等待,直到发出信号或被中断,或者经过指定的等待时间
boolean await(long time, TimeUnit unit) throws InterruptedException;
//使当前线程等待,直到发出信号或被中断,或者经过指定的截止时间
boolean awaitUntil(Date deadline) throws InterruptedException;
//唤醒一个等待的线程
void signal();
//唤醒所有的等待线程
void signalAll();
}
           

我们只需要将ReentranLockObj1 waitFun方法中添加lock即可,修改如下:

public void waitFun() {
        lock.lock();
        try {
            System.out.println("current thread name :" + Thread.currentThread().getName());
            condition.await();
            System.out.println("current thread name :" + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
           

看看运行结果:

Java多线程编程-ReentrantLock(1)ReentrantLock简单使用

await方法后面的语句没有输入到控制台,处于等待状态。

进行通知唤醒:

public class ReentranLockObj2 {

    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    /**
     * 等待
     */
    public void waitFun() {
        lock.lock();
        try {
            System.out.println("current thread name:" + Thread.currentThread().getName());
            condition.await();
            System.out.println("current thread name:" + Thread.currentThread().getName());
        } catch (IllegalMonitorStateException | InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 唤醒
     */
    public void notifyFun() {
        lock.lock();
        try {
            condition.signal();
        } catch (IllegalMonitorStateException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
           
public class ReentranLockObj1T2Main {

    public static void main(String[] args) throws InterruptedException {
        ReentranLockObj2 reentranLockObj2 = new ReentranLockObj2();
        ReentranLockObj2T2 reentranLockObj2T2 = new ReentranLockObj2T2(reentranLockObj2);
        reentranLockObj2T2.start();
        Thread.sleep(1000);
        reentranLockObj2.notifyFun();
    }
}
           

运行结果:

Java多线程编程-ReentrantLock(1)ReentrantLock简单使用

打印第一句,1秒之后打印第二句,使用signal()唤醒。唤醒所有等待中的线程,使用signalAll()

public class ReentranLockObj3 {

    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void waitFun1() {
        lock.lock();
        try {
            System.out.println("current thread name:" + Thread.currentThread().getName());
            condition.await();
            System.out.println("current thread name:" + Thread.currentThread().getName());
        } catch (IllegalMonitorStateException | InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void waitFun2() {
        lock.lock();
        try {
            System.out.println("current thread name:" + Thread.currentThread().getName());
            condition.await();
            System.out.println("current thread name:" + Thread.currentThread().getName());
        } catch (IllegalMonitorStateException | InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void notifyAllFun() {
        lock.lock();
        try {
            condition.signalAll();
        } catch (IllegalMonitorStateException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}
           

线程1:

public class ReentranLockObj3T1 extends Thread {

    private ReentranLockObj3 reentranLockObj3;

    public ReentranLockObj3T1(ReentranLockObj3 reentranLockObj3, String threadName) {
        this.reentranLockObj3 = reentranLockObj3;
        this.setName(threadName);
    }

    @Override
    public void run() {
        reentranLockObj3.waitFun1();
    }
}
           

线程2:

public class ReentranLockObj3T2 extends Thread {

    private ReentranLockObj3 reentranLockObj3;

    public ReentranLockObj3T2(ReentranLockObj3 reentranLockObj3, String threadName) {
        this.reentranLockObj3 = reentranLockObj3;
        this.setName(threadName);
    }

    @Override
    public void run() {
        reentranLockObj3.waitFun2();
    }
}
           

运行结果:

Java多线程编程-ReentrantLock(1)ReentrantLock简单使用

先输出上面两行,等5分钟通过调用signalAll进行唤醒所有等待中的线程。

如果想单独唤醒一个,该怎么处理呢? 我们需要创建多个condition进行处理了。通过condition唤醒部分线程,可以有效提高程序运行效率。

public class ReentranLockObj4 {

    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();

    public void waitFunCondition1() {
        lock.lock();
        try {
            System.out.println("current thread name:" + Thread.currentThread().getName());
            condition1.await();
            System.out.println("current thread name:" + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void waitFunCondition2() {
        lock.lock();
        try {
            System.out.println("current thread name:" + Thread.currentThread().getName());
            condition2.await();
            System.out.println("current thread name:" + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void notifyFunCondition1() {
        lock.lock();
        try {
            condition1.signalAll();
        } catch (IllegalMonitorStateException e) {
            e.printStackTrace();
        } finally {

            lock.unlock();
        }
    }

    public void notifyFunCondition2() {
        lock.lock();
        try {
            condition2.signalAll();
        } catch (IllegalMonitorStateException e) {
            e.printStackTrace();
        } finally {

            lock.unlock();
        }
    }

}
           

线程1:

public class ReentranLockObj4T1 extends Thread {

    private ReentranLockObj4 reentranLockObj4;

    public ReentranLockObj4T1(ReentranLockObj4 reentranLockObj4, String threadName) {
        this.reentranLockObj4 = reentranLockObj4;
        this.setName(threadName);
    }

    @Override
    public void run() {
        reentranLockObj4.waitFunCondition1();
    }
}
           

线程2:

public class ReentranLockObj4T2 extends Thread {

    private ReentranLockObj4 reentranLockObj4;

    public ReentranLockObj4T2(ReentranLockObj4 reentranLockObj4, String threadName) {
        this.reentranLockObj4 = reentranLockObj4;
        this.setName(threadName);
    }

    @Override
    public void run() {
        reentranLockObj4.waitFunCondition2();
    }
}
           

运行:

public class ReentranLockObj4Main {

    public static void main(String[] args) throws InterruptedException {
        ReentranLockObj4 reentranLockObj4 = new ReentranLockObj4();
        ReentranLockObj4T1 reentranLockObj4T1 = new ReentranLockObj4T1(reentranLockObj4, "thread01");
        reentranLockObj4T1.start();
        ReentranLockObj4T2 reentranLockObj4T2 = new ReentranLockObj4T2(reentranLockObj4, "thread02");
        reentranLockObj4T2.start();
        Thread.sleep(5000);
        reentranLockObj4.notifyFunCondition1();
    }

}
           

运行结果:

Java多线程编程-ReentrantLock(1)ReentrantLock简单使用

有运行可以看出,当线程1唤醒的时候,调用线程1的wait后的方法,线程2没有调用,就还在等待中,程序没有结束。由此可以得出结论,ReentrantLock可以唤醒指定种类的线程。

总结一下:

1.Object类中的wait相当于Condition中的await

2.Object类中的wait(long timeout) 相当于Condition中的await(long timeout,TimeUnit unit)

3.Object类中的notify相当于Condition中的signal

4.Object类中的notifyAll相当于Condition中的signalAll

继续阅读