线程通信
1.为什么wait/notify/notifyAll是Object类中的方法,而不是Thread里的方法
线程为了进入临界区(也就是同步块内),需要获得锁并等待锁可用,它们并不知道也不需要知道哪些线程持有锁,它们只需要知道当前资源是否被占用,是否可以获得锁,所以锁的持有状态应该由同步监视器来获取,而不是线程本身。
2.等待通知机制中wait/notify/notifyAll方法的作用
1.使用wait(),notify(),notifyAll()方法之前,要获取同一个对象的锁。
2.调用wait()方法之后,线程会从RUNABLE状态变为WAITING状态,并会释放对象锁,并会将线程移入到对象的等待队列中。
3.notify()和notifyAll()调用之后,等待的线程的wait方法并不会立马返回,需要锁空闲的时候,等待的线程获取了锁,wait()方法才会返回。
4.调用了notify()和notifyAll()方法之后,notify会将一个线程从等待队列放置到同步队列,同步队列是因为锁被占有而处于BLOCKED阶段的线程,notifyAll则是将等待队列中所有的线程都移动到同步队列,这两个方法都是将线程从WAITING状态变为BLOCKED状态。
3.notify/notifyAll的区别?
notify只要唤醒一个线程就会有等待池进入该对象的锁池,会导致死锁。
notifyALL需要唤醒所有线程才能有等待池进入锁池,不会导致死锁。
4.生产者消费者模型
class BlockingQueue<E>{
private final LinkedList<E> queue = new LinkedList<>();
private static int max; //表示阻塞队列存储元素的最大个数
private static final int DEFAULT_MAX_VALUE = 10;
public BlockingQueue(){
this(DEFAULT_MAX_VALUE);
}
public BlockingQueue(int max){
this.max = max;
}
//生产数据
public void put(E value){
synchronized (queue){
//判断当前队列是否有位置存放新生产的数据
if(queue.size() >= max){
System.out.println(Thread.currentThread().getName() +" :: queue is full");
try {
//没有位置,当前生产数据的线程需要阻塞
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+":: the new value " +value+" has been produced");
queue.addLast(value);
queue.notifyAll();//期望唤醒消费者线程
}
}
//消费数据
public E take(){
synchronized (queue){
//判断当前队列是否存在可消费的数据
if(queue.isEmpty()){
System.out.println(Thread.currentThread().getName() +" :: queue is empty");
try {
//不存在,则调用消费数据的线程阻塞
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
E result = queue.removeFirst();
queue.notifyAll();//期望唤醒生产者线程
System.out.println(Thread.currentThread().getName()+":: the value " +result + " has been consumed");
return result;
}
}
}
public class ProducerAndConsumerDemo {
public static void main(String[] args) {
BlockingQueue<Integer> queue = new BlockingQueue<Integer>();
new Thread("Producer"){
@Override
public void run() {
while(true){
queue.put((int)(Math.random() * 1000));
}
}
}.start();
new Thread("Consumer"){
@Override
public void run() {
while(true){
queue.take();
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
5.await方法的使用为什么需要绑定到一个condition对象上
Condition接口的方法
boolean await(long time, TimeUnit unit) throws InterruptedException;
// 当前线程进入等待状态直到被通知、中断或者到某个时间。如果未超时返回true,否则返回false
6.一个ReenteantLock对象可以拥有多个Condition对象,这些对象有什么作用
ReentrantLock功能性方面更全面,比如时间锁等候,可中断锁等候,锁投票等,因此更有扩展性。在多个条件变量和高度竞争锁的地方,用ReentrantLock更合适,ReentrantLock还提供了Condition,对线程的等待和唤醒等操作更加灵活,一个ReentrantLock可以有多个Condition实例,所以更有扩展性。每一个Lock可以有任意数据的Condition对象,Condition是与Lock绑定的,所以就有Lock的公平性特性