0
点赞
收藏
分享

微信扫一扫

Java多线程.ReentrantLock

腊梅5朵 2022-04-20 阅读 49

功能类比

  1. 实现了雷同synchronized(obj)的类似同步功能,但跟synchronized(obj)不一样,也就是说reentracntlockojb.lock跟synchronized(reentracntlockojb)不互斥。
  2. ReentrantLock lock中的lock锁的对象是自己,相当于:synchronized(lock){  }。
  3. 锁多少次就unlock多少次,否则锁不能释放,或者报IllegalMonitorStateException异常。

lock/unlock

public class TestThreadLock1 implements Runnable{
	private ReentrantLock lock = new ReentrantLock();
	public void run() {
		lock.lock();
		{
			//逻辑代码
		}
		lock.unlock();
         //类似
		/*synchronized (lock) {
			//逻辑代码
		}*/
	}
}

注意:

  1. unlock必须保持跟lock一样的数量,多了就报:
  2. Exception in thread "main" java.lang.IllegalMonitorStateException
    	at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
    	at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
    	at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457)
    
  3. unlock最后在finally中写,原因你懂得。

 isHeldByCurrentThread

查询当前线程是否保持此锁,这样就不会报错了,因为unlock的时候你也不知道线程是否还保持次锁,所以需要判断一下。

ReentrantLock l = new ReentrantLock();
if(l.isHeldByCurrentThread()){
	l.unlock();
}

lockInterruptibly

可以被interrupt的lock

Thread1中run方法内调用lock1.lock();这个方法的时候,如果无法获取lock1的锁,那么久无限等待的。

如果在别的Thread2内有Thread1对象,且想终端Thread1,调用:Thread1.Interrupt()是无法中断的。

如果把lock1.lock()替换为可中断锁lock1.lockInterruptibly(); 那么调用该线程的interrupt方法是可以中断的。

解决死锁代码:

/**
 * 测试死锁中断
 * 1. 无法使用interrupt对synchronized对资源的等待,也没法打断ReentrantLock.lock的等待
 * 	- 可以使用interrupt对ReentrantLock.lockInterruptibly对资源的等待,见TestThreadLock3
 * 2. 可中断锁lockInterruptibly
 * 3. 如果使用了可中断锁,中断后对没有锁的lock就不能unlock,否则保持,需要使用isHeldByCurrentThread判断,线程是否被当前线程给终端
 */
public class TestThreadLock3 implements Runnable{
	
	private static Logger log = Logger.getLogger(TestThreadLock3.class);
	
	private ReentrantLock t1 = null;
	private ReentrantLock t2 = null;
	
	public TestThreadLock3(ReentrantLock t1,ReentrantLock t2){
		this.t2 = t2;
		this.t1 = t1;
	}

	public static void main(String[] args) throws Exception {

		ReentrantLock t1 = new ReentrantLock();
		ReentrantLock t2 = new ReentrantLock();
		
		Thread th1 = new Thread(new TestThreadLock3(t1,t2));
		Thread th2 = new Thread(new TestThreadLock3(t2,t1));
		
		th1.start();
		th2.start();
		
		//需要打断点进行
		Thread.sleep(1000l * 3);
		th1.interrupt();
	
	}

	public void run() {
		try {
			this.t1.lockInterruptibly();
			log.info("get t1 monitor ...");
			Thread.sleep(1000l * 1);

			log.info("get t2 monitor ...");
			t2.lockInterruptibly();
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			if(t1.isHeldByCurrentThread()){
				this.t1.unlock();
			}
			if(t2.isHeldByCurrentThread()){
				this.t2.unlock();
			}
			log.info(Thread.currentThread().getId() + "退出!");
		}
	}
}

tryLock(…)

锁申请等待限时,这个当然就不会产生死锁了。

  1. lock是无限制等待,直到获得锁,比较执着
  2. lockInterruptibly是无限制等,直到获取锁或者自己所在的线程被interrupt
  3. tryLock() 是试着获取锁,获取到锁返回true,否则直接返回false,不会等待
  4. tryLock(3 s) 是试着获取锁,获取到了直接返回true,没获取到等个3s,如果3s后没获取到返回false

示例代码:

/**
 * 锁申请等待限时
 * 1. tryLock() 如果获取到锁则继续执行,如果锁被其他线程持有,则立即返回 false ,也就是不会使当前线程等待,所以不会产生死锁。
 * 2. tryLock(long timeout, TimeUtil unit)指定时长内获取到锁则继续执行,如果等待指定时长后还没有获取到锁则返回false。
 * 3. 当然如果无法获取锁就需要在业务逻辑做出处理
 */
public class TestThreadLock4 implements Runnable{
	
	private static Logger log = Logger.getLogger(TestThreadLock3.class);
	
	private ReentrantLock t1 = null;
	public TestThreadLock4(ReentrantLock t1){
		this.t1 = t1;
	}
	
	public static void main(String[] args) throws Exception {
		ReentrantLock t = new ReentrantLock();
		new Thread(new TestThreadLock4(t)).start();
		Thread.sleep(1000l * 50);
	}

	public void run() {
		try {
			Thread.sleep(500l);
			if(!t1.tryLock()){
				//对没有获取到锁子的处理
				log.info("t1 没有获取到锁");
				//return; //正常逻辑没有获取到所就直接返回,这里测试就注释了
			}
			if(!t1.tryLock(3,TimeUnit.SECONDS)){
				//对没有获取到锁子的处理
				log.info("t1 没有在指定时间内获取到锁");
				return; //正常逻辑没有获取到所就直接返回,这里测试就注释了
			}
			
			//获取到锁的处理
			log.info("get lock success ...");
		} catch (Exception e) {
			log.error("",e);
		} finally{
			if(this.t1.isHeldByCurrentThread()){
				t1.unlock();
			}
		}
	}
}

公平锁

对比非公平锁,如果等待的线程是有先来后到就是公平锁,否则就是不公平。

刚放弃锁的线程,再次获取锁,相当于最后来的。

图示:

 示例代码:

/**
 * 测试公平锁
 * 1. 公平锁就是按照时间先后顺序,使先等待的线程先得到锁,而且,公平锁不会产生饥饿锁,也就是只要排队等待,最终能等待到获取锁的机会。使用重入锁(默认是非公平锁)创建公平锁:
 * 2. 默认是:new ReentrantLock()使用的是NonfairSync,即非公平锁
 *     public ReentrantLock() {
 *     sync = new NonfairSync();
 *     }
 * 3. 公平锁创建:new ReentrantLock(true)
 * public ReentrantLock(boolean fair) {
 * 		sync = fair ? new FairSync() : new NonfairSync();
 * }
 * 4.公平锁日志:可以发现,t1和t2交替获取到锁
 * 2018-07-20 16:39:37,876 [t1] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 1
	2018-07-20 16:39:37,876 [t2] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 1
	2018-07-20 16:39:37,985 [t2] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 2
	2018-07-20 16:39:38,094 [t1] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 2
	2018-07-20 16:39:38,204 [t2] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 3
	2018-07-20 16:39:38,313 [t1] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 3
	2018-07-20 16:39:38,422 [t2] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 4
	2018-07-20 16:39:38,526 [t1] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 4
	2018-07-20 16:39:38,637 [t2] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 5
	2018-07-20 16:39:38,752 [t1] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 5
 *5.非公平锁日志:(顺序没法保证)
 *2018-07-20 16:41:32,689 [t1] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 1
	2018-07-20 16:41:32,689 [t2] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 1
	2018-07-20 16:41:32,689 [t2] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 2
	2018-07-20 16:41:32,689 [t2] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 3
	2018-07-20 16:41:32,689 [t1] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 2
	2018-07-20 16:41:32,689 [t1] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 3
	2018-07-20 16:41:32,689 [t1] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 4
	2018-07-20 16:41:32,689 [t1] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 5
	2018-07-20 16:41:32,689 [t2] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 4
	2018-07-20 16:41:32,689 [t2] INFO  [com.haiwei.lock.TestThreadLock3] -  i = 5
 */
public class TestThreadLock5 implements Runnable{
	
	private static Logger log = Logger.getLogger(TestThreadLock3.class);
	private ReentrantLock t1 = null;
	public TestThreadLock5(ReentrantLock t1){
		this.t1 = t1;
	}
	
	public static void main(String[] args) throws Exception {
		//创建公平锁true;非公平锁false
		ReentrantLock t = new ReentrantLock(true);
		new Thread(new TestThreadLock5(t),"t1").start();
		new Thread(new TestThreadLock5(t),"t2").start();
		Thread.sleep(100000l);
	}

	public void run() {
		try {
			int i = 0 ;
			while(i++ < 5){
				log.info(" i = " + i);
				this.t1.lock();
				t1.unlock();
			}
		} catch (Exception e) {
			log.error("",e);
		} finally{
			if(this.t1.isHeldByCurrentThread()){
				t1.unlock();
			}
		}
	}
}

await/ signal

实现等待和唤醒,还记得生产者消费者模式不。

记住:await(condition接口方法),不是wait(是Object的方法)

/**
 * 测试ReentrantLock的等待和唤醒
 * - 类似synchronized(obj)中的obj.wait和obj.notify,需要锁住后再调用
 * ReentrantLock中操作等待是Conditond对象,方法为:await和signal
 * 1. ReentrantLock 实现了Lock接口,可以通过该接口提供的newCondition()方法创建Condition对象
 * 	Condition condition = lock.newCondition();
 * 2.Condition接口:
 *	public interface Condition {
    void await() throws InterruptedException; // 类似于Object.wait()
    void awaitUninterruptibly(); // 与await()相同,但不会再等待过程中响应中断
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    boolean awaitUntil(Date deadline) throws InterruptedException;
    void signal(); // 类似于Obejct.notify()
    void signalAll();
	}
 * 3. 注意:类似
 * 	等待和唤醒需要同一个Condition方可,因为lock.newCondition()两次就会产生两个Condition对象
 * 4. 记住了,一定是await(condition接口方法),不是wait(是Object的方法)
 * 5. 日志:
 * 	2018-07-20 17:11:05,017 [t1] INFO  [com.haiwei.lock.TestThreadLock3] - get t1 lock ..
 	2018-07-20 17:11:08,022 [main] INFO  [com.haiwei.lock.TestThreadLock3] - signal all ...
 	2018-07-20 17:11:08,022 [t1] INFO  [com.haiwei.lock.TestThreadLock3] - wait end ..
 */
public class TestThreadLock6 implements Runnable{
	
	private static Logger log = Logger.getLogger(TestThreadLock3.class);
	
	private ReentrantLock lock = null;
	private Condition condition = null;
	
	public TestThreadLock6(ReentrantLock t1,Condition c1){
		this.lock = t1;
		this.condition = c1;
	}
	
	public static void main(String[] args) throws Exception {
		ReentrantLock lock = new ReentrantLock(true);
		Condition condition = lock.newCondition();
		
		new Thread(new TestThreadLock6(lock,condition),"t1").start();
		
		Thread.sleep(1000l * 3);
		lock.lock();
		condition.signalAll();
		lock.unlock();
		log.info("signal all ...");
		Thread.sleep(10000l * 5);
	}

	public void run() {
		try {
			log.info("get t1 lock ..");
			this.lock.lock();
			this.condition.await(); 
//记住了,一定是await(condition接口方法),不是wait(是Object的方法)
			log.info("wait end ..");
		} catch (Exception e) {
			log.error("", e);
		} finally{
			if(this.lock.isHeldByCurrentThread()){
				lock.unlock();
			}
		}
	}
}
举报

相关推荐

0 条评论