多线程(三)---Lock
一、Lock的基本介绍
1.Lock本身是一个接口,接口中含有四个抽象方,且不是Java内置的。
2.分别是lock(),unlock(),trylock()以及lockInterruptibly(){下方有详细介绍}。
3.Lock是一个操作麻烦(对比Synchronized来说),但是功能强大的锁。
lock的源码:
public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); }
透过源码我们可以看到Lock本身是一个包含有四个抽象方法的接口。
二、Synchronized的优点与缺点
Synchronized的优点:操作比较简单不需要手动释放锁,自带异常处理机制。
Synchronized的缺点:Synchronized功能单一,无法做比较细节的操作。Synchronized采用悲观锁机制,在保护了安全的同时,牺牲了效率。
三、Lock的四种方法
1.因为Lock只是一个接口,而实现了Lock接口的类只有ReentrantLock。所以我们通过子类向上传递的方式得到Lock。
1.lock():
lock本身只是单纯的一个锁,当调用lock()时,如果资源处于被释放状态,则线程锁住该资源;否则线程进入等待状态等候资源的被释放。值的注意的是,Lock本身不具备异常机制,所以我们需要自行作出异常处理机制。
public class LockTest { static Lock lock = new ReentrantLock(); public static void main(String[]args){ new Thread( new Runnable() { @Override public void run() { LockTest.instert(Thread.currentThread()); } } ).start(); new Thread( new Runnable() { @Override public void run() { LockTest.instert(Thread.currentThread()); } } ).start(); } public static void instert(Thread thread){ lock.lock(); try{ System.out.println(Thread.currentThread()+":获取了锁"); }catch (Exception e){ e.printStackTrace(); }finally { System.out.println(Thread.currentThread()+":释放了锁"); lock.unlock(); } } }
2.unlock():
当调用该方法时,会释放当下资源。与Synchronized不同,Lock是需要手动调用方法来释放锁的(很重要),不然将陷入死锁状态。所以我们常常在异常处理中的finally模块来调用unlock()。
3.trylock():
线程尝试获取资源,如果可以获得该资源,则会返回true,否则会返回false;trylock的重载方法中,可以设置时间以及时间格式来完成在一段时间内尝试获取资源,如果有则返回true,否则返回false;
这是使用trylock方法:
public class LockTest { static Lock lock = new ReentrantLock(); public static void main(String[]args) { new Thread ( new Runnable() { @Override public void run(){ LockTest.instert(Thread.currentThread()); } } ).start(); new Thread( new Runnable() { @Override public void run() { LockTest.instert(Thread.currentThread()); } } ).start(); } public static void instert(Thread thread){ if(lock.tryLock()) { try { System.out.println(Thread.currentThread() + ":获取了锁"); Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } finally { System.out.println(Thread.currentThread() + ":释放了锁"); lock.unlock(); } }else{ System.out.println(Thread.currentThread()+":没能获得锁"); } } }
trylock带参数的实现:由于带参方法作出了InterrupedException,所以调用该方法的方法都应该抛出该异常,但是重写的Run方法无法抛出异常所以需要手动在run中调用trycatch方式抛出异常
public class LockTest { static Lock lock = new ReentrantLock(); public static void main(String[]args) { new Thread( new Runnable() { @Override public void run() { try{ LockTest.instert(Thread.currentThread()); }catch (InterruptedException e){ e.printStackTrace(); } } } ).start(); new Thread( new Runnable() { @Override public void run() { try{ LockTest.instert(Thread.currentThread()); }catch (InterruptedException e){ e.printStackTrace(); } } } ).start(); } public static void instert(Thread thread) throws InterruptedException{ if(lock.tryLock((long)2000,TimeUnit.MILLISECONDS)){ try { System.out.println(Thread.currentThread() + ":获取了锁"); Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } finally { System.out.println(Thread.currentThread() + ":释放了锁"); lock.unlock(); } }else{ System.out.println(Thread.currentThread()+":没能获得锁"); } } }
4.lockInterruptibly():
lockInterruptibly()方法比较特殊,当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。
这就是Lock锁的可中断性,是Synchronized不具备的:
public class LockTest { static Lock lock = new ReentrantLock(); public static void main(String[]args) { new Thread( new Runnable() { @Override public void run() { try{ LockTest.instert(Thread.currentThread()); }catch (InterruptedException e){ e.printStackTrace(); } } } ).start(); Thread thread1 = new Thread( new Runnable() { @Override public void run() { try{ LockTest.instert(Thread.currentThread()); }catch (InterruptedException e){ e.printStackTrace(); } } } ); thread1.start(); thread1.interrupt(); System.out.println(thread1.isAlive()); } public static void instert(Thread thread) throws InterruptedException{ // if(lock.tryLock((long)2000,TimeUnit.MILLISECONDS)){ //2000毫秒的尝试 // if(lock.tryLock()){ //trylock尝试一次 // lock.lock(); //调用锁方法 lock.lockInterruptibly(); try { System.out.println(Thread.currentThread() + ":获取了锁"); Thread.sleep(10000); } catch (Exception e) { e.printStackTrace(); } finally { System.out.println(Thread.currentThread() + ":释放了锁"); lock.unlock(); } // }else{ // System.out.println(Thread.currentThread()+":没能获得锁"); // } } }
四、ReadWriteLock类:
这个类也是一个接口,该接口只定义了writeLock与readLock两个抽象方法,而且这两个方法最后都会返回一个Lock。源码如下:
public interface ReadWriteLock { /** * Returns the lock used for reading. * * @return the lock used for reading */ Lock readLock(); /** * Returns the lock used for writing. * * @return the lock used for writing */ Lock writeLock(); }
调用ReentrantReadWriteLock中readLock会返回ReentrantReadWriteLock.ReadLock类,该类中依然拥有trylock,lock,newCondition,unlock等方法。
调用ReentrantReadWriteLock中readLock会返回ReentrantwriteWriteLock.WriteLock类,该类中依然拥有trylock,lock,newCondition,unlock等方法。
当两个都是读取时,锁允许多个线程使用,有一个是写线程时,则进入的线程会锁住。
public class ReadWrite { static ReentrantReadWriteLock rl =new ReentrantReadWriteLock(); public static void main(String[]args){ new Thread( new Runnable() { @Override public void run() { ReadWrite.insert(Thread.currentThread()); } } ).start(); new Thread( new Runnable() { @Override public void run() { ReadWrite.insert(Thread.currentThread()); } } ).start(); } public static void insert(Thread thread1){ rl.writeLock().lock(); // rl.readLock().lock(); try{ long start = System.currentTimeMillis(); while(System.currentTimeMillis()-start<=300) { System.out.println(Thread.currentThread().getName() + "正在进行写操作"); Thread.sleep(10); } }catch (Exception e){ e.printStackTrace(); }finally { rl.writeLock().unlock(); // rl.readLock().unlock(); System.out.println((Thread.currentThread()+":已经结束了写操作")); } } }