java并发编程的艺术-(4)

四、Lock的使用

Lock对象比synchronized 的使用更加灵活,并且支持嗅探锁定,多路分支通知

1. ReentrantLock 实现同步

  • lock对象的 lock()方法,必须在 finally{ lock.unlock () };释放掉锁
  • lock.newCondition()产生的 Condition 对象只在 lock.lock() 与 lock.unlock() 之间有效;
    比如 condition.await(); 和 condition.signal();
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class MyService {
    private ReentrantLock lock = new ReentrantLock();
    public Condition condition = lock.newCondition();
    
    public void serviceA(){
        try {
            lock.lock();
            System.out.println("在这里进行同步业务操作");
        }finally {
            lock.unlock();
        }
    }
    
    public void await() {
        try {
            lock.lock();
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    
    public void signal(){
        try {
            lock.lock();
            condition.signal();
        } finally {
            lock.unlock();
        }
    }

    public void signalAll(){
        try {
            lock.lock();
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
}

2.使用多个 Condition 实现通知部分线程

同一个 Lock对象可以制造多个 Condition ,让不同的线程使用不同的Condition 就可以达到 只通知使用特定 Condition 的线程的目的。

与 synchronized( obj ) { if( tag ) { obj.wait(); } … obj.notify() } 使用相似,

也可以用 condition 的 await 和 signal / signalAll 实现 n对 n 的生产消费模型

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class MyServiceA {
    ReentrantLock lock = new ReentrantLock();
    Condition conditionA = lock.newCondition();
    Condition conditionB = lock.newCondition();
    public void awaitA(){
        try {
            lock.lock();
            conditionA.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    public void signalA(){
        try {
            lock.lock();
            conditionA.signalAll();
        }  finally {
            lock.unlock();
        }
    }
    public void awaitB(){
        try {
            lock.lock();
            conditionB.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    public void signalB(){
        try {
            lock.lock();
            conditionB.signalAll();
        }  finally {
            lock.unlock();
        }
    }
}


public class MyServiceATest {
    public static void main(String[] args) throws InterruptedException {
        final MyServiceA myServiceA = new MyServiceA();
        Thread tA = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程A开始等待");
                myServiceA.awaitA();
                System.out.println("线程A被唤醒");
            }
        });

        Thread tB = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程B开始等待");
                myServiceA.awaitB();
                System.out.println("线程B被唤醒");
            }
        });
        tA.start();
        tB.start();
        Thread.sleep(1000);
        myServiceA.signalA();
    }

}

3.Lock锁分为 ‘公平锁’和‘非公平锁’

  • 公平锁:根据线程加锁的顺序,即先进先出,来分配锁的获取顺序(趋势是这样,也有少数几个线程可能例外)

  • 非公平锁:先加锁的不一定先得锁,可能造成某些线程一直得不到锁,是随机获取的

    new ReenTrantLock( true ); // true 公平锁,false 非公平锁

4.ReentrantLock 的常用api

4.1 getHoldCount() 当前锁嵌套层级

当前线程执行 getHoldCount时的得锁次数

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

    public void serviceA(){
        try {
            lock.lock();
            System.out.println("[1]当前加锁次数:"+lock.getHoldCount());

            try {
                lock.lock();
                System.out.println("[2]当前加锁次数:"+lock.getHoldCount());
            }finally {
                lock.unlock();
            }

        }finally {
            lock.unlock();
        }
    }

4.2 getQueueLength() 得到正在竞争等待获取 此 Lock对象的线程 估计个数

10个线程同时竞争一个Lock,1个得锁,9个进入竞争等待。

   public void serviceB(){
        try {
            lock.lock();
            System.out.println("线程:"+Thread.currentThread().getName()+"得到锁");
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }


public class Mytest {
    public static void main(String[] args) throws InterruptedException {
        final MyServiceB myServiceB = new MyServiceB();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                myServiceB.serviceB();

            }
        };
        for (int i = 0; i < 10 ; i++) {
            Thread thread = new Thread(runnable);
            thread.start();
        }
        Thread.sleep(3000);
        int queueLength = myServiceB.lock.getQueueLength();
        System.out.println("此时等待得锁的线程数:"+queueLength);
    }
}

4.3 getWaitQueueLength( Condition ) 指定 condtion 下wait()状态的线程个数

4.4 hasQueuedThread( Thread ) 指定线程是否在竞争等待得锁

4.5 hasQueuedThreads( )是否有线程在竞争等待得到 此锁

4.6 hasWaiters( Condition ) 指定 condtion 是否有 wait()状态的线程

4.7 isFair() 锁是否公平

4.8 isHeldByCurrentThread() 当前线程是否持有锁

4.9 isLocked() 此锁定是否有任意线程保持

4.10 lock. lockInterruptibly() : 如果当前线程未标记中断,得到锁,已被中断,则异常

4.11 tryLock() 当前锁没有被其它线程保持时,就获得锁;

4.12 tryLock( long , TimeUnit) 指定时间内不断 tryLock()

4.14 condition.awaitUninterruptibly( ) 不可被中断的等待

普通的 await() 方法,在使线程等待期间遇到,线程 interrupt() 的时候,会抛出中断异常

4.15 awaitUntil( long ) 等待一段时间唤醒自己

5. 使用 多个condition 的等待通知模型,可以实现对线程业务执行的排序

下列 线程A 唤醒 线程B 唤醒 线程C 唤醒 线程A 形成闭环 , 线程A是起点 ,

    线程A{
        if ( tag1 ){
            conditionA.await();
        }
        doSomeThingA();
        
        conditionB.singnalAll();
    } 
    
    线程B{
        if ( tag2 ){
            conditionB.await();
        }
        doSomeThingB();
        
        conditionC.singnalAll();
    } 
    
    线程C {
        if ( tag2 ){
            conditionC.await();
        }
        doSomeThingC();
        
        conditionA.singnalAll();
    } 

6. 注意事项

  • 使用 Condition 的方法或者以 Condition作为参数的方法时,都必须包含 在 lock.lock()后的代码块中
  • 使用 await( ) 与 signal( ) 方法的时候,要注意 signal( ) 不要抢先执行了(比如在父线程中时,signal () 代码 可能抢先)

7. 读/写锁 ReenTrantReadWriteLock

为了提高代码的执行效率,Lock 对象的 readLock().lock() 之间是不会互斥的,
而 writeLock().lock() 与读写锁都会互斥

读锁之间不互斥,写锁与读锁和写锁都会互斥,这样让允许 ‘只读’ 的代码可以异步调用,但是当读发生在同时有写的发生的时候就会互斥。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值