在实际开发中,Object 类的三个用来实现线程同步的方法常常被用到,尤其在解决生产者-消费者问题的时候:wait、notify、notifyAll
package com.techlog.test.service;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* just for test
* Created by techlog on 16/5/20.
*/
@Service
public class PrintSortedThreadName {
private static List threadIds;
private static int index;
private static boolean start;
public static void main(String[] argv) {
threadIds = new ArrayList();
final Object lock = new Object();
int concurrentcount = 10;
start = false;
index = 0;
for (int i = 0; i < concurrentcount; i++) {
new Thread(new Process(lock)).start();
}
synchronized (lock) {
Collections.sort(threadIds);
start = true;
lock.notifyAll();
}
}
private static class Process implements Runnable {
private final Object lock;
public Process(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock) {
threadIds.add(Thread.currentThread().getId());
while (threadIds.get(index) != Thread.currentThread().getId() || !start) {
try {
lock.wait();
} catch (InterruptedException e) {
System.out.println("wait interrupted");
}
}
System.out.println(Thread.currentThread().getId() + " : " + Thread.currentThread().getName());
index++;
lock.notifyAll();
}
}
}
}
上面的代码实现了一个简单的线程同步功能:按照线程 ID 从小到大的顺序输出线程名称
我们通过 main 方法创建了 concurrentcount 指定个数的线程
每一个线程都向 threadIds 写入了自己的 ThreadId,然后,调用 Object 的 wait 方法陷入等待(因为类共享的 start 变量没有被开启)
main 方法在此后将 threadIds 进行了排序,并打开了 start 开关,此后,waiting 状态的所有线程相继被唤醒,打印出了:
11 : Thread-0
12 : Thread-1
13 : Thread-2
14 : Thread-3
15 : Thread-4
16 : Thread-5
17 : Thread-6
18 : Thread-7
19 : Thread-8
20 : Thread-9
锁 lock 实现了两个目的:对共享数据(index、threadIds)的保护和线程同步
上面的代码很直观地展示出了 Object 对象的 wait 和 notifyAll 两个方法的用法
wait 方法让线程释放锁并陷入 waiting 状态,直到被唤醒,如果在 wait 方法中传递了可选的 timeout 参数,则线程会在相应时间后自动被唤醒,而 notifyAll 则唤醒在相应锁上所有处于 waiting 状态的线程
Object 还实现了 notify 方法,每次调用只唤醒一个在相应锁上所有处于 waiting 状态的线程,通常我们使用 notifyAll,如果将上面代码中的 notifyAll 换成 notify,则所有并发线程会陷入死锁