Java—线程的通信
概念
线程通信概念:线程是操作系统中独立的个体,但这些个体如果不经过特殊处理就不能成为一个整体,线程间的通信就成为整体的必用方式之一。当线程存在通信指挥,系统间的交互性会更强大,在提高CPU利用率的同时还会使开发人员对线程任务在处理过程中进行有效的把控与监督。
引入
问题引入:使用两个线程打印1-100。线程1,线程2交替打印。
代码:
package com.CharlesLC_Test;
public class Thread_Test2 {
public static void main(String[] args) {
Count demo = new Count();
Thread count_num1 = new Thread(demo);
Thread count_num2 = new Thread(demo);
count_num1.setName("线程一");
count_num2.setName("线程二");
count_num1.start();
count_num2.start();
}
}
//定义子类,实现Runnable接口
class Count implements Runnable{
private int num;
public void run() {
while(true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (this) {
if (num <= 100)
System.out.println(Thread.currentThread().getName() + ": " + num++);
else
break;
}
}
}
}
对于不加任何措施的话,输出结果并没有依次输出,这是因为这两个线程都在夺取cpu的执行权,这是一个概率性的:
wait()与notify()和notifyAll()
- wait():令当前线程挂起并放弃CPU、同步资源并等待,使别的线程可访问并修改共享资源,而当前线程排队等候其他线程调用notify()或notifyAll()方法唤醒,唤醒后等待重新获得对监视器的所有权后才能继续执行。
- notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待
- notifyAll():唤醒正在排队等待资源的所有线程结束等待.
注意:这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则会报java.lang.llegalMonitorStateException异常。
因为这三个方法必须有锁对象调用,而任意对象都可以作为synchronized的同步锁,因此这三个方法只能在Object类中声明。
利用wait()和notify()解决
修改Count类,将wait()和notify()加入其中
class Count implements Runnable{
private int num;
public void run() {
while(true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (this) {
notify();
if (num <= 100) {
System.out.println(Thread.currentThread().getName() + ": " + num++);
}
else{break;}
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
结果如下(后面就交替输出了):
解释:这里结果显示线程二先进入循环中,他先遇到synchronized,它拿到了对象锁,那么线程1就进不来,当线程二遇到notify()的时候,因为没有正在被阻塞的线程,所以无需做什么。当线程二输出0的时候,它遇到wait()线程二被阻塞,并且释放对象锁,线程一就可以进来,然后线程一遇到notify(),帮线程二结束等待,然后线程一遇到wait(),线程一被阻塞 ,等待线程二遇到notify()来结束线程一的等待…(之后一直这样进行)
生产者和消费者模式
问题:生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。
- 这里可能出现两个问题:
生产者比消费者快时,消费者会漏掉一些数据没有取到。
消费者比生产者快时,消费者会取相同的数据。
这个例子就存在通信:
代码如下(synchronized+wait-notify模式):
package com.charlesLC_thread;
public class ProduceTest {
public static void main(String[] args) {
Clerk demo = new Clerk();
Customer customer = new Customer(demo);
Producer producer = new Producer(demo);
customer.setName("我是消费者:");
producer.setName("我是生产者:");
producer.start();
customer.start();
}
}
class Clerk{
private int num;
public synchronized void getproduce() {
if (num<20){
notify();
num++;
System.out.println(Thread.currentThread().getName() + "开始生产第"+num+"个产品");
}
else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void costproduce() {
if (num>0){
notify();
System.out.println(Thread.currentThread().getName()+"开始消费第"+num+"个产品");
num--;
}
else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Customer extends Thread{
private Clerk clerk;
public Customer(Clerk clerk ){
this.clerk = clerk;
}
@Override
public void run() {
System.out.println("开始消费产品");
while(true){
clerk.costproduce();
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer extends Thread{
private Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println("开始生产产品");
while (true){
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.getproduce();
}
}
}
结果如下: