Java5多线程---Lock
在Java5中的并发包java.util.concurrent.locks 下有个接口Lock ,这个接口是实现同步访问的另外一种形式,Lock为锁和等待条件提供一个框架的接口,它不同于内置同步和监视器。以前我们都是在用synchronized 关键字,用于修饰方法(同步方法)或者同步代码块来实现同步访问,在java5中我们可以用Lock来实现同步的访问。我们都知道用关键字synchronized修饰的方法或者synchronized代码块,当一个线程获取对应的监控器(对象锁)时候,并执行synchronized里面的代码的时候,其它线程会一直处于等待此线程释放监视器才有机会进行执行,此线程释放监视器有两种,一是此线程执行完毕,二是出现异常java虚拟机会让其自动释放监视器。
试想下在当前获取监视器的线程进行耗时的操作(如:进行IO操作或者直接说进行休眠),那么其它线程将无限的进行等待下去。synchronized方法或者 synchronized代码块执行完毕之后会自动释放监视器,而Lock需要用户自己手动释放,但lock 是更细粒度控制同步问题。
一、Lock
1、Lock是 java.util.concurrent.locks包下的一个接口,它实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作。锁变成了更加的灵活性,也带来了更多的责任。我们不使用块结构锁就事情了synchronized方法和代码块时会自动释放功能,大多数情况下,应该使用以下语句
Lock l = ...; l.lock(); try { // access the resource protected by this lock } finally { l.unlock(); }锁定和取消锁定出现在不同的作用访问中时,必须谨慎地确保保持锁定时的所有执行的所有代码块用try-finally 货try-catch 加以保护,用于确保在必要时候释放锁的作用。所有Lock实现都必须实时与监视器锁提供的相同内存同步:成功的lock操作与成功的Lock操作具有相同的内存同步效应,成功的unlock操作与成功的Unlock操作具有相同的内存同步效应 2、接口方法说明
void lock(); void lockInterruptibly(); Condition newCondition(); boolean tryLock(); boolean tryLock(long time,TimeUnit unit); void unlock()void lock() 方法获取锁,如果当前锁不可用,出于禁用当前的线程,并且在获得锁之前,该线程将处于休眠状态。unlock释放锁,当前持有监视器(锁)释放出来,让给其它线程持有机会具体用法代码如下:
package java5; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * lock的用法 * @author dongtian * @date 2015年6月15日 下午3:49:27 */ public class LockTest { static Lock lock = new ReentrantLock(); private static class Thread1 extends Thread { @Override public void run() { lock.lock(); System.err.println(Thread.currentThread().getName() +"获取锁"); try { for (int i = 0; i < 10; i++) { System.err.println("当前线程" +Thread.currentThread().getName()); Thread.currentThread().sleep(100); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); System.err.println(Thread.currentThread().getName() +"释放锁"); } } } public static void main(String[] args) { Thread1 thread1 = new Thread1(); Thread1 thread2 = new Thread1(); thread1.start(); thread2.start(); } }输出结果:是线程0执行10次,完成释放锁然后线程1获取锁执行10次然后释放锁
Thread-0获取锁 当前线程Thread-0 当前线程Thread-0 当前线程Thread-0 当前线程Thread-0 当前线程Thread-0 当前线程Thread-0 当前线程Thread-0 当前线程Thread-0 当前线程Thread-0 当前线程Thread-0 Thread-0释放锁 Thread-1获取锁 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 Thread-1释放锁boolean tryLock()方法是尝试获取锁,是在仅在锁空闲状态才获取该锁并立即返回 (true) 否则返回false 如果没有获取空闲的锁则不会再次尝试,我们可以用一个循环轮询获取锁具体案例代码:
package java5; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * trylock的用法 * @author dongtian * @date 2015年6月15日 下午3:49:27 */ public class TryLock { static Lock lock = new ReentrantLock(); private static class Thread1 extends Thread { @Override public void run() { while (!lock.tryLock()) { System.err.println("当前线程 "+ Thread.currentThread().getName() +" 正在尝试获取锁"); } System.err.println("当前线程 "+ Thread.currentThread().getName() +" 获取锁成功!"); try { for (int i = 0; i < 10; i++) { System.err.println("当前线程" +Thread.currentThread().getName()); Thread.currentThread().sleep(100); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); System.err.println(Thread.currentThread().getName() +"释放锁"); } } } public static void main(String[] args) { Thread1 thread1 = new Thread1(); Thread1 thread2 = new Thread1(); thread1.start(); thread2.start(); } }输出结果:
当前线程 Thread-0 正在尝试获取锁 当前线程 Thread-0 获取锁成功! 当前线程 Thread-1 正在尝试获取锁 ... 当前线程 Thread-1 正在尝试获取锁 当前线程 Thread-1 正在尝试获取锁 当前线程 Thread-1 正在尝试获取锁 当前线程 Thread-1 正在尝试获取锁 当前线程 Thread-1 正在尝试获取锁 当前线程 Thread-1 正在尝试获取锁 Thread-0释放锁 当前线程 Thread-1 获取锁成功! 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 Thread-1释放锁boolean tryLock(long time,TimeUnit unit) 如果给定的锁在给定的时间内空闲,并且当前线程没有被中断,则可以获取该锁并立即返回,否则已经超过指定的等待的事件则会返回,此方法不会阻塞。具体方法如下:
package java5; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * trylock的用法 * @author dongtian * @date 2015年6月15日 下午3:49:27 */ public class TryLock { static Lock lock = new ReentrantLock(); private static class Thread1 extends Thread { @Override public void run() { try { while (!lock.tryLock(1000,TimeUnit.SECONDS)) { System.err.println("当前线程 "+ Thread.currentThread().getName() +" 正在尝试获取锁"); } } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } System.err.println("当前线程 "+ Thread.currentThread().getName() +" 获取锁成功!"); try { for (int i = 0; i < 10; i++) { System.err.println("当前线程" +Thread.currentThread().getName()); Thread.currentThread().sleep(100); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); System.err.println(Thread.currentThread().getName() +"释放锁"); } } } public static void main(String[] args) { Thread1 thread1 = new Thread1(); Thread1 thread2 = new Thread1(); thread1.start(); thread2.start(); } }输出结果如下:
当前线程 Thread-0 获取锁成功! 当前线程Thread-0 当前线程Thread-0 当前线程Thread-0 当前线程Thread-0 当前线程Thread-0 当前线程Thread-0 当前线程Thread-0 当前线程Thread-0 当前线程Thread-0 当前线程Thread-0 Thread-0释放锁 当前线程 Thread-1 获取锁成功! 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 当前线程Thread-1 Thread-1释放锁lockInterrupt() 方法,如果当前线程未被中断,则获取锁。如果锁可用,则获取锁,并立即返回;如果锁不可用,出于线程调度目的,将禁用当前线程具体代码如下:
package java5; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * lock的用法 * @author dongtian * @date 2015年6月15日 下午3:49:27 */ public class LockInterruptTest { static Lock lock = new ReentrantLock(); private static class Thread1 extends Thread { @Override public void run() { System.err.println(Thread.currentThread().getName() +"获取锁"); try { lock.lockInterruptibly(); for (int i = 0; i < 10; i++) { System.err.println("当前线程" +Thread.currentThread().getName()); Thread.currentThread().sleep(100); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); System.err.println(Thread.currentThread().getName() +"释放锁"); } } } public static void main(String[] args) { Thread1 thread1 = new Thread1(); Thread1 thread2 = new Thread1(); thread1.start(); thread2.start(); } }二、 ReadWriteLock ReadWriteLock 唯一的一个实现类 ReentrantReadWriteLock 维护了一对相关的锁一个用于只读操作,一个用于写入操作。只要没有writer 读取锁可以由多个reader线程同时保存。写入锁是独占的。在高并发的多线程的情况下虽然异常只有一个线程可以修改共享数据名单上在任何多个线程可以同时读取共享数据,读-写锁利用了这一点。从理论上将,与互斥相比,使用读-写锁允许并发性增强将带来更大的性能提高,在实践中,只有在多个处理器上并且只有访问模式适用于共享数据时候,才能完全实现并发性的增强。
ReadWriteLock接口方法 (1)Lock readLock(); (2) Lock writeLock();
package java5; import java.util.concurrent.locks.ReentrantReadWriteLock; /*** * * @author dongtian * @date 2015年6月15日 下午4:48:56 */ public class CachedData { ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); public void progress() { readWriteLock.readLock().lock(); //读操作 String readData = reader.readLine(); readWriteLock.readLock().unlock(); readWriteLock.writeLock().lock(); write.write(readData); readWriteLock.writeLock().unlock(); } }说明:如果一个线程占有读锁的时候,那么其它申请写锁会一直处于等待状态。 如果一个线程占有写锁的时候,那么其它线程申请读锁或者写锁会一直处于等待状态。