三.多线程之间通信
1.为什么有线程之间的通信?
(1)线程在运行时,cpu是随机调度的,往往我们会使用多个线程来完成同一个任务(类似多人协作),并且我们希望多线程完成任务是有规律的,那么在完成任务的过程中,线程之间必然要有一些交流,这样才能使用同一份数据进行操作。
(2)多线程使用同一份数据来完成任务,必然就会造成资源的争夺,所以引入线程的通信可以解决这样一个问题。
2.什么是线程之间的通信
多线程之间的通信就是多个线程共同操作同一个共享变量,一个线程在占用共享变量时,通知其他线程进行等待(wait()方法),使用完毕后,将其他线程唤醒(notify()和notifyall()方法)。这就是线程的等待唤醒机制:wait()、notify()。线程之间通信最典型的例子就是生产者与消费者问题。
3.wait、notify 方法
(1)因为涉及到对象锁,他们必须都放在synchronized中来使用. Wait()、Notify()一定要在synchronized里面进行使用。
(2)Wait()必须暂定当前正在执行的线程,并释放资源锁,让其他线程可以有机会运行
(3)notify/notifyall: 唤醒因锁池中的线程,使之运行
注意:一定要在线程同步中使用,并且是同一个锁的资源
4.演示生产者和消费者
package thread_demo3;
/**
* 多线程之间通讯,生产者消费者
* @author johson
* 1.因为涉及到对象锁,他们必须都放在synchronized中来使用. Wait、Notify一定要在synchronized里面进行使用。
* 2.Wait必须暂定当前正在执行的线程,并释放资源锁,让其他线程可以有机会运行
* 3. notify/notifyall: 唤醒线程,使之运行
*
*/
//生产的对象
class Person{
String name;
String sex;
//flag==false时,只写不度
//flag==true时,只读不写
boolean flag = false;
}
//生产者进程
class writeThread extends Thread{
public Person person;
public writeThread(Person person) {
this.person = person;
}
@Override
public void run() {
int count = 0;
while(true){
synchronized (person) {
//当flag==true的时候,消费者去读取person
if(person.flag){
try {
person.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//当flag==false时,生产person,然后通知消费者去读取
else {
if(count == 0){
person.name = "小红";
person.sex = "女";
}
else {
person.name = "小军";
person.sex = "男";
}
System.out.println("生产者生产"+person.name+","+person.sex);
count = (count+1)%2;
person.flag = true;
person.notify();
}
}
}
}
}
//消费者进程
class readThread extends Thread{
public Person person;
public readThread(Person person) {
this.person = person;
}
@Override
public void run() {
while (true) {
synchronized (person) {
//flag==false时,等待生产者生产person
if(!person.flag){
try {
person.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//flag==true时,消费者读取person,然后将flag变成false,通知生产者去生产person
else {
System.out.println("消费者消费"+person.name+","+person.sex);
person.flag = false;
person.notify();
}
}
}
}
}
public class test1 {
public static void main(String[] args) {
//新建一个person对象
Person person = new Person();
//生产者
writeThread write = new writeThread(person);
//消费者
readThread read = new readThread(person);
write.start();
read.start();
}
}
运行结果如下:
5.wait与sleep区别
(1)对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。
(2)sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
(3)在调用sleep()方法的过程中,线程不会释放对象锁。
(4)调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备,获取对象锁进入运行状态。