同步模式--wait/notify、lock、park/unpark

运行顺序:必须先2后1

wait-notify版

// 用来同步的对象
static Object obj = new Object();
// t2 运行标记, 代表 t2 是否执行过
static boolean t2runed = false;
public static void main(String[] args) {
 	Thread t1 = new Thread(() -> {
 		synchronized (obj) {
 			// 如果 t2 没有执行过
 			while (!t2runed) {
 				try {
 					// t1 先等一会
 					obj.wait();
 				} catch (InterruptedException e) {
 					e.printStackTrace();
    	    	}
    	    }
    	}
        System.out.println(1);
    });
    Thread t2 = new Thread(() -> {
 		System.out.println(2);
	 	synchronized (obj) {
 			// 修改运行标记
 			t2runed = true;
 			// 通知 obj 上等待的线程(可能有多个,因此需要用 notifyAll)
 			obj.notifyAll();
 		}
 	});
 	t1.start();
 	t2.start();
}

park/unpark 版本

可以看到,实现上很麻烦:

  • 首先,需要保证先 wait 再 notify,否则 wait 线程永远得不到唤醒。因此使用了『运行标记』来判断该不该 wait
  • 第二,如果有些干扰线程错误地 notify 了 wait 线程,条件不满足时还要重新等待,使用了 while 循环来解决 此问题
  • 最后,唤醒对象上的 wait 线程需要使用 notifyAll,因为『同步对象』上的等待线程可能不止一个

可以使用 LockSupport 类的 park 和 unpark 来简化上面的题目:

Thread t1 = new Thread(() -> {
 	try { Thread.sleep(1000); } catch (InterruptedException e) { }
 		// 当没有『许可』时,当前线程暂停运行;有『许可』时,用掉这个『许可』,当前线程恢复运行
 		LockSupport.park();
 		System.out.println("1");
	});
Thread t2 = new Thread(() -> {
 	System.out.println("2");
 	// 给线程 t1 发放『许可』(多次连续调用 unpark 只会发放一个『许可』)
 	LockSupport.unpark(t1);
});
t1.start();
t2.start();

park 和 unpark 方法比较灵活,他俩谁先调用,谁后调用无所谓。并且是以线程为单位进行『暂停』和『恢复』, 不需要『同步对象』和『运行标记』

交替输出

线程 1 输出 a 5 次,线程 2 输出 b 5 次,线程 3 输出 c 5 次。现在要求输出 abcabcabcabcabc 怎么实现

wait notify版

class SyncWaitNotify {
 	private int flag;
 	private int loopNumber;
 	public SyncWaitNotify(int flag, int loopNumber) {
 		this.flag = flag;
	 	this.loopNumber = loopNumber;
 	}
	public void print(int waitFlag, int nextFlag, String str) {
 		for (int i = 0; i < loopNumber; i++) {
 			synchronized (this) {
 				while (this.flag != waitFlag) {
 					try {
 						this.wait();
 					} catch (InterruptedException e) {
 						e.printStackTrace();
 					}
 				}
 			System.out.print(str);
 			flag = nextFlag;
 			this.notifyAll();
 		}
 		}	
	}
}
SyncWaitNotify syncWaitNotify = new SyncWaitNotify(1, 5);
new Thread(() -> {
 	syncWaitNotify.print(1, 2, "a");
}).start();
new Thread(() -> {
 	syncWaitNotify.print(2, 3, "b");
}).start();
new Thread(() -> {
 	syncWaitNotify.print(3, 1, "c");
}).start();

Lock版本

class AwaitSignal extends ReentrantLock {
 	public void start(Condition first) {
 	this.lock();
	try {
 		log.debug("start");
 		first.signal();
 	} finally {
 		this.unlock();
 	}
 	}
 	public void print(String str, Condition current, Condition next) {
 		for (int i = 0; i < loopNumber; i++) {
 			this.lock();
 			try {
 				current.await();
 				log.debug(str);
 				next.signal();
 			} catch (InterruptedException e) {
 				e.printStackTrace();
 			} finally {
 				this.unlock();
 			}
 		}
 	}
 // 循环次数
 	private int loopNumber;
 	public AwaitSignal(int loopNumber) {
		this.loopNumber = loopNumber;
 	}
}
AwaitSignal as = new AwaitSignal(5);
Condition aWaitSet = as.newCondition();
Condition bWaitSet = as.newCondition();
Condition cWaitSet = as.newCondition();
new Thread(() -> {
 	as.print("a", aWaitSet, bWaitSet);
}).start();
new Thread(() -> {
 	as.print("b", bWaitSet, cWaitSet);
}).start();
new Thread(() -> {
 	as.print("c", cWaitSet, aWaitSet);
}).start();
as.start(aWaitSet);

该实现没有考虑 a,b,c 线程都就绪再开始

Park Unpark版本

class SyncPark {
 	private int loopNumber;
 	private Thread[] threads;
 	public SyncPark(int loopNumber) {
 		this.loopNumber = loopNumber;
 	}
 	public void setThreads(Thread... threads) {
 		this.threads = threads;
 	}
 	public void print(String str) {
 		for (int i = 0; i < loopNumber; i++) {
 			LockSupport.park();
 			System.out.print(str);
 			LockSupport.unpark(nextThread());
 		}
 	}
 	private Thread nextThread() {
 		Thread current = Thread.currentThread();
 		int index = 0;
 		for (int i = 0; i < threads.length; i++) {
 			if(threads[i] == current) {
 			index = i;
 			break;
 			}
 		}
 		if(index < threads.length - 1) {
 			return threads[index+1];
 		} else {
 			return threads[0];
 		}
 	}
 	public void start() {
 		for (Thread thread : threads) {
 			thread.start();
 		}
 		LockSupport.unpark(threads[0]);
 	}
}
SyncPark syncPark = new SyncPark(5);
Thread t1 = new Thread(() -> {
 	syncPark.print("a");
});
Thread t2 = new Thread(() -> {
 	syncPark.print("b");
});
Thread t3 = new Thread(() -> {
 	syncPark.print("c\n");
});
syncPark.setThreads(t1, t2, t3);
syncPark.start();

Categories:

Java   Concurrent