Lock锁、ReadWriteLock锁

       前面已经介绍了synchronized关键字如何实现线程安全的同步,下面介绍一个更为灵活的Lock接口(ReadWriteLock接口)来实现多线程的线程安全。

 

基础知识

synchronized 与Lock(或ReadWriteLock)的区别

声明:以下几点https://www.cnblogs.com/dolphin0520/p/3923167.html总结的很好,这里直接引用前辈的

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

注:在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时
竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

锁的相关知识介绍

可重入锁

        当某个方法(或代码块儿)已经获得了锁时,如果该方法(或代码块儿)内部犯法(或内部代码块儿)还需要获取该(同
一个)锁,那么无需竞争,默认获得。只要“外面”已经获得了锁,那么进入里面需要同一个锁的方法(或代码块儿)时,可重入。
即:最外层已经获得了锁了,那么只要程序还处在这个大范围内,那么就一直持有着该锁。

可中断锁

        当某个线程正在等待一个正在被其它线程占用的锁时,如果这个正在等待锁的线程可以被响应中断。那么即为可中断锁。

注:Lock为可中断锁,而synchronized为不可中断锁。

公平锁

如果多个线程抢占同一个锁时,总是被等待时间最长(排队最前面)的线程获得,那么就是公平锁。

即:“先到先得”即为公平锁。

非公平锁

如果多个线程抢占同一个锁时,能否抢占到与等待时间无关,那么就是非公平锁。

即:“先到不一定先得”,能不能抢到锁看自己,即为非公平锁。

注:synchronized为非公平锁;Lock的ReentrantLock()实现默认为非公平锁。当然我们可以通过
     ReentrantLock(boolean flag)来指定其属于公平锁还是非公平锁。

读写锁

读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。
即:多个读锁时,可提高效率。

 

Lock接口方法说明

package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;

/**
 * Lock接口  保证线程安全
 *
 * @author JustryDeng
 * @DATE 2018年9月4日 下午1:49:56
 */
public interface Lock {

    /**
     * 获取锁
     * 注:如果当前线程没有获取到锁,那么会一直等待直到获取到锁
     */
    void lock();

    /**
     * 获取锁
     * 注:如果一个线程在等待其他线程释放锁时线程中断,那么这个线程抛出异常
     * 注:相当于等待时长无限的tryLock(long time, TimeUnit unit)方法
     *
     * @throws InterruptedException
     *             线程中断异常
     * @DATE 2018年9月4日 下午1:36:12
     */
    void lockInterruptibly() throws InterruptedException;

    /**
     * 获取锁
     * 注:立即返回结果。如果获取到了立马返回true;如果没有获取到,那么也不作等待,直接返回false
     *
     * @DATE 2018年9月4日 下午1:38:42
     */
    boolean tryLock();

    /**
     * 获取锁
     * 注:如果在time时间内(时间单位为unit)获取到了锁,那么返回true;否则返回false
     * 注:如果在等待获取锁时,当前线程中断,那么抛出异常
     *
     * @throws InterruptedException
     *            线程中断异常
     * @DATE 2018年9月4日 下午1:38:42
     */
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    /**
     * 释放锁
     *
     * @DATE 2018年9月4日 下午1:44:13
     */
    void unlock();

    /**
     * 返回一个(绑定到此 Lock实例的) Condition 实例。 
     * 注:Condition 将 Object 监视器方法(wait、notify 和 notifyAll)
     *    分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,
     *    为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 
     *    synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。 
     *    
     * @DATE 2018年9月4日 下午1:44:37
     */
    Condition newCondition();
}

 

详细代码示例说明

.lock()示例

/**
 * .lock()使用测试
 *
 * @author JustryDeng
 * @DATE 2018年9月4日 上午11:20:17
 */
public class LockTest {

	/**
	 * 多线程测试方法
	 * 注:多个线程同时调用此方法(即:抢占此资源)
	 *
	 * @DATE 2018年9月4日 下午2:18:49
	 */
	static void multiThreadTest(Thread thread) {
		String threadName = thread.getName();
		for (int i = 0; i < 10; i++) {
			System.out.println(threadName + "进入for循环,此次i为:" + i);
		}
        System.out.println(threadName + "执行完毕!");
	}
	
	/**
	 * 程序入口
	 *
	 * @DATE 2018年9月4日 下午2:28:40
	 */
	public static void main(String[] args) {
		
		Lock lock = new ReentrantLock();
		// 匿名内部类,开启第一个线程
		new Thread("线程One") {
			public void run() {
				// 需要获取到lock的锁能进入下面的代码
				lock.lock();
				try {
					// 竞争multiThreadTest()资源
					multiThreadTest(Thread.currentThread());
				} catch (Exception e) {
				}finally {
					lock.unlock();
				}
			};
		}.start();
		// 匿名内部类,开启第二个线程
		new Thread("线程Two") {
			public void run() {
				// 需要获取到lock的锁能进入下面的代码
				lock.lock();
				try {
					// 竞争multiThreadTest()资源
					multiThreadTest(Thread.currentThread());
				} catch (Exception e) {
				}finally {
					lock.unlock();
				}
			};
		}.start();
	}

}

注意事项:
        一个Lock实例只有一个锁,当某一个线程获取到了这个对象的锁后,其他线程要想获取到这个(同一个)对象的锁,就需
要等待(读锁例外,下面会讲到)。当然,如果每个线程需要获取不同的Lock对象的锁,那么也不能保证线程安全。即:
多个线
程必须保证要抢占的是同一个“锁”,才能保证线程安全
。下面也是一样的道理,下面就不再赘述了。

输出结果为:

注:无论执行多少遍main函数,都会发现都是先等其中一个线程执行完,其他线程才开始执行。由此可见:lock实现了线程安全。

 

.tryLock()示例

/**
 * .tryLock()使用测试
 * 注:无论线程是否抢占到了Lock实例的锁,都会立即返回一个结果
 *
 * @author JustryDeng
 * @DATE 2018年9月4日 上午11:20:17
 */
public class TryLockNoParamTest {

	/**
	 * 多线程测试方法
	 * 注:多个线程同时调用此方法(即:抢占此资源)
	 *
	 * @DATE 2018年9月4日 下午2:18:49
	 */
	static void multiThreadTest(Thread thread) {
		String threadName = thread.getName();
		for (int i = 0; i < 10; i++) {
			System.out.println(threadName + "进入for循环,此次i为:" + i);
		}
		System.out.println(threadName + "执行完毕!");
	}
	
	/**
	 * 程序入口
	 *
	 * @DATE 2018年9月4日 下午2:28:40
	 */
	public static void main(String[] args) {
		
		Lock lock = new ReentrantLock();
		// 匿名内部类,开启第一个线程
		new Thread("线程One") {
			public void run() {
				// 需要获取到lock的锁能进入下面的代码
				if(lock.tryLock()) {
					System.out.println(Thread.currentThread().getName() 
							              + "中的.tryLock()抢占到了锁,立即返回true;进入if条件成立逻辑");
					try {
						// 竞争multiThreadTest()资源
						multiThreadTest(Thread.currentThread());
					} catch (Exception e) {
					}finally {
						lock.unlock();
					}
				}else {
					System.out.println(Thread.currentThread().getName() 
							              + "中的.tryLock()没抢占到锁,不作等待,立即返回false");
				}
			};
		}.start();
		// 匿名内部类,开启第二个线程
		new Thread("线程Two") {
			public void run() {
				// 需要获取到lock的锁能进入下面的代码
				if(lock.tryLock()) {
					System.out.println(Thread.currentThread().getName() 
							              + "中的.tryLock()抢占到了锁,立即返回true;进入if条件成立逻辑");
					try {
						// 竞争multiThreadTest()资源
						multiThreadTest(Thread.currentThread());
					} catch (Exception e) {
					}finally {
						lock.unlock();
					}
				}else {
					System.out.println(Thread.currentThread().getName() 
							              + "中的.tryLock()没抢占到锁,不作等待,立即返回false");
				}
			};
		}.start();
	}

}

注意事项:

只有需要Lock锁的程序,才会遵循“排队”执行规则,即:只有对应的需要锁的代码块儿才会在抢占到了锁的前提下才执行,如果线程中压根就没有需要锁的代码,那么是不需要抢占锁才执行的。

注:锁的基本规范、基本概念、基本遵循流程,都与synchronized是一样的

某次main函数输出结果为:

注:上面已经提到,如果没抢占到锁,线程Two就不会进入if(){}成立的逻辑中,就会进入esle{}逻辑中,而else{}中的逻辑属于
     “普通”代码,无需抢占Lock实例的锁、无需考虑线程安全问题,所以线程Two不会等线程One释放了锁才执行,会和线
     程One抢占执行时机。

注:假设线程A、线程B、线程C之间是需要同时抢占同一个Lock实例lock123的锁,线程H、线程I、线程J之间是需要同时
     抢占另一个Lock实例lock456的锁,线程X、线程Y、线程Z是不需要锁的
。那么:线程X线程Y线程Z线程X(或线程
     Y或线程Z)
线程H(或线程I或线程J)最多五个线程同时执行代码。下面也是一样的道理,下面就不再赘述了。

 

.tryLock(long time, TimeUnit unit)之等待获取锁时长测试示例

/**
 * .tryLock(long time, TimeUnit unit)使用测试(注:这里主要测试time)
 * 注:如果线程没有抢占到锁,那么等待time这么久(单位为unit),
 * 注:等待期间,抢占到了返回true;过了时间仍然没有抢占到,那么返回false
 * 注:等待期间,线程中断了,那么throws InterruptedException
 *
 * @author JustryDeng
 * @DATE 2018年9月4日 上午11:20:17
 */
public class TryLockHasParamTestTime {
	
	/**
	 * 多线程测试方法
	 * 注:多个线程同时调用此方法(即:抢占此资源)
	 *
	 * @DATE 2018年9月4日 下午2:18:49
	 */
	static void multiThreadTest(Thread thread) {
		String threadName = thread.getName();
		for (int i = 0; i < 10; i++) {
			System.out.println(threadName + "进入for循环,此次i为:" + i);
		}
		System.out.println(threadName + "执行完毕!");
	}
	
	/**
	 * 程序入口
	 *
	 * @DATE 2018年9月4日 下午2:28:40
	 */
	public static void main(String[] args) {
		
		Lock lock = new ReentrantLock();
		// 匿名内部类,开启第一个线程
		new Thread("线程One") {
			public void run() {
				try {
					// 需要获取到lock的锁才能进入if条件成立对应的代码
					// 设置等待获取Lock实例锁,最多等待100毫秒
					if(lock.tryLock(100, TimeUnit.MILLISECONDS)) {
						System.out.println(Thread.currentThread().getName() 
								              + "中的.tryLock(long time, TimeUnit unit)在指定时"
								                  + "间内抢占到了锁,返回true;进入if条件成立逻辑");
						try {
							// 竞争multiThreadTest()资源
							multiThreadTest(Thread.currentThread());
							// 休眠101毫秒。如果此线程获得了锁,那么另一个线程必定等待超时,返回false
							Thread.sleep(101);
						}finally {
							// 释放锁
							lock.unlock();
						}
					}else {
						System.out.println(Thread.currentThread().getName() 
								              + "中的.tryLock(long time, TimeUnit unit)指定时"
								                  + "间内没抢占到锁,返回false");
					}
				} catch (InterruptedException e) {
					System.out.println(Thread.currentThread().getName() 
				              + "在等待获取锁时,【被中断】了!!!");
				}
			};
		}.start();
		// 匿名内部类,开启第二个线程
		new Thread("线程Two") {
			public void run() {
				try {
					// 需要获取到lock的锁才能进入if条件成立对应的代码
					// 设置等待获取Lock实例锁,最多等待100毫秒
					if(lock.tryLock(100, TimeUnit.MILLISECONDS)) {
						System.out.println(Thread.currentThread().getName() 
								              + "中的.tryLock(long time, TimeUnit unit)在指定时"
								                  + "间内抢占到了锁,返回true;进入if条件成立逻辑");
						try {
							// 竞争multiThreadTest()资源
							multiThreadTest(Thread.currentThread());
							// 休眠50毫秒。如果此线程获得了锁,那么另一个线程必定等待超时,返回false
							Thread.sleep(50);
						}finally {
							// 释放锁
							lock.unlock();
						}
					}else {
						System.out.println(Thread.currentThread().getName() 
								              + "中的.tryLock(long time, TimeUnit unit)指定时"
								                  + "间内没抢占到锁,返回false");
					}
				} catch (InterruptedException e) {
					System.out.println(Thread.currentThread().getName() 
				              + "在等待获取锁时,【被中断】了!!!");
				}
			};
		}.start();
	}

}

多次运行main函数,其中某两次的输出结果为:

某一次:

某一次:

 

 

.tryLock(long time, TimeUnit unit)之等待期间中断进而抛出异常测试示例

/**
 * .tryLock(long time, TimeUnit unit)使用测试(这里主要测试 线程 中断时 抛出异常)
 * 注:如果线程没有抢占到锁,那么等待time这么久(单位为unit),
 * 注:等待期间,抢占到了返回true;过了时间仍然没有抢占到,那么返回false
 * 注:等待期间,线程中断了,那么throws InterruptedException
 *
 * @author JustryDeng
 * @DATE 2018年9月4日 上午11:20:17
 */
public class TryLockHasParamTestException {
	
	/**
	 * 多线程测试方法
	 * 注:多个线程同时调用此方法(即:抢占此资源)
	 *
	 * @DATE 2018年9月4日 下午2:18:49
	 */
	static void multiThreadTest(Thread thread) {
		String threadName = thread.getName();
		for (int i = 0; i < 10; i++) {
			System.out.println(threadName + "进入for循环,此次i为:" + i);
		}
		System.out.println(threadName + "执行完毕!");
	}
	
    /**
     * 为了将代码放在一个类里面展示,所以本人在这里使用了成员内部类
     * 注:实际上单独创建一个继承Thread的类更轻松
     *
     * @DATE 2018年9月4日 下午5:42:28
     */
    class MyThread extends Thread {
    	
    	Lock lock = null;

    	public MyThread(Lock lock, String name) {
    		super(name);
    		this.lock = lock;
		}
    	
		@Override
		public void run() {
			try {
				// 需要获取到lock的锁才能进入if条件成立对应的代码
				// 设置等待获取Lock实例锁,最多等待200毫秒
				if(lock.tryLock(200, TimeUnit.MILLISECONDS)) {
					System.out.println(Thread.currentThread().getName() 
							              + "中的.tryLock(long time, TimeUnit unit)在指定时"
							                  + "间内抢占到了锁,返回true;进入if条件成立逻辑");
					try {
						// 竞争multiThreadTest()资源
						multiThreadTest(Thread.currentThread());
					}finally {
						// 释放锁
						lock.unlock();
					}
				}else {
					System.out.println(Thread.currentThread().getName() 
							              + "中的.tryLock(long time, TimeUnit unit)指定时"
							                  + "间内没抢占到锁,返回false");
				}
			} catch (InterruptedException e) {
				System.out.println(Thread.currentThread().getName() 
			              + "在等待获取锁时,【被中断】了!!!");
			}
		}
	}
	
	/**
	 * 程序入口
	 *
	 * @DATE 2018年9月4日 下午2:28:40
	 */
	public static void main(String[] args) {
		
		final Lock lock = new ReentrantLock();
		// 匿名内部类,开启第一个线程
		new Thread("线程One") {
			public void run() {
				try {
					// 需要获取到lock的锁才能进入if条件成立对应的代码
					// 设置等待获取Lock实例锁,最多等待100毫秒
					if(lock.tryLock(100, TimeUnit.MILLISECONDS)) {
						System.out.println(Thread.currentThread().getName() 
								              + "中的.tryLock(long time, TimeUnit unit)在指定时"
								                  + "间内抢占到了锁,返回true;进入if条件成立逻辑");
						try {
							// 竞争multiThreadTest()资源
							multiThreadTest(Thread.currentThread());
							// 休眠101毫秒。如果此线程获得了锁,那么另一个线程必定等待超时,返回false
							Thread.sleep(101);
						}finally {
							// 释放锁
							lock.unlock();
						}
					}else {
						System.out.println(Thread.currentThread().getName() 
								              + "中的.tryLock(long time, TimeUnit unit)指定时"
								                  + "间内没抢占到锁,返回false");
					}
				} catch (InterruptedException e) {
					System.out.println(Thread.currentThread().getName() 
				              + "在等待获取锁时,【被中断】了!!!");
				}
			};
		}.start();
		
		// 成员内部类,开启第二个线程
		TryLockHasParamTestException.MyThread myThread = 
				                new TryLockHasParamTestException().new MyThread(lock, "线程Two");
		myThread.start();
		
		// 在主线程中计时,过20毫秒,中断myThread线程
		// 注:如果线程One率先抢占到了锁,那么至少会消耗101毫秒(因为我在线程One中sleep了200毫秒)才会释放锁
        //	   那么线程Two至少需要等待101毫秒;这里我们在其等待的第20毫秒左右中断该线程,那么线程Two中的
		//   lock.tryLock(100, TimeUnit.MILLISECONDS)方法会抛出InterruptedException异常,
		//    进而被捕获,输出"线程Two在等待获取锁时,【被中断】了!!!"
		long startTime = System.currentTimeMillis();
		for (;;) {
			if(System.currentTimeMillis() - startTime >= 20) {
				myThread.interrupt();
			     break;
			}
		}
		
	}

}

输出结果为:

注:线程的.interrupt()方法是中断线程,将会设置该线程的中断状态位,即设置为true,中断的结果线程是死亡、还是等待
     新的任务或是继续运行至下一步,就取决于这个程序本身。线程会不时地检测这个中断标示位,以判断线程是否应该被
     中断(中断标示值是否为true)。它并不像stop方法那样会中断一个正在运行的线程。

更多可参考:https://www.cnblogs.com/onlywujun/p/3565082.html

 

.lockInterruptibly()之等待期间中断进而抛出异常测试示例

/**
 * .lockInterruptibly()使用测试
 * 注:如果线程没有抢占到锁,那么进行等待(等待时间无限大)
 * 注:等待期间,线程中断了,那么throws InterruptedException
 * 注:此方法相当于等待时间无限久的.tryLock(long time, TimeUnit unit)方法
 *
 * @author JustryDeng
 * @DATE 2018年9月4日 上午11:20:17
 */
public class LockInterruptiblyTest {
	
	/**
	 * 多线程测试方法
	 * 注:多个线程同时调用此方法(即:抢占此资源)
	 *
	 * @DATE 2018年9月4日 下午2:18:49
	 */
	static void multiThreadTest(Thread thread) {
		String threadName = thread.getName();
		for (int i = 0; i < 10; i++) {
			System.out.println(threadName + "进入for循环,此次i为:" + i);
		}
		System.out.println(threadName + "执行完毕!");
	}
	
    /**
     * 为了将代码放在一个类里面展示,所以本人在这里使用了成员内部类
     * 注:实际上单独创建一个继承Thread的类更轻松
     *
     * @DATE 2018年9月4日 下午5:42:28
     */
    class MyThread extends Thread {
    	
    	Lock lock = null;

    	public MyThread(Lock lock, String name) {
    		super(name);
    		this.lock = lock;
		}
    	
		@Override
		public void run() {
				try {
					// 需要获取到lock的锁才能进入下面的代码
				    lock.lockInterruptibly();
					System.out.println(Thread.currentThread().getName() 
							              + "抢占到了锁!");
					try {
						// 竞争multiThreadTest()资源
						multiThreadTest(Thread.currentThread());
					}finally {
						// 释放锁
						lock.unlock();
					}
			} catch (InterruptedException e) {
				System.out.println(Thread.currentThread().getName() 
			              + "在等待获取锁时,【被中断】了!!!");
			}
		}
	}
	
	/**
	 * 程序入口
	 *
	 * @DATE 2018年9月4日 下午2:28:40
	 */
	public static void main(String[] args) {
		
		final Lock lock = new ReentrantLock();
		// 匿名内部类,开启第一个线程
		new Thread("线程One") {
			public void run() {
				try {
					// 需要获取到lock的锁才能进入下面的代码
				    lock.lockInterruptibly();
					System.out.println(Thread.currentThread().getName() 
							              + "抢占到了锁!");
					try {
						// 竞争multiThreadTest()资源
						multiThreadTest(Thread.currentThread());
						// 休眠200毫秒
						Thread.sleep(200);
					}finally {
						// 释放锁
						lock.unlock();
					}
				} catch (InterruptedException e) {
					System.out.println(Thread.currentThread().getName() 
				              + "在等待获取锁时,【被中断】了!!!");
				}
			};
		}.start();
		
		// 成员内部类,开启第二个线程
		LockInterruptiblyTest.MyThread myThread = 
				                new LockInterruptiblyTest().new MyThread(lock, "线程Two");
		myThread.start();
		
		// 在主线程中计时,过20毫秒,中断myThread线程
		// 注:如果线程One率先抢占到了锁,那么至少会消耗200毫秒(因为我在线程One中sleep了200毫秒)才会释放锁
        //	   那么线程Two至少需要等待200毫秒;这里我们在其等待的第20毫秒左右中断该线程,那么线程Two中的
		//     .lockInterruptibly();方法会抛出InterruptedException异常,
		//    进而被捕获,输出"线程Two在等待获取锁时,【被中断】了!!!"
		long startTime = System.currentTimeMillis();
		for (;;) {
			if(System.currentTimeMillis() - startTime >= 20) {
				myThread.interrupt();
			     break;
			}
		}
		
	}

}

输出结果为:

注:此方法相当于等待时间无限久的.tryLock(long time, TimeUnit unit)方法。不过此方法无返回值,而
     .tryLock(long time, TimeUnit unit)方法方法返回布尔值。

 

Condition简单介绍

/**
 * Condition 将 Object 监视器方法(wait、notify 和 notifyAll)
 *    分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,
 *    为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 
 *    synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。 
 *
 * @DATE 2018年9月4日 下午11:09:06
 */
public interface Condition {

    /**
     * 当前线程进入等待状态,直到被.signal()通知运行  或者  被中断(中断时抛出异常)
     *
     * @throws InterruptedException
     * @DATE 2018年9月4日 下午10:56:05
     */
    void await() throws InterruptedException;

    /**
     * 当前线程进入等待状态,直到被通知,对中断不做响应;
     *
     * @DATE 2018年9月4日 下午10:58:12
     */
    void awaitUninterruptibly();

    /**
     * 在await()的基础上增加了等待时间,返回值表示当前剩余的时间
     * 即:如果在nanosTimeout之前被唤醒,返回值 = nanosTimeout - 实际消耗的时间
     * 注:返回值 <= 0 则表示超时
     *
     * @DATE 2018年9月4日 下午10:59:23
     */
    long awaitNanos(long nanosTimeout) throws InterruptedException;

    /**
     * 在await()基础上增加了 等待时间、时间单位 ;且如果在规定时间内被唤醒,那么返回true,否者返回false
     *
     * @DATE 2018年9月4日 下午11:01:02
     */
    boolean await(long time, TimeUnit unit) throws InterruptedException;

    /**
     * 在await()的基础上增加了一个 “期限”时间;
     * 注:在期限时间到来之前被唤醒返回true;如果到期限时间之前还没有被唤醒,那么返回false
     *
     * @DATE 2018年9月4日 下午11:02:33
     */
    boolean awaitUntil(Date deadline) throws InterruptedException;

    /**
     * 唤醒 处于await等待中的线程
     *
     * @DATE 2018年9月4日 下午11:04:01
     */
    void signal();

    /**
     * 唤醒 处于await等待中的线程
     *
     * @DATE 2018年9月4日 下午11:04:29
     */
    void signalAll();
}

注:被唤醒的线程(不论是Object的notify、notifyAll唤醒的,还是Condition的signal、signalAll唤醒的),直接执行
     等待方法(不论是Object的wait等待方法还是Condition的await等待方法)之后的逻辑,并不会从头开始走。
     追注:Object唤醒Object的等待线程、Condition唤醒Condition的等待线程,别混淆了

 

Condition之简单使用示例

场景说明:

        甲和乙打赌,甲说可以在乙先跑39米的情况下,挥刀仍能伤着乙;乙不信,要比一下速度。于是请来裁判丁。

于是比赛规则(流程)就有了:

        第一步:丁(裁判)倒计时5个数,数到0时,(通知)乙开始跑。

        第二步:乙开始跑,跑到第39米时,(通知)甲可以挥刀了。

        第三步:甲挥刀。

我们接下来就在代码里面进行说明

/*
 * 场景说明:
 * 甲和乙打赌,甲说可以在乙先跑39米的情况下,挥刀仍能伤着乙;乙不信,要比一下速度。于是请来裁判丁。
 *
 * 于是比赛规则(流程)就有了:
 *	 第一步:丁(裁判)倒计时5个数,数到0时,(通知)乙开始跑。
 *	 第二步:乙开始跑,跑到第39米时,(通知)甲可以挥刀了。
 *	 第三步:甲挥刀。
 */
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Condition简单测试
 *
 * @author JustryDeng
 * @DATE 2018年9月4日 下午8:42:14
 */
public class ConditionTest {

	/** 获取Lock实例 */
	Lock lock = new ReentrantLock();
	
	/** 丁(裁判)倒计时结束后,由false变为true; */
	boolean gameStartFlag = false;
	
	/** 乙跑了39米后,由false变为true; */
	boolean swingFlag = false;
	
	/** 对应jia中用到的Condition */
	Condition jiaCondition = lock.newCondition();
	
	/** 对应yi中用到的Condition */
	Condition yiCondition = lock.newCondition();
	
	/**
	 * 对应 --- 甲
	 */
	public void jia() {
		lock.lock();
		try {
			if(!swingFlag) {// 如果乙还没跑够39米,那么继续等乙跑够
				jiaCondition.await();
			}
			if(swingFlag) {
				System.out.println(Thread.currentThread().getName() + ":我挥刀了!!!");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			lock.unlock();
		}
	}
	
	/**
	 * 对应 --- 乙
	 */
	public void yi() {
		lock.lock();
		try {
			if(!gameStartFlag) { // 如果游戏还没开始,那么继续等
				yiCondition.await();
			}
			System.out.println(Thread.currentThread().getName() + ":我开始跑了!!!");
			Thread.sleep(2000);
			System.out.println(Thread.currentThread().getName() + ":我已经跑了39米了!!!");
			// 将 挥刀 标识符 状态改为true;这样 甲就知道可以 挥刀了
			swingFlag = true;
			// 由于要 唤醒  甲; 解铃还须系铃人,用jiaCondition.await()的,这里就用jiaCondition来唤醒
			jiaCondition.signal();
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			lock.unlock();
		}
	}
	
	/**
	 * 对应 --- 丁
	 */
	public void ding() {
		lock.lock();
		try {
			System.out.println(Thread.currentThread().getName() + "(裁判):倒计时开始!!!");
			for (int i = 5; i >= 0; i--) {
				System.out.println(Thread.currentThread().getName() + "(裁判):" + i);
			}
			System.out.println(Thread.currentThread().getName() + "(裁判):比赛开始!!!");
			// 将 开始 标识符 状态改为true;这样乙就知道可以开始跑了
			gameStartFlag = true;
			// 由于要 唤醒  乙; 解铃还须系铃人,用yiCondition.await()的,这里就用yiCondition来唤醒
			yiCondition.signal();
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			lock.unlock();
		}
	}
    /**
     * 入口
     */
    public static void main(String[] args) {
    	ConditionTest ct = new ConditionTest();
    	new Thread("甲") {
    		@Override
    		public void run() {
    			ct.jia();
    		}
    	}.start();
    	
    	new Thread("乙") {
    		@Override
    		public void run() {
    			ct.yi();
    		}
    	}.start();
    	
    	new Thread("丁") {
    		@Override
    		public void run() {
    			ct.ding();
    		}
    	}.start();
    }
}

输出结果为

注:被唤醒的线程(不论是Object的notify、notifyAll唤醒的,还是Condition的signal、signalAll唤醒的),直接执行
     等待方法(不论是Object的wait等待方法还是Condition的await等待方法)之后的逻辑,并不会从头开始走。
     追注:Object唤醒Object的等待线程、Condition唤醒Condition的等待线程,别混淆了

 

ReadWriteLock读写锁

注:Lock与ReadWriteLock都是锁接口,不过ReadWriteLock将整个锁再细分成了读锁、写锁。

       我们知道:一个资源,如果只是读的话,是对该资源本身没什么影响的。读锁就是基于此考虑,被设计出来的。

 

        假设当前线程竞争到了某个锁实例的读锁,另一个想竞争这个锁对象的读锁时,需要等待(即,如果当下是一个写锁线程获得
了锁时,有再多的读锁也得等着。)
如:

/**
 * 读写锁 ---> 已经获得锁的线程 与 其它要获得同一个读锁的线程,可以同时进行(无需等待)
 *
 * @author JustryDeng
 * @DATE 2018年9月4日 下午7:36:59
 */
public class ReadWriteLockTestReadAndRead {
	

	public static void main(String[] args) {

		// ReentrantReadWriteLock是ReadWriteLock接口的主要实现之一;不仅提供了基本的实现,还额外丰富了几种方法
		ReentrantReadWriteLock rrwl = new ReentrantReadWriteLock();
		// 匿名内部类 开启第一个线程
		new Thread("线程One") {
			public void run() {
				rrwl.readLock().lock();
				try {
					for (int i = 0; i < 100; i++) {	
						System.out.println(Thread.currentThread().getName() + "第" + i+ "次读");
					}
				} finally {
					rrwl.readLock().unlock();
				}
			};
		}.start();

		// 匿名内部类 开启第二个线程
		new Thread("线程Two") {
			public void run() {
				rrwl.readLock().lock();
				try {
					for (int i = 0; i < 100; i++) {	
						System.out.println(Thread.currentThread().getName() + "第" + i+ "次读");
					}
				} finally {
					rrwl.readLock().unlock();
				}
			};
		}.start();

	}

}

输出结果为:

假设当前线程竞争到了某个锁实例的写锁,另一个想竞争这个锁对象的写锁时,需要等待,如:

输出结果为:

假设当前线程竞争到了某个锁实例的读锁(写锁),另一个想竞争这个锁对象的写锁(读锁)时,需要等待,如:

输出结果为:

微笑参考链接
       
 https://www.cnblogs.com/dolphin0520/p/3923167.html
        https://www.jianshu.com/p/be2dc7c878dc
        https://blog.csdn.net/leexichang/article/details/79328738

微笑如有不当之处,欢迎指正

微笑本次示例测试代码项目托管链接
       
 https://github.com/JustryDeng/PublicRepository

微笑本文已经被收录进《程序员成长笔记(三)》,笔者JustryDeng

猜你喜欢

转载自blog.csdn.net/justry_deng/article/details/82392953