所谓线程通信,通俗的来说,就是多个线程之间需要进行信息交换,达到改变线程执行顺序、状态的目的。
1:三种方法
-
wait():一旦执行该方法,当前进程就进入阻塞状态,并释放同步监视器(与sleep不同的一点)。
-
notify():唤醒被wait的线程中优先级最高者。(唤醒一个)
-
notifyAll ():唤醒被wait的所有线程。(唤醒所有)
这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则会报java.lang.IllegalMonitorStateException异常。
这三个方法的调用者必须是同步代码块或同步方法中的同步监视器。
因为这三个方法必须有锁对象调用,而任意对象都可以作为synchronized的同步锁,因此这三个方法只能在Object类中声明。
2:代码实例:
使用两个线程打印 1-100,线程1, 线程2 交替打印
class Number implements Runnable{
private int number = 1;
Object obj = new Object();
@Override
public void run() {
while(true){
synchronized(obj){
obj.notify();//notify()方法唤醒线程,notifyAll()唤醒所有线程
if(number <= 10){
System.out.println(Thread.currentThread().getName()+"打印了:"+number);
number++;
try {
obj.wait();//wait()方法将线程阻塞,同时释放该线程的锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
}
}
}
public class ConmmunicationTest {
public static void main(String[] args) {
Number number = new Number();
Thread t1 = new Thread(number);
Thread t2 = new Thread(number);
t1.start();
t2.start();
}
}
3:生产者消费者问题
class Clerk{
private int num;//产品数量
//生产产品
public synchronized void produceProduct() {
if(num < 20){
num++;
System.out.println(Thread.currentThread().getName() + ":开始生产第" + num + "个产品");
notify();//生产者
}else{
//当产品数量大于20时,生产者阻塞
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费产品
public synchronized void consumeProduct() {
if(num>0){
System.out.println(Thread.currentThread().getName() + ":开始消费第" + num + "个产品");
num--;
notify();//消费者消费完产品后唤醒生产者
}else{
//没有产品时,消费者阻塞
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//生产者类
class Producer extends Thread{
private Clerk clerk;
public Producer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
//生产产品
while(true){
clerk.produceProduct();
}
}
}
class Consumer extends Thread{
private Clerk clerk;
public Consumer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
//消费者消费产品
while(true){
clerk.consumeProduct();
}
}
}
public class ProductTest {
public static void main(String[] args){
Clerk clerk = new Clerk();
Producer p = new Producer(clerk);
Consumer c = new Consumer(clerk);
p.setName("生产者");
c.setName("消费者");
p.start();
c.start();
}
}
4:wait 和 sleep 方法的异同
- 相同点:
(1)一旦执行方法,都可以使得当前的线程进入阻塞状态。- - 不同点:
(1)两个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait()。
(2)调用的要求不同:sleep()可以在任何需要的场景下调用,wait()必须使用在同步代码块或同步方法中。
(3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放同步监视器,而wait()会释放同步监视器。