线程协作线程通信08

线程协作以及通信
生产消费者问题
  • 应用场景:
    • 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中的产品取走消费
    • 如果仓库中没有产品,则生成者将产品放入仓库,否则停止生产,并等待,直到仓库中的产品被消费者取走为止
    • 如果仓库中方有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止
  • 这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间销户依赖,互为条件。
    • 在生产者消费者问题中,仅有synchronized是不够的
    • synchronized可阻止并发,更新同一个共享资源,实现了同步
    • synchronized不能实现不同线程之前的消息传递(通信)
线程通信
方法名 作用
wait() 表示线程一直等待,直到其他线程通知,与sleep不同。会释放锁
wait(long timeout ) 指定等待的毫秒数
notify() 唤醒一个处于等待状态的线程
notifyAll() 唤醒同一个对象上所有调用wait()方法的线程,优先级高的线程优先调度

注意:均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常lllegalMonitorStateException

管程法

并发协作模型"生产者/消费者"->管程法

  • 生产者:负责生产数据的模块(可能是方法,对象,线程,进程);
  • 消费者:负责处理数据的模块(可能是方法,对象,线程,进程);
  • 缓冲区:消费者不能直接使用生产者的数据,他们之间有个"缓冲区"
package com.cnblo.www.Thread.coop;

public class ProcederSale {
	public static void main(String args[]) 
	
	{
		Pools ps=new Pools();
		
		new Proceder(ps,"食堂大妈").start();
		new Custmer(ps,"小明").start();
		new Custmer(ps,"小红").start();
	
	
		
	
	}
}

//厨师,生产者
class Proceder extends Thread {
	Pools p;//容器公用的
	public Proceder(Pools p,String name) {
		super(name);
		this.p=p;
		
	}
public void run() 
{
	for (int j=0;j<5;j++)
	{
		p.processFo(new Food(j));
	}	
 }
}

//消费者
class Custmer extends Thread {
	Pools p;//容器公用的
	public Custmer(Pools p,String name) {
		super(name);
		this.p=p;
		
	}
public void run() 
{
	for (int j=0;j<5;j++)
	{
	   System.out.println(Thread.currentThread().getName()+"消费了---->第"+ p.eatFo().id+"号食品");
	}	
 }

}


class  Food extends  Thread   {
	
	int id;
	Pools p;
	public void run() {
		
	}
	public Food(int id) {
		this.id=id;
	}
	
}


class Pools {
	//装食物的容器
	Food[] panzi=new Food[10];
	//计数器
	int counts=0;
	
	
	//生产方法
	public synchronized void processFo( Food f)
	{
		 //如果满了就等待消费
		if(counts==panzi.length) {
			System.out.println("货存满了等待消费");
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}//否则生产,放入容器,同时计数器加1
		{
			
			panzi[counts]=f;
			System.out.println(Thread.currentThread().getName()+"生产食品编号为+++>"+f.id);
			
			counts++;
	   //通知可以消费
			this.notifyAll();
		
		}
		
	}
	//消费的方法
	public synchronized Food eatFo()
	{
		 ///如果容器为空就等待生产者生产
		
		if(counts==0) 
		{
			System.out.println("没食物了,等待生产");
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
		 }//否则就消费
		
			//消费,计数器减一
		{  
			
			
			counts--;
			Food  fd=panzi[counts]; //把要吃掉的事物保另存返回,一遍获取
			//吃完了唤醒生产;不唤醒就会吃光一直等待
			this.notifyAll();
			return fd;
		}
		
		
	}

}
/*食堂大妈生产食品编号为+++>0
食堂大妈生产食品编号为+++>1
食堂大妈生产食品编号为+++>2
食堂大妈生产食品编号为+++>3
食堂大妈生产食品编号为+++>4
小明消费了---->第2号食品
小明消费了---->第4号食品
小红消费了---->第0号食品
小明消费了---->第3号食品
小红消费了---->第1号食品
没食物了,等待生产
没食物了,等待生产

*/

注意:感觉得保证两个线程的for循环次数一直,不然会最后持续等待;

生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据

信号灯法

并发协作模型"生产者/消费者"->信号灯法

  • 通过一个标志位来判断,让线程等待或者唤醒。
package com.cnblo.www.guancheng;

public class Test03WatchMv {
	public static void main(String args[]) {
		
     Movie m=new Movie();
     Employ e=new Employ(m);
     
     Guster g=new Guster(m);
     e.start();
     g.start();
	}

}
class Employ extends Thread{
	Movie v;
	public Employ(Movie v) {
		this.v=v;
	}
	public void run() {
		for (int i=0;i<10;i=i+1) {
		
			v.createMovide("荒野求生"+i);
		
		}
		
	}
}
class Guster extends Thread{
	Movie v;
	public Guster(Movie v) {
		this.v=v;
	}
	public void run()
	{
		for (int i=0;i<10;i++) {
			v.wathcMovie();
		}
	}
}
class Movie{
	String name;
	boolean flags=true;
	
	public Movie(String name) {
		
		this.name=name;
	}
public Movie() {
		
		
	}
	
	public synchronized void createMovide(String v) {
		if(!flags) 
		{
			System.out.println("等待观众观看"); //等待观众观看
			try {
				this.wait();
			} catch (InterruptedException e) {
			
				e.printStackTrace();
			}
			
		}{ 
			this.name=v;
			System.out.println("创作电影----+1"+this.name); //等待观众观看
			flags=!this.flags; //创造电影后就改变标志位,下一次等待用户观看
			this.notifyAll();
		}
	}
	public synchronized void wathcMovie() {
	 if(flags) 
	 {
		 System.out.println("等待电影开播"); //如果没电影电影就等待电影拍摄创造
		 try {
			this.wait();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		 
	 } {
		 //如果有电影就观看电影
		 System.out.println("用户观看了电影"+this.name);
		 flags=!this.flags;//观看电影后改表标志位等待下一场电影创造		 
		 this.notifyAll();//唤醒电影创造
		 
	  }
		
 }
}/*
等待电影开播
创作电影----+1荒野求生0
等待观众观看
用户观看了电影荒野求生0
等待电影开播
创作电影----+1荒野求生1
用户观看了电影荒野求生1
等待电影开播
创作电影----+1荒野求生2
用户观看了电影荒野求生2
等待电影开播
创作电影----+1荒野求生3
用户观看了电影荒野求生3
等待电影开播
创作电影----+1荒野求生4
用户观看了电影荒野求生4
等待电影开播
创作电影----+1荒野求生5
用户观看了电影荒野求生5
等待电影开播
创作电影----+1荒野求生6
等待观众观看
用户观看了电影荒野求生6
等待电影开播
创作电影----+1荒野求生7
用户观看了电影荒野求生7
等待电影开播
创作电影----+1荒野求生8
用户观看了电影荒野求生8
等待电影开播
创作电影----+1荒野求生9
用户观看了电影荒野求生9*/
使用线程池
  • 背景:经常创建和销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大。

  • 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁,实现重复利用。类似生活中的公共交通工具。

  • 好处:

    • 提高相应速度(减少了创建线程时间)
    • 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
    • 便于线程管理(...)
      • corePoolSize:核心池的大小
      • maximumPoolSize:最大线程数
      • keepAliveTime:线程没有任务时最多保持多长时间后会终止
  • JDK5.0提供了线程池相关API:ExecutorService和Executors

  • ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor

    • void execute(Runnable command):执行任务/命令,没有返回值,一般用来运行Runnable
    • Futuresubmit(/Callabletask):执行任务,有返回值,一般又来执行Callable
    • void shutdown():关闭连接池
  • Executors:工具类,线程池的工厂类,用于创建并返回不同类型的线程池

package com.cnblo.www.guancheng;

import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class ThreadPool {
	
	public static void main(String args[]) {
		
		MyRunnables mr=new MyRunnables();
		ExecutorService eservice=Executors.newFixedThreadPool(4);
		
		eservice.execute(mr);
		eservice.execute(mr);
		eservice.execute(mr);
		eservice.execute(mr);
		eservice.execute(mr);
		eservice.shutdown();

	}
}

class MyRunnables implements Runnable{

	@Override
	public void run() {
				
			System.out.println(Thread.currentThread().getName());

	     }
	}
/*
pool-1-thread-1
pool-1-thread-4
pool-1-thread-3
pool-1-thread-2
pool-1-thread-1

*/
线程的总结
package com.cnblo.www.guancheng;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

//总结实现线程的几种方式
public class Zongjie {
	
	public static void main(String args[]) {
		
		new MyThreadTest01().start();	
		new Thread(new MyThreadTest02()).start();
		ExecutorService et=Executors.newFixedThreadPool(1);//新建一个服务设定池子大小
		Future<Integer> f=et.submit(new MyThreadTest03());
		try {
			System.out.println(f.get());//获取返回值
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		et.shutdown();//关闭服务
		

	}

}

//方式一继承Thread
class MyThreadTest01  extends Thread{
	
	public void run() {
		System.out.println("继承Thread类");
	}
	
}
//方式2实现Runnable 
class MyThreadTest02  implements Runnable{
	
	public void run() {
		System.out.println("实现Runnable接口");
	}
	
}
//方式3实现Callable接口注意,是有返回值的 
class MyThreadTest03  implements Callable<Integer>{
	

	@Override
	public Integer call() throws Exception {
		System.out.println("实现Callable接口");
		return 100;
	}
	
}

posted @ 2025-12-30 10:59  翻滚的小井蛙  阅读(4)  评论(0)    收藏  举报