类 ReentrantLock 使用(一)

        在Java多线程中,可以使用 synchronized 关键字来实现线程之间同步互斥,但在JDK1.5中新增加了 ReentrantLock 类也能达到同样的效果,并且在扩展功能上也更加强大,比如具有嗅探锁定,多路分支通知等功能,而且在使用上也比synchronized更加的灵活。

1. 使用 ReentrantLock 测试同步一

@Test
public void test() {
        MyService myService = new MyService();
        ThreadG1A threadA = new ThreadG1A(myService);
        threadA.setName("ThreadA");
        ThreadG1A threadB = new ThreadG1A(myService);
        threadB.setName("ThreadB");
        ThreadG1A threadC = new ThreadG1A(myService);
        threadC.setName("ThreadC");
        ThreadG1A threadD = new ThreadG1A(myService);
        threadD.setName("ThreadD");
        threadA.start();
        threadB.start();
        threadC.start();
        threadD.start();
    }

class MyService {
    private Lock lock = new ReentrantLock();

    public void testMethod() {
        try {
            lock.lock();
            for (int i=0; i<3; i++) {
                System.out.println("ThreadName=" + Thread.currentThread().getName() + (" " + (i+1)));
            }
        } finally {
            lock.unlock();
        }
    }
}

class ThreadG1A extends Thread {
    private MyService service;

    public ThreadG1A(MyService service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.testMethod();
    }
}


执行结果:
ThreadName=ThreadA 1
ThreadName=ThreadA 2
ThreadName=ThreadA 3
ThreadName=ThreadC 1
ThreadName=ThreadC 2
ThreadName=ThreadC 3
ThreadName=ThreadD 1
ThreadName=ThreadD 2
ThreadName=ThreadD 3
ThreadName=ThreadB 1
ThreadName=ThreadB 2
ThreadName=ThreadB 3

调用 ReentrantLock 对象的 lock() 方法获取锁,调用 unlock() 方法释放锁。        

结果分析:

        从运行结果来看,当前线程打印完毕之后将锁释放,其他线程获取到锁才可以继续打印。并且线程之间打印的顺序是随机的。

2. 使用 ReentrantLock 测试同步二

@Test
public void test() throws InterruptedException {
        MethodAService methodAService = new MethodAService();
        ThreadA threadA = new ThreadA(methodAService);
        threadA.setName("A");
        ThreadAA threadAA = new ThreadAA(methodAService);
        threadAA.setName("AA");
        ThreadB threadB = new ThreadB(methodAService);
        threadB.setName("B");
        ThreadBB threadBB = new ThreadBB(methodAService);
        threadBB.setName("BB");
        threadA.start();
        threadAA.start();
        threadB.start();
        threadBB.start();
        while (Thread.activeCount() > 1){}
    }

class MethodAService {
    private Lock lock = new ReentrantLock();

    public void methodA() {
        lock.lock();
        System.out.println("methodA begin ThreadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
        System.out.println("methodA end ThreadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
    }

    public void methodB() {
        lock.lock();
        System.out.println("methodB begin ThreadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
        System.out.println("methodB end ThreadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
    }
}

class ThreadA extends Thread {
    private MethodAService service;

    public ThreadA(MethodAService service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.methodA();
    }
}

class ThreadAA extends Thread {
    private MethodAService service;

    public ThreadAA(MethodAService service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.methodA();
    }
}

class ThreadB extends Thread {
    private MethodAService service;

    public ThreadB(MethodAService service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.methodB();
    }
}

class ThreadBB extends Thread {

    private MethodAService service;

    public ThreadBB(MethodAService service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.methodB();
    }
}


执行结果:
methodA begin ThreadName=A time=1661914714665
methodA end ThreadName=A time=1661914714665
methodA begin ThreadName=AA time=1661914714665
methodA end ThreadName=AA time=1661914714665
methodB begin ThreadName=BB time=1661914714665
methodB end ThreadName=BB time=1661914714665
methodB begin ThreadName=B time=1661914714665
methodB end ThreadName=B time=1661914714665

结果分析:

        此实验说明,调用 lock.lock() 代码的线程就持有了 “对象监视器”,其他线程只有等待锁被释放时再次争抢。效果和使用synchronized关键字一样。

3. 使用 Condition 实现等待/通知:错误用法与解决

        关键字 synchronized 与 wait() 和 notify() / notifyAll() 方法相结合可以实现等待/通知模式,类 ReentrantLock 也可以实现同样的功能,但需要借助于 Condition 对象。Condition 类是在JDK5中出现的技术,使用它有更好的灵活性,比如可以实现多路通知功能,也就是在一个 Lock 对象里面可以创建多个 Condition (即对象监视器) 实例,线程对象可以注册在指定的 Condition 中,从而可以有选择性地进行线程通知,在调度线程上更加灵活。

        在使用notify() / notifyAll() 方法进行通知时,被通知的线程却是由JVM随机选择的。但使用 ReentrantLock 结合 Condition 类是可以实现前面介绍过的 “选择性通知”,这个功能是非常重要的,而且在 Condition 类中是默认提供的。

        而 synchronized 就相当于整个 Lock 对象中只有一个单一的 Condition 对象,所有的线程都注册在它一个对象的身上。线程开始 notifyAll() 时,需要通知所有的 WAITING 线程,没有选择权,会出现相当大的效率问题。

@Test
public void test() {
        MyServiceB myServiceB = new MyServiceB();
        ThreadG3A threadG3A = new ThreadG3A(myServiceB);
        threadG3A.start();
        while (Thread.activeCount() > 1){}
    }

class MyServiceB {

    private Lock lock = new ReentrantLock();

    private Condition condition = lock.newCondition();

    public void await() {
        try {
            System.out.println("A");
            condition.await();
            System.out.println("B");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class ThreadG3A extends Thread {
    private MyServiceB myServiceB;

    public ThreadG3A(MyServiceB myServiceB) {
        this.myServiceB = myServiceB;
    }

    @Override
    public void run() {
        myServiceB.await();
    }
}

执行结果:
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
	at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:154)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1265)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(AbstractQueuedSynchronizer.java:1728)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2041)
	at com.demo.g.MyServiceB.await(ThreadG3.java:45)
	at com.demo.g.ThreadG3A.run(ThreadG3.java:62)

结果分析:

        报错的异常信息是监视器出错,解决的办法是必须在 condition.await() 方法调用之前调用 lock.lock() 代码获取同步监视器。

@Test
    public void test() {
        MyServiceB myServiceB = new MyServiceB();
        ThreadG3A threadG3A = new ThreadG3A(myServiceB);
        threadG3A.start();
        while (Thread.activeCount() > 1){}
    }

class MyServiceB {

    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("锁释放了!");
        }
    }
}

class ThreadG3A extends Thread {
    private MyServiceB myServiceB;

    public ThreadG3A(MyServiceB myServiceB) {
        this.myServiceB = myServiceB;
    }

    @Override
    public void run() {
        myServiceB.await();
    }
}

结果分析:

         在控制台只打印了一个字母A,原因是调用了 Condition 对象的 await() 方法,使当前执行任务的线程进入了等待 WAITING 状态。

4. 正确使用Condition实现等待/通知        

@Test
public void test() throws InterruptedException {
        G4Service g4Service = new G4Service();
        ThreadG4A threadG4A = new ThreadG4A(g4Service);
        threadG4A.setName("threadG4A");
        threadG4A.start();
        Thread.sleep(3000);
        g4Service.signal();
    }

class ThreadG4A extends Thread {

    private G4Service g4Service;

    public ThreadG4A(G4Service g4Service) {
        this.g4Service = g4Service;
    }

    @Override
    public void run() {
        g4Service.await();
    }
}

class G4Service {
    private Lock lock = new ReentrantLock();

    public Condition condition = lock.newCondition();

    public void await() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "阻塞, await 时间为:" + System.currentTimeMillis());
            condition.await();
            System.out.println(Thread.currentThread().getName() + "释放,时间为:" + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void signal() {
        lock.lock();
        System.out.println("signal 时间为:" + System.currentTimeMillis());
        condition.signal();
        lock.unlock();
    }
}


执行结果:
threadG4A阻塞, await 时间为:1661928413396
signal 时间为:1661928416396
threadG4A释放,时间为:1661928416396

结果分析:

        正确使用 Condition 实现 等待/通知 模式。     

Object 类中的 wait() 方法相当于 Condition 类中的 await() 方法。
Object 类中的 wait(long timeout) 方法相当于 Condition 类中的 await(long timeout, TimeUnit unit) 方法。
Object 类中的 notify() 方法相当于 Condition 类中的 signal() 方法。
Object 类中的 notifyAll() 方法相当于 Condition 类中的 signalAll() 方法。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值