java 多线程的同步与通信,典型应用生产者与消费者模型
当我们使用生产者消费者来进行操作同一资源时
class Resource{
private int count;
private String name;
public void set(String name) {
this.name = name;
System.out.println(Thread.currentThread().getName()+" 生产者 "+name);
}
public void out()
{
System.out.println(Thread.currentThread().getName()+" 生产者 "+name);
}
}
class Producer implements Runnable{
Resource resource;
public Producer(Resource resource) {
this.resource = resource;
}
public void run(){
while(true)
{
resource.set("面包");
}
}
}
class Consumer implements Runnable{
Resource resource;
public Consumer(Resource resoure)
{
this.resource = resource;
}
public void run() {
while(true) {
resource.out();
}
}
}
public class TestDemo {
public static void main(String[] args) {
Resource r = new Resource();
Producer p1 = new Producer(r);
Consumer c1 = new Consumer(r);
//创建线程任务
Thread t1 = new Thread(p1);
Thread t2 = new Thread(c1);
//开启多线程
t1.start();
t2.start();
}
}
该方式会出现以下情况
Thread-0 生产者 面包 20207
Thread-0 生产者 面包 20208
Thread-0 生产者 面包 20209
Thread-0 生产者 面包 20210
Thread-0 生产者 面包 20211
Thread-0 生产者 面包 20212
Thread-0 生产者 面包 20213
Thread-0 生产者 面包 20214
Thread-1 消费者 面包 20207
Thread-1 消费者 面包 20215
Thread-1 消费者 面包 20215
Thread-1 消费者 面包 20215
Thread-1 消费者 面包 20215
Thread-1 消费者 面包 20215
说明不同步,生产运行结果数据错误,已经被生产的商品才被消费到,因为,例如当线程由生产者占用时生产了面包1,切换到消费者线程,消费者线程还没来得及输出就切回生产者线程,生产完面包2 3之后,消费者线程从刚刚断开地方执行,消费面包1,出现线程安全问题,加入同步来解决,使用同步函数
线程安全问题
- 1多个线程在操作共享数据
- 2线程任务操作共享数据的代码有多条(运算有多个)
使用同步代码块或同步函数来进行同步时,会使得当一个线程1执行该同步代码块或者同步函数时,拿到锁,若该线程未将同步代码块或同步函数执行完,就切换到另一线程2,则线程2不会执行,因为没有锁。当线程1执行完会释放锁,线程2 拿到锁,然后执行。
class Resource{
private int count;
private String name;
public synchronized void set(String name) {
this.name = name+" "+count;
count++;
System.out.println(Thread.currentThread().getName()+" 生产者 "+this.name);
}
public synchronized void out()
{
System.out.println(Thread.currentThread().getName()+" 消费者 "+this.name);
}
}
class Producer implements Runnable{
Resource resource;
public Producer(Resource resource) {
this.resource = resource;
}
public void run(){
while(true)
{
resource.set("面包");
}
}
}
class Consumer implements Runnable{
Resource resource;
public Consumer(Resource resource)
{
this.resource = resource;
}
public void run() {
while(true) {
resource.out();
}
}
}
public class TestDemo {
public static void main(String[] args) {
Resource r = new Resource();
Producer p1 = new Producer(r);
Consumer c1 = new Consumer(r);
//创建线程任务
Thread t1 = new Thread(p1);
Thread t2 = new Thread(c1);
//开启多线程
t1.start();
t2.start();
}
}
发现问题已解决,不会再消费到之前很早期的商品,但是还是会出现连续生产却没有被消费,一个商品被消费多次的情况。
Thread-0 生产者 面包 91301
Thread-0 生产者 面包 91302
Thread-0 生产者 面包 91303
Thread-1 消费者 面包 91303
Thread-1 消费者 面包 91303
Thread-1 消费者 面包 91303
Thread-1 消费者 面包 91303
Thread-1 消费者 面包 91303
Thread-1 消费者 面包 91303
线程间的通信
我们的希望是生产者生产一个面包,消费者就消费掉一个面包,也就是说生产者生产了商品后,应该告诉消费者来消费,这是生产者应该处等待状态
消费者消费了商品后,就应该告诉生产者生产
等待唤醒机制
- wait();让线程处于等待状态,其实就让线程临时存储到了线程池中
- notify();会唤醒线程池中任意一个等待的线程
- notifyAll();唤醒线程池中所有的等待线程
- 记住这些方法必须使用再同步中,因为必须要标识wait notify 等方法所属的锁
- 同一个锁上的notify,只能唤醒该锁上的被wait的线程
class Resoure1
{
private boolean flag = false;//false的时候标志为空,true的时候标识为非空
private String name;
private int count;
public synchronized void set(String name)
{
while(flag)
{
try{wait();}catch(Exception e) {}
}
//给成员变量赋值并加上编号
this.name= name+count;
count++;
System.out.println(Thread.currentThread().getName()+"..生产者.."+this.name);
flag = true;
notify();
}
public synchronized void out()
{
while(!flag)
{
try{wait();}catch(Exception e) {}
}
System.out.println(Thread.currentThread().getName()+"..消费者.."+this.name);
flag = false;
notify();
}
}
//2.描述生产者,具备着自己的任务
class Producer1 implements Runnable
{
private Resoure1 r;
public Producer1(Resoure1 r) {
this.r = r;
}
@Override
public void run() {
while(true)
{
r.set("面包");
}
}
}
//3.描述消费者,具备着自己的任务
class Consumer1 implements Runnable
{
private Resoure1 r;
public Consumer1(Resoure1 r) {
this.r = r;
}
@Override
public void run() {
while(true)
{
r.out();
}
}
}
public class TestDemo4 {
public static void main(String[] args) {
Resoure1 resoure = new Resoure1();
//创建线程任务
Producer1 p1 = new Producer1(resoure);
Consumer1 c1 = new Consumer1(resoure);
//创建线程
Thread t1 =new Thread(p1);
Thread t2 =new Thread(c1);
//开启线程
t1.start();
t2.start();