java 锁机制(synchronized 与 Lock)
在java中,解决同步问题,很多时候都会使用到synchronized和lock,这两者都是在多线程并发时候常使用的锁机制。
synchronized是java中的一个关键字,也就是说是java内置的一个特性。当一个线程访问一个被synchronized修饰的代码块,会自动获取对应的一个锁,并在执行该代码块时,其他线程想访问这个代码块,会一直处于等待状态,自有等该线程释放锁后,其他线程进行资源竞争,竞争获取到锁的线程才能访问该代码块。
线程释放synchronized修饰的代码块锁的方式有两种:
- 该线程执行完对应代码块,自动释放锁。
- 在执行该代码块是发生了异常,jvm会自动释放锁。
采用synchronized关键字来实现同步,会导致如果存在多个线程想执行该代码块,而当前获取到锁的线程又没有释放锁,可想而知,其他线程只有一只等待,这将严重印象执行效率。lock锁机制的出现就是为了解决该现象。lock是一个java接口,通过这个接口可以实现同步,使用lock时,用户必须手动进行锁的释放,否则容易出现死锁。
reentranlock是lock的唯一实现类。下面简单介绍一下reentranlock与synchronized的区别:
- synchronized是一个同步锁。当一个线程a访问synchronized修饰的代码块时,线程a就会获取该代码块的锁,如果这时存在其他线程范围该代码块时,将会阻塞,但是不影响这些线程访问其他非同步代码块。
- reentranlock是可重入锁。由构造方法可知,该锁支持两种锁模式,公平锁和非公平锁。默认是非公平的。
公平锁:当线程a获取访问该对象,获取到锁后,此时内部存在一个计数器num+1,其他线程想访问该对象,就会进行排队等待(等待队列最前一个线程处于待唤醒状态),直到线程a释放锁(num = 0),此时会唤醒处于待唤醒状态的线程进行获取锁的操作,一直循环。如果线程a再次尝试获取该对象锁是,会检查该对象锁释放已经被占用,如果被占用,会做一次是否为当前线程占用锁的判断,如果是内部计数器num+1,并且不需要进入等待队列,而是直接回去当前锁。
非公平锁:当线程a在释放锁后,等待对象的线程会进行资源竞争,竞争成功的线程将获取该锁,其他线程继续睡眠。
公平锁是严格的以fifo的方式进行锁的竞争,但是非公平锁是无序的锁竞争,刚释放锁的线程很大程度上能比较快的获取到锁,队列中的线程只能等待,所以非公平锁可能会有“饥饿”的问题。但是重复的锁获取能减小线程之间的切换,而公平锁则是严格的线程切换,这样对操作系统的影响是比较大的,所以非公平锁的吞吐量是大于公平锁的,这也是为什么jdk将非公平锁作为默认的实现。
下面是接口lock的方法:
附上对接口lock方法的测试,有什么问题欢迎各位大佬留言评论。
import java.util.concurrent.timeunit; import java.util.concurrent.locks.lock; import java.util.concurrent.locks.reentrantlock; public class testlock { // reentrantlock为lock的唯一实现类 private lock lock = new reentrantlock(); /** * 测试使用lock 的 lock()方法 :如果锁已经被其他线程获取,则等待 * @param thread */ public void testlock(thread thread){ try { // 1.获取锁 lock.lock(); system.out.println("线程 " + thread.getname() + " 获取了锁!"); }catch (exception e){ e.printstacktrace(); }finally { system.out.println("线程 " + thread.getname() + " 释放了锁!"); // 必须在 finally 中释放锁,防止死锁 lock.unlock(); } } /** * 测试使用lock 的 lock()方法 :通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。 * @param thread */ public void testlockinterruptibly(thread thread){ try { // 1.获取锁 lock.lockinterruptibly(); system.out.println("线程 " + thread.getname() + " 获取了锁!"); thread.sleep(3000); }catch (exception e){ e.printstacktrace(); }finally { system.out.println("线程 " + thread.getname() + " 释放了锁!"); // 必须在 finally 中释放锁,防止死锁 lock.unlock(); } } /** * 测试使用lock 的 trylock()方法 :如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false * @param thread */ public void testtrylock(thread thread){ if(lock.trylock()){// 如果获取到了锁 try { system.out.println("线程 " + thread.getname() + " 获取了锁!"); thread.sleep(3000); }catch (exception e){ e.printstacktrace(); }finally { system.out.println("线程 " + thread.getname() + " 释放了锁!"); // 必须在 finally 中释放锁,防止死锁 lock.unlock(); } }else { // 没有获取到锁 system.out.println("线程 " + thread.getname() + " 没有获取到锁!"); } } /** * 测试使用lock 的 trylock(long time, timeunit unit)方法 :和trylock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间, * 在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。 * @param thread */ public void testtrylock_time_unit(thread thread){ try { if(lock.trylock(1000, timeunit.milliseconds)){// 如果获取到了锁 try { system.out.println("线程 " + thread.getname() + " 获取了锁!"); }catch (exception e){ e.printstacktrace(); }finally { system.out.println("线程 " + thread.getname() + " 释放了锁!"); // 必须在 finally 中释放锁,防止死锁 lock.unlock(); } }else { // 没有获取到锁 system.out.println("线程 " + thread.getname() + " 没有获取到锁!"); } } catch (interruptedexception e) { e.printstacktrace(); } } public static void main(string[] args){ testlock testlock = new testlock(); thread a = new thread("a") { @override public void run() { /*// 测试 lock() testlock.testlock(thread.currentthread());*/ /*// 测试 lockinterruptibly() testlock.testlockinterruptibly(thread.currentthread());*/ /*// 测试 trylock() testlock.testtrylock(thread.currentthread());*/ /*// 测试 trylock(long time, timeunit unit) testlock.testtrylock_time_unit(thread.currentthread());*/ testlock.testtrylock_time_unit(thread.currentthread()); } }; thread b = new thread("b") { @override public void run() { testlock.testtrylock(thread.currentthread()); } }; a.start(); b.start(); } }