调用(notify\notifyAll,wait)等线程控制方法时,必须有两个前提
1:要在被synchronized关键字控制的同步代码块中,调用这些方法
2:调用者为当前的锁对象
wait,notify,notifyAll都是当前线程成为Monitor的Owner了,才可以调用
线程已经获得了锁,然后调用wait放弃(释放)了锁,到Monitor的waitSet中等待,阻塞
notify挑一个唤醒waitSet中的线程,notifyAll唤醒所有
被唤醒的线程(并不是立即继续执行,而是进入到EntryList中,等当前线程释放了锁,EntryList中的线程再进行竞争)
// 当前线程没有获得对象锁,调用wait会抛异常
IllegalMonitorStateException:if the current thread is not the owner of the object's monitor.
// ok
static final Object lock = new Object();
public static void main(String[] args) {
synchronized (lock) { // 先要成为lock锁对象关联的monitor的owner
try { // 才能调用wait,进入monitor的waitSet
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// error
static final Object lock = new Object();
public static void main(String[] args) {
try { // lock锁对象没有关联monitor,线程也没有成为monitor的owner
lock.wait();// IllegalMonitorStateException
} catch (InterruptedException e) {
e.printStackTrace();
}
}
sleep是Thread的静态方法
wait是Object的实例方法,wait需要先获得对象锁,配合synchronized使用
sleep不会释放对象锁,wait会释放对象锁
保护性暂停(产生结果的线程和将来使用结果的线程是11对应的)
public class Main {
public static void main(String[] args) {
// 共用一个guardedObject,产生的线程和使用的线程要11对应
GuardedObject guardedObject = new GuardedObject();
// t1线程会等待t2线程,直到拿到t2线程的结果
new Thread(() -> {
Object result = guardedObject.get();
System.out.println(result);
},"t1").start();
new Thread(() -> {
String data = "cwdf";
guardedObject.complete(data);
},"t2").start();
}
}
// 线程间传递结果
class GuardedObject {
private Object response; // 结果
public Object get() {
synchronized (this) {
while(response == null){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return response;
}
}
public void complete(Object response){
synchronized (this){
this.response = response;
this.notifyAll();
}
}
}
join原理(等待线程的结束)
// t1.join()// 主线程陷入阻塞,等待t1线程运行结束
public final synchronized void join(final long millis) throws InterruptedException {
if (millis > 0) {
if (isAlive()) {
final long startTime = System.nanoTime();
long delay = millis;
do {
wait(delay);
} while (isAlive() && (delay = millis - TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0);
}
} else if (millis == 0) {
while (isAlive()) {// 只要t1线程还活着
wait(0);// 当前线程就睡觉
// (当前线程执行join方法时,会为this对象即t1加锁.锁对象this\t1调用wait,所以当前线程就会睡觉(到Monitor的waitSet中))
}
} else {
throw new IllegalArgumentException("timeout value is negative");
}
}
保护性暂停模式,解耦,将GuardedObject放到信箱中
public class Main {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 3; i++) {
new People().start();// 3个等待收信
}
TimeUnit.SECONDS.sleep(1);
// 每个邮件对应一个邮递员配送(1对1)
for (Integer id : Mailboxes.getIds()) {
new PostMan(id,"内容" + id).start();
}
}
}
// 居民
@Slf4j
class People extends Thread{
@Override
public void run() {
GuardedObject guardedObject = Mailboxes.createGuardedObject();
Object mail = guardedObject.get();
log.info("收到信id:{},内容:{}",guardedObject.getId(),mail);
}
}
// 邮递员
@Slf4j
@AllArgsConstructor
class PostMan extends Thread {
private int id;
private String mail;
@Override
public void run() {
GuardedObject guardedObject = Mailboxes.getGuardedObject(id);
log.info("送信id:{},内容:{}",id,mail);
guardedObject.complete(mail);
}
}
// 信箱(解耦等待和生产)
class Mailboxes {
// 信箱小格子(编号,邮件)
private static Map<Integer,GuardedObject> boxes = new Hashtable<>();
private static int id = 1;
private static synchronized int generateId(){
return id++;
}
public static GuardedObject getGuardedObject(int id){
return boxes.remove(id);
}
public static GuardedObject createGuardedObject(){
GuardedObject guardedObject = new GuardedObject(generateId());
boxes.put(guardedObject.getId(),guardedObject);
return guardedObject;
}
// 所有要送的信的编号
public static Set<Integer> getIds(){
return boxes.keySet();
}
}
// 线程间传递结果
@Data
class GuardedObject {
private Integer id;
private Object response; // 结果
public GuardedObject(int id){
this.id = id;
}
public Object get() {
synchronized (this) {
while(response == null){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return response;
}
}
public void complete(Object response){
synchronized (this){
this.response = response;
this.notifyAll();
}
}
}