Java并发编程之 lock (十)

一. 前言

synchronized 是Java的关键字,是Java的内置特性,在JVM层面实现了对临界资源的同步互斥访问,但 synchronized 粒度有些大,在处理实际问题时存在诸多局限性,比如响应中断等。Lock 提供了比 synchronized更广泛的锁操作,它能以更优雅的方式处理线程同步问题。

二.Lock相关接口

1.lock

void lock();

lock()方法是平常使用得最多的一个方法,就是用来获取锁。如果锁已被其他线程获取,则进行等待。如果采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此,一般来说,使用Lock必须在try…catch…块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。通常使用Lock来进行同步的话,是以下面这种形式去使用的:

Lock lock = ...;
lock.lock();
try{
    //处理业务逻辑
}catch(Exception ex){
	//异常处理
}finally{
 //释放锁
   lock.unlock();  
}

2.lockInterruptibly
当一个线程获取了锁之后,是不会被interrupt()方法中断的。因为interrupt()方法只能中断阻塞过程中的线程而不能中断正在运行过程中的线程。因此,当通过lockInterruptibly()方法获取某个锁时,如果不能获取到,那么只有进行等待的情况下,才可以响应中断的。与 synchronized 相比,当一个线程处于等待某个锁的状态,是无法被中断的,只有一直等待下去。

void lockInterruptibly() throws InterruptedException;

常用方法:

public void method() throws InterruptedException {
	//中断正在等待阻塞中的线程
    lock.lockInterruptibly();
    try {  
     //获取锁,处理逻辑
    }
    finally {
    	//解锁
        lock.unlock();
    }  
}

3.tryLock
尝试是否能获得锁 如果不能获得立即返回。如果锁未被另一个线程保持,则获取锁。
如果当前线程已经保持此锁,则将保持计数加 1,该方法将返回 true。
如果锁被另一个线程保持,则此方法将立即返回 false 值。

boolean tryLock();

常用方法:

 Lock lock = ...;
      if (lock.tryLock()) {
        try {
          // 进行锁状态,执行任务
        } finally {
          lock.unlock();
        }
      } else {
        // 执行替代动作
      }}     

应用场景,比如防止重复提交业务

4.tryLock+time
如果在给定的等待时间内是空闲的,当超过时间无获取锁,调用B.interrupt()会被中断等待线程

boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

实现可以检测到错误地使用锁,例如将导致调用的死锁,并且可能在这种情况下抛出(未检查)异常。

5.unlock
释放锁

void unlock();

6.newCondition

 Condition newCondition();

(1).Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。不同的是,Object中的这些方法是和同步锁捆绑使用的;而Condition是需要与互斥锁/共享锁捆绑使用的。
(2).Condition它更强大的地方在于:能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。
例如,假如多线程读/写同一个缓冲区:当向缓冲区中写入数据之后,唤醒"读线程";当从缓冲区读出数据之后,唤醒"写线程";并且当缓冲区满的时候,"写线程"需要等待;当缓冲区为空时,“读线程"需要等待。
如果采用Object类中的wait(), notify(), notifyAll()实现该缓冲区,当向缓冲区写入数据之后需要唤醒"读线程"时,不可能通过notify()或notifyAll()明确的指定唤醒"读线程”,而只能通过notifyAll唤醒所有线程(但是notifyAll无法区分唤醒的线程是读线程,还是写线程)。 但是,通过Condition,就能明确的指定唤醒读线程。

三.lock与synchronized区分

(1) Lock是一个接口,是JDK层面的实现;而synchronized是Java中的关键字,是Java的内置特性,是JVM层面的实现;
(2) synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
(3) Lock 可以让等待锁的线程响应中断,而使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
(4) 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到;
(5) Lock可以提高多个线程进行读操作的效率。

四.案例

1.模拟1000个线程同时对一个变量通过lock对一个变量进行增加操作

    static ExecutorService service=Executors.newCachedThreadPool();
    static Lock lock = new ReentrantLock();
    private static int num;
    public static void main(String[] args) {
        for (int i=0;i<1000;i++){
            service.submit(new Runnable() {
                @Override
                public void run() {   
                	caculateNum();                
                }
            });
        }
    }

    private  static void caculateNum() {
        try {
            lock.lock();
            num++;
        }finally {
            lock.unlock();
        }
        System.out.println("num:"+num);
    }

结果:最终可输出1000.


num:922
num:920
num:918
num:1000

2.通过newCondition模拟多线程并发执行对同一变量进行操作,通过对条件的操控,来对newCondition的认识

    static ExecutorService service= Executors.newCachedThreadPool();
    static Lock lock = new ReentrantLock();

    private static final Condition addCondition = lock.newCondition();
    private static final Condition reduceCondition = lock.newCondition();

    private static int  num;

    public static void main(String[] args) {
        for (int i=0;i<100;i++){
            service.submit(new AddTask());
        }

        for (int i=0;i<1000;i++){
            service.submit(new ReduceTask());
        }
    }

    public static class AddTask implements Runnable{
        @Override
        public void run() {
            try {
                lock.lock();
                if (num == 5) {
                    addCondition.await();
                }
                num++;
                System.out.println("线程AddTask===========:"+num);
                reduceCondition.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static   class ReduceTask implements Runnable{
        @Override
        public void run() {
            try {
                lock.lock();
//                if (num == 0) {
//                    reduceCondition.wait();
//                }
                num--;
                addCondition.signal();
                System.out.println("线程ReduceTask:"+num);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}

结果


线程ReduceTask:-883
线程ReduceTask:-884
线程ReduceTask:-885
线程ReduceTask:-886
线程ReduceTask:-887
线程ReduceTask:-888
线程ReduceTask:-889
线程ReduceTask:-890
线程ReduceTask:-891
线程ReduceTask:-892
线程ReduceTask:-893
线程ReduceTask:-894
线程ReduceTask:-895
线程ReduceTask:-896
线程ReduceTask:-897
线程ReduceTask:-898
线程ReduceTask:-899
线程ReduceTask:-900

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值