一、wait方法
(1)方法wait()的作用是使当前执行代码的线程进行等待,该方法会将该线程放入”预执行队列“中,并且在wait()所在的代码处停止执行,直到接到通知或被中断为止。
(2)在调用wait()之前,线程必须获得该对象级别锁,这是一个很重要的地方,很多时候我们可能会忘记这一点,即只能在同步方法或同步块中调用wait()方法。
(3)还需要注意的是wait()是释放锁的,即在执行到wait()方法之后,当前线程会释放锁,当从wait()方法返回前,线程与其他线程竞争重新获得锁。
二、notify方法
(1)和wait()方法一样,notify()方法也要在同步块或同步方法中调用,即在调用前,线程也必须获得该对象的对象级别锁。
(2)该方法是用来通知那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则由线程规划器随机挑选出其中一个呈wait状态的线程,对其发出通知notify,并使它等待获取该对象的对象锁。
(3)这里需要注意的是,执行notify方法之后,当前线程不会立即释放其拥有的该对象锁,而是执行完之后才会释放该对象锁,被通知的线程也不会立即获得对象锁,而是等待notify方法执行完之后,释放了该对象锁,才可以获得该对象锁。
(4)notifyAll()通知所有等待同一共享资源的全部线程从等待状态退出,进入可运行状态,重新竞争获得对象锁。
三、wait()/notify()方法总结
(1)wait()/notify()要集合synchronized关键字一起使用,因为他们都需要首先获取该对象的对象锁;
(2)wait方法是释放锁,notify方法是不释放锁的;
四、注意事项
(1)wait()和notify()方法要在同步块或同步方法中调用,即在调用前,线程也必须获得该对象的对象级别锁。
(2)wait方法是释放锁,notify方法是不释放锁的;
(3)notify每次唤醒wait等待状态的线程都是随机的,且每次只唤醒一个;
(4)notifAll每次唤醒wait等待状态的线程使之重新竞争获取对象锁,优先级最高的那个线程会最先执行;
(5)当线程处于wait()状态时,调用线程对象的interrupt()方法会出现InterruptedException异常;
五、线程间的通信方式
(1)锁机制
1.1 互斥锁:提供了以排它方式阻止数据结构被并发修改的方法。
1.2 读写锁:允许多个线程同时读共享数据,而对写操作互斥。
1.3 条件变量:可以以原子的方式阻塞进程,直到某个特定条件为真为止。
对条件测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
(2)信号量机制:包括无名线程信号量与有名线程信号量
(3)信号机制:类似于进程间的信号处理。
线程间通信的主要目的是用于线程同步,所以线程没有象进程通信中用于数据交换的通信机制。
demo:
package thread.demo04.test2;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicInteger;
public class MyQueue {
// 1、需要一个承装元素的集合
private final LinkedList<Object> list = new LinkedList<>();
// 2、需要一个计数器
private final AtomicInteger count = new AtomicInteger(0);
// 3、需要指定上限和下限
private final int maxSize = 5;
private final int minSize = 0;
// 5、初始化锁对象
private final Object lock = new Object();
/**
* put方法
*/
public void put(Object obj) {
synchronized (lock) {
// 达到最大无法添加,进入等到
while (count.get() == maxSize) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add(obj); // 加入元素
count.getAndIncrement(); // 计数器增加
System.out.println(" 元素 " + obj + " 被添加 ");
lock.notify(); // 通知另外一个阻塞的线程方法
}
}
/**
* get方法
*/
public Object get() {
Object temp;
synchronized (lock) {
// 达到最小,没有元素无法消费,进入等到
while (count.get() == minSize) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count.getAndDecrement();
temp = list.removeFirst();
System.out.println(" 元素 " + temp + " 被消费 ");
lock.notify();
}
return temp;
}
public int size() {
return count.get();
}
}
package thread.demo04.test2;
public class Test {
public static void main(String[] args) throws Exception {
final MyQueue myQueue = new MyQueue();
initMyQueue(myQueue);
Thread t1 = new Thread(() -> {
myQueue.put("f");
myQueue.put("g");
myQueue.put("h");
myQueue.put("i");
}, "t1");
Thread t2 = new Thread(() -> {
try {
Thread.sleep(1000);
myQueue.get();
Thread.sleep(1000);
myQueue.get();
Thread.sleep(1000);
myQueue.get();
Thread.sleep(1000);
myQueue.get();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t2");
t1.start();
Thread.sleep(1000);
t2.start();
}
private static void initMyQueue(MyQueue myQueue) {
myQueue.put("a");
myQueue.put("b");
myQueue.put("c");
myQueue.put("d");
myQueue.put("e");
System.out.println("当前元素个数:" + myQueue.size());
}
}