ReentranLock in Java

In Java multithreading, the synchronized keyword can be used to achieve synchronization and mutual exclusion between threads, but the newly added ReentranLock in JDK1.5 can also achieve the same effect, and it is also more powerful in extension functions.

1. Use lock method and unlock method to achieve synchronization

    The lock method of ReentranLock is used to acquire the lock, and the method of calling unlock is to release the lock.

public class Test1 {
    public static void main(String[] args) throws InterruptedException {
        Service service = new Service();
        ThreadA a = new ThreadA(service);
        a.setName("A");
        ThreadA aa = new ThreadA(service);
        aa.setName("AA");
        Thread.sleep(100);
        ThreadB b = new ThreadB(service);
        b.setName("B");
        ThreadB bb = new ThreadB(service);
        bb.setName("BB");
        bb.start();
        a.start();
        aa.start();
        b.start();
    }
}
class ThreadA extends Thread{
    private Service service;
    public ThreadA(Service service){
        this.service = service;
    }
    @Override
public void run() {
        service.methodA();
}        
}
class ThreadB extends Thread{
    private Service service;
    public ThreadB(Service service){
        this.service = service;
    }
    @Override
public void run() {
        service.methodB();
}        
}
class Service{
    private ReentrantLock lock = new ReentrantLock();
    public void methodA(){
        try {
            lock.lock();
            System.out.println("A  ThreadName = "+Thread.currentThread().getName());
            Thread.sleep(2000);
            System.out.println("A  ThreadName = "+Thread.currentThread().getName());
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void methodB(){
        try {
            lock.lock();
            System.out.println("B  ThreadName = "+Thread.currentThread().getName());
            Thread.sleep(2000);
            System.out.println("B  ThreadName = "+Thread.currentThread().getName());
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

上述代码使用lock方法和unlock方法实现了线程间的同步。输出结果如下


结果说明,调用lock方法的代码的线程就持有了对象监视器,其他线程只有等待锁被释放时再次争抢,效果和使用sunchronized关键字一样。

二、使用Condition实现等待/通知。

使用synchronized关键字以及wait方法和notify方法相结合可以实现等待/通知模式,而ReentranLock也可以实现同样的功能,但需要借助于Condition对象。

你可以在一个ReentranLock对象里面创建多个Condition实例(对象监视器),线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度线程上更加灵活。

public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        Service1 service1 = new Service1();
        ThreadAA threadAA = new ThreadAA(service1);
        threadAA.setName("A");
        threadAA.start();
        Thread.sleep(1000);
        service1.singal();
    }
}
class Service1{
    private ReentrantLock lock = new ReentrantLock();
    public Condition condition = lock.newCondition();
    public void await(){
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName()+"我在等待通知的到来1");
            condition.await();
            System.out.println(Thread.currentThread().getName()+"等到通知了,我继续执行");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void singal(){
        try {
            lock.lock();
            System.out.println("我要通知在等待的线程");
            condition.signal();
        }finally {
            lock.unlock();
        }
    }
}
class ThreadAA extends Thread{
    private Service1 service1;
    public ThreadAA(Service1 service1){
        this.service1 = service1;
    }
    @Override
    public void run() {
        service1.await();
    }
}

上述代码就描述了Condition的用法,Condition在调用await方法之前必须调用lock方法来获得同步监视器。否则会报错。

而singal方法可以唤醒同一个Condition(对象监视器)监视下的线程。

输出结果如下:


等待一秒后通知


线程继续执行未完的代码。其中wait方法相当于await方法,notify方法相当于singal方法,notifyAll方法相当于singalAll方法。

刚才我们只是使用一个Condition方法通知一个线程,这次我们使用多个Condition通知多个线程。

public class Test3 {
    public static void main(String[] args) throws InterruptedException {
        Service2 service2 = new Service2();
        Thread1 thread1 = new Thread1(service2);
        thread1.start();
        Thread2 thread2 = new Thread2(service2);
        thread2.start();
        Thread.sleep(2000);
        service2.singalA();
        Thread.sleep(2000);
        service2.singalB();
    }
}
class Thread1 extends Thread{
    private Service2 service2 = new Service2();
    public Thread1(Service2 service2){
        this.service2 = service2;
    }
    @Override
    public void run() {
        service2.awaitA();
    }
}
class Thread2 extends Thread{
    private Service2 service2 = new Service2();
    public Thread2(Service2 service2){
        this.service2 = service2;
    }
    @Override
    public void run() {
        service2.awaitB();
    }
}
class Service2{
    private ReentrantLock lock = new ReentrantLock();
    public Condition condition1 = lock.newCondition();
    public Condition condition2 = lock.newCondition();
    public void awaitA(){
        try {
            lock.lock();
            System.out.println("awaitA begin 在等待通知 ");
            condition1.await();
            System.out.println("awaitA end 等到通知");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void awaitB(){
        try {
            lock.lock();
            System.out.println("awaitB begin 在等待通知 ");
            condition2.await();
            System.out.println("awaitB end 等到通知");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void singalA(){
        try {
            lock.lock();
            System.out.println("我要通知awaitA");
            condition1.signal();
        }finally {
            lock.unlock();
        }
    }
    public void singalB(){
        try {
            lock.lock();
            System.out.println("我要通知awaitB");
condition2.signal();
}finally {
            lock.unlock();
}                            
    }
}
The above code has two threads, thread 1 executes the awaitA method, thread 2 executes the awaitB method, and singalA only wakes up the thread monitored by condition1, while singalB only wakes up the thread monitored by condition2. The running result is as follows


At the beginning, both thread 1 and thread 2 are waiting for notification


Two seconds later, singalA is called to wake up thread 1, thread 1 is notified and executes unfinished code, while thread 2 is still waiting


After another two seconds, call the singalB method to notify thread 2, thread 2 receives the notification and completes the execution

3. Fair lock and unfair lock

    ReentranLock is divided into fair locks and unfair locks. Fair locks indicate that the order in which threads acquire locks is allocated according to the order in which threads are locked, that is, the order of first-come, first-served. Unfair locks are a kind of lock preemption mechanism. Locks are obtained randomly, which may cause some threads to consistently fail to obtain locks, so it is unfair.

public class Test4 {
    public static void main(String[] args) {
        Service3 service3 = new Service3(true);
        Thread[] threads = new Thread[10];
        for (int i=0;i<10;i++){
            threads[i] = new Thread(){
                @Override
                public void run() {
                    service3.testMethod();
                }
            };
            threads[i].setName("线程"+(i+1));
        }

        for (int i=0;i<10;i++){
            threads[i].start();
        }
    }
}
class Service3{
    private ReentrantLock lock;
    public Service3(boolean isFair){
        lock = new ReentrantLock(isFair);
    }
    public void testMethod(){
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName()+"获得锁");
        }finally {
            lock.unlock();
        }
    }
}

上述代码是使用公平锁,在构造ReentranLock时,有一个布尔值,为true代表公平锁,默认为非公平锁。


从结果来看,基本呈有序状态,这就是公平锁


而这个是非公平锁,可以看出基本是乱序。

总结:

    1、ReentranLock使用lock和unlock来获得锁和释放锁

    2、unlock最好的地方就是finally中,因为这样正常运行或者异常运行都会释放锁

    3、使用Condition可以进行线程间通信,而且使用非常灵活,记住,使用condition的await和singal方法之前,必须调用lock方法获得对象监视器

    4、公平锁和非公平锁基本没什么说的啦

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325685015&siteId=291194637