Java-Lock简介
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java-Lock简介相关的知识,希望对你有一定的参考价值。
1. 为什么会有Lock的出现
JDK5之前,我们一般都是通过synchronized来进行加锁操作,但synchronized是一种悲观锁,如果这个获取到锁的线程被阻塞了,但却没有释放锁,其他线程将只能一直无限期的等待着。而Lock可以实现其他线程只等待一定的时间或者能够响应中断。
又比如,当有多个线程读写文件时,读操作和写操作会发生冲突,写操作和写操作会发生冲突,但是读操作和读操作不会发生冲突。
如果采用synchronized关键字来实现同步的话,就会导致一个问题:如果多个线程都只是进行读操作,所以当一个线程在进行读操作时,其他线程只能等待无法进行读操作。因此就需要一种机制来使得多个线程都只是进行读操作时,线程之间不会发生冲突,通过Lock就可以办到。
并且通过Lock可以知道线程有没有成功获取到锁,而这点synchronized是无法办到的。
2. Lock不同于synchronized的地方?
1. Lock锁是通过代码手动实现的,加锁之后,需要我们手动释放锁,不释放的话将很可能会出现死锁问题;而Synchronized是Java的关键字,是由JVM来实现的,synchronized在代码块执行玩或锁定时方法抛出异常的情况下,JVM会自动释放锁;
2. 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到;
3. Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断
3. Lock接口方法介绍
package java.util.concurrent.locks; import java.util.concurrent.TimeUnit; /* * * @since 1.5 * @author Doug Lea */ public interface Lock { /** * 获取锁操作,如果锁被其他线程占用,则进行等待,该接口使用最多 */ void lock(); /** * 中断等待过程,如果线程A拿到了锁,而线程B处于等待中,那么调用该方法能够中断线程B的等待过程。而如果一个线程已经获取到了锁,是不会被该方法中断的。要注意方法抛出了异常 */ void lockInterruptibly() throws InterruptedException; /** * tryLock用来尝试获取锁,如果获取成功,则返回true,如果获取失败(锁被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。 * Lock lock = ...; * if (lock.tryLock()) { * try { * ... * } finally { * lock.unlock(); * } * } else { * ... * } * */ boolean tryLock(); /** * 和tryLock方法类似,但该方法在拿不到锁的时候会等待一定的时间,如果在时间期限内还拿不到锁,就返回false,如果这段时间内拿到了锁,则返回true; */ boolean tryLock(long time, TimeUnit unit) throws InterruptedException; /** * 释放锁的方法,一般用于finally中进行释放 */ void unlock(); /***/ Condition newCondition(); }
注意:
由于Lock要手动释放,因此一般使用Lock都必须在try{}catch{}中进行,在finally中进行释放锁的操作;
Lock lock = ...; lock.lock(); try { //处理任务 } catch (Exception ex) { } finally { lock.unlock(); //释放锁 }
4. 一般我们可以通过ReentrantLock来操作,ReentrantLock是Lock的实现类;
a. testLockInterruptibly
/** * testLockInterruptibly * * @param thread the thread */ private void testLockInterruptibly(Thread thread) throws InterruptedException{ if (lock.tryLock()) { try { System.out.println("当前线程 " + thread.getName() + "获得了锁!"); Thread.sleep(3000); } catch (Exception e) { e.printStackTrace(); } finally { System.out.println("当前线程 " + thread.getName() + "释放了锁!"); lock.unlock(); } } else { System.out.println("线程" + thread.getName() + "中断了!"); lock.lockInterruptibly(); } } public static void main(String[] args) { Main main = new Main(); new Thread(() -> { try { main.testLockInterruptibly(Thread.currentThread()); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); new Thread(() -> { try { main.testLockInterruptibly(Thread.currentThread()); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); }
当前线程 Thread-0获得了锁! 线程Thread-1中断了! 当前线程 Thread-0释放了锁!
b. testLock,testTryLock:
/** * Test lock. * * @param thread the thread */ private void testLock(Thread thread) { lock.lock(); try { System.out.println("当前线程 " + thread.getName() + "获取了锁!"); } catch (Exception e) { } finally { System.out.println("当前线程 " + thread.getName() + "释放了锁!"); lock.unlock(); } } /** * Test tryLock. * * @param thread the thread */ private void testTryLock(Thread thread) { if (lock.tryLock()) { try { System.out.println("当前线程 " + thread.getName() + "获得了锁!"); } catch (Exception e) { e.printStackTrace(); } finally { System.out.println("当前线程 " + thread.getName() + "释放了锁!"); lock.unlock(); } } else { System.out.println("有人占着锁,线程" + thread.getName() + "获取不了锁"); } } public static void main(String[] args) { Main main = new Main(); new Thread(() -> main.testLock(Thread.currentThread())).start(); new Thread(() -> main.testLock(Thread.currentThread())).start(); }
另一个Lock是读写锁:ReadWriteLock,该接口只有两个方法,一个用来获取读锁,一个用来获取写锁。也就是说将文件的读写操作分开,分成2个锁来分配给线程,从而使得多个线程可以同时进行读操作。
而ReentrantReadWriteLock则实现了ReadWriteLock接口。
所以,在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。
本文参考自:https://www.cnblogs.com/baizhanshi/p/6419268.html
以上是关于Java-Lock简介的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向类加载器 ClassLoader ( 类加载器源码简介 | BaseDexClassLoader | DexClassLoader | PathClassLoader )(代码片段
Android 逆向Linux 文件权限 ( Linux 权限简介 | 系统权限 | 用户权限 | 匿名用户权限 | 读 | 写 | 执行 | 更改组 | 更改用户 | 粘滞 )(代码片段
SpringCloud系列十一:SpringCloudStream(SpringCloudStream 简介创建消息生产者创建消息消费者自定义消息通道分组与持久化设置 RoutingKey)(代码片段
C#-WebForm-★内置对象简介★Request-获取请求对象Response相应请求对象Session全局变量(私有)Cookie全局变量(私有)Application全局公共变量Vi(代码片段