当前位置: 移动技术网 > IT编程>开发语言>Java > 多线程:多生产者与多消费者(线程通信)、线程池

多线程:多生产者与多消费者(线程通信)、线程池

2020年07月31日  | 移动技术网IT编程  | 我要评论
多线程单生产者与单消费者(线程通信)多生产者与多消费者(线程通信)public static void main(String[] args) {//创建资源对象Resource r = new Resource();//创建生产者和消费者对象Product p = new Product(r);Customer c = new Customer(r);//创建多个生产者线程Thread p0 = new Thread(p);Thread p1 = new Thread

多线程

单生产者与单消费者(线程通信)

多生产者与多消费者(线程通信)

public static void main(String[] args) {
	//创建资源对象
	Resource r = new Resource();
	//创建生产者和消费者对象
	Product p = new Product(r);
	Customer c = new Customer(r);
	//创建多个生产者线程
	Thread p0 = new Thread(p);
	Thread p1 = new Thread(p);
	Thread p2 = new Thread(p) ;
	//创建多个消费者线程
	Thread c0 = new Thread(c);
	Thread c1 = new Thread(c);
	Thread c2 = new Thread(c);
	p0. start();
	p1.start();
	p2. start();
	c0. start();
	c1. start();
	c2. start();
}

notify()方法唤醒,一般会唤醒等待时间最长的
我们需要的效果是:生产线程唤醒消费线程,消费线程唤醒生产线程,现在唤醒的有可能是本方线程(NO)
但是,线程是一个独立的方法栈,CPU去里面数据运行线程之间不认识谁是本方,谁是对方
使用Object类的方法notifyAlI()唤醒所有的线程
线程全部唤醒后,依然没有达到需要的结果,因为线程唤醒后会直接就运行了,不会在判断flag标志是什么,线程已经判断过了
线程全部唤醒后,也不能立刻就执行,再次判断flag标志,允许生产再生产,如果不允许生产,继续等待

/*
*资源类
*包子
*成员变量私有修饰,提供方法对外访问
*/
public class Resource {
	//定 义布尔类型的成员,标志位,指示线程该做什么
	//false没有,需要生产,true需 要消费
	private boolean flag = false;
	private int count ;//包子的计数 器
	//提供方法,生产
	public synchronized void product() {
		//判断变量=true,不能生产,等待
		while(flag==true )
		try{this . wait( );}catch( Exception ex) {ex. printStackTrace();}
		count++;
		System . out. println("生产了第"+count+"个");
		//修改标志位
		flag=true;
		//唤醒全部线程
		this.notifyAll();
	}
	//提供方法,消费
	public synchronized void customer() {
		while(flag==false)
		try{this . wait( );}catch( Exception ex) {ex. printStackTrace();}
		System. out . println("消费了第"+count+"个") ;
		//修改标志位
		flag=false;
		//唤醒全部线程
		this.notifyAll();
	}
}

多生产者与多消费者问题分析

  • notifyAl()唤醒的全部的线程资源的浪费
    。能不能只唤醒对方的一个
  • wait(),notify(),notifyAll() 都是本地方法,C++编写方法和操作系统交互
    。通知操作系统,将线程挂起不要运行,通知操作系统,让线程继续运行
    。操作系统找CPU来实现线程的等待和唤醒
    。频繁的和操作系统交互,降低程序的效率

Lock接口

  • Condition newCondition()返回- 一个Condition的接口类型
    。Condition接口取代Object类的监视器方法
Condition接口

作用:线程的阻塞队列,本身是一一个容器存储的是线程.
进入到队列的线程,释放同步锁
一个锁Lock接口对象,可以产生多个线程阻塞队列,让线程释放同步锁后,进入到队列
需要唤醒线程的时候,指定唤醒哪一个队列中的线程
好处:不会全部唤醒,不会和操作系统进行交互,提高效率
Condition接口方法:

  • void await()线程释放锁,并进去到线程的阻塞队列,取代了Object类的方法wait()
  • void singal()线程从阻塞队列出来,获取锁再运行,取代了Object类的方法notify()
public class Resource {
		private boolean flag = false;
		private int count ;//包子的计数 器
		//创建Lock接口的实现类对象,作为锁,去掉synchronized
		private Lock lock=new ReentrantLock();
		//通过这个锁对象,产生出2个存储线程的容器阻塞队列)
		//Lock接口的方法,newCondition()
		Condition proCondition=lock.newCondition();//线程的阻塞队列,生产队列
		Condition cusCondition=lock.newCondition();//线程的阻塞队列,消费队列
		//提供方法,生产
		public  void product() {
			//获取锁
			lock.lock();
			//判断变量=true,不能生产,等待
			while(flag==true ) {
				//释放锁,到阻塞队列中等待
				try {
					proCondition.await();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			count++;
			System . out. println("生产了第"+count+"个");
			//修改标志位
			flag=true;
			//唤醒消费队列中的线程
			cusCondition.signal();
			//释放锁
			lock.unlock();
		}
		//提供方法,消费
		public  void customer() {
			//获取锁
			lock.lock();
			while(flag==false) {
				try {
					cusCondition.await();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			System. out . println("消费了第"+count+"个") ;
			//修改标志位
			flag=false;
			//唤醒生产队列中的线程
			proCondition.signal();
			//释放锁
			lock.unlock();
		}
}
public class Customer implements Runnable {
	Resource r ;
	public Customer ( Resource r) {
	this.r = r;
	}
	public void run( ) {
		while(true){
			r.customer();
		}
	}
}
public class Product implements Runnable{
		//创建资源对象
		private Resource r ;
		public Product( Resource r) {
		this.r = r;
		}
		public void run() {
			while(true){
				r.product();
			}
		}	
}
public static void main(String[] args) throws IOException {
	//创建资源对象
		Resource r = new Resource();
		//创建生产者和消费者对象
		Product p = new Product(r);
		Customer c = new Customer(r);
		//创建多个生产者线程
		Thread p0 = new Thread(p);
		Thread p1 = new Thread(p);
		Thread p2 = new Thread(p) ;
		//创建多个消费者线程
		Thread c0 = new Thread(c);
		Thread c1 = new Thread(c);
		Thread c2 = new Thread(c);
		p0. start();
		p1.start();
		p2. start();
		c0. start();
		c1. start();
		c2. start();
 }

线程池

概念:缓冲池是为了提高效率使用的,线程池也是缓冲池的一种
为什么要出现线程池技术:创建线程是要和操作系统进行交互的线程允许完毕( run0方法结束),频繁的创建线程,大量的浪费操作系统的资源,为了解决资源消耗和提高效率问题,人们设计出了线程池
线程池的思想:创建-一些线程,存储在一个容器中,不要让线程销毁,需要的时候拿一个线程出来, 线程执行完任务的时候,放回到容器中.存储线程的容器,线程池

JDK内置线程池

java. util. concurrent. Executors工厂类,创建对象
创建线程池对象的方法:
static ExecutorService newFixedThreadPool(int n) 创建具有固定个数的线程池,返回的是接口类型,实现类就是创建的线程池对象
java.util.ExecutorService接口中的方法submit(Runnable r),提交一个线程执行的任务

public class MyRunnable implements Runnable{
	public void run() {
	System . out . print1n(Thread.currentThread().getName()+"线程执行任务");
	}
}
public static void main(String[] args) {
	//创建具 有2个线程的,线程池对象
	ExecutorService es = Executors . newFixedThreadPool(2);
	//提交任务
	es. submit( new MyRunnable());
}

java. util. concurrent. Callable接口,视为Runnable接口的扩展
接口的抽象方法:v call()具有返回值,还能抛出异常,此方法作为线程的任务使用,可以获取到方法的返回值,只能用于池中
线程池提交任务方法submit(Callable c)传递接口实现类,提交任务的方法,有返回值
返回的是Futrue的接口类型,接口的实现类,就是线程运行结果的返回值

/*
实现线程池,使用Callable接口
获取到线程执行后的返回值
*/
public static void main(String[] args) throws Exception{
	//创建具 有2个线程的,线程池对象
	ExecutorService es = Executors . newFixedThreadPool(2);
	//提交线程任务submit,接收返回值,接口类型
	Futrue<String> f=es. submit(new  MyCallable());
	//Futrue接口的方法get()拿到线程的返回值
	System.out.println(f.get());
}
public class MyCallable implements Callable<String>{
	public String call() throws Exception{
	return "线程的执行结果";
	}
}

线程池的异步计算

/*
*线程池,实现异步的计算
*
提交2个线程任务
*1个任务计算1+100
*2个任务计算1+200
*获取计算的结果
*/
public static void main(String[] args) throws Exception{
	//创建2个固定个数的线程池
	ExecutorService es = Executors . newFixedThreadPool(2);
	//提交线程任务submit,接收返回值,接口类型
	Futrye<Integer> f=es. submit(new  MyCallable(100));
	//Futrue接口的方法get()拿到线程的返回值
	System.out.println(f.get());
	f=es. submit(new  MyCallable(200));
	System.out.println(f.get());
}
/*
*线程任务:求和计算
*/
public class MyCallable implements Callable<Integer>{
	private int a;
	public MyCallable(int a){
	this.a=a;
	}
	public Integer call() {
		int sum = 0;
		for(int i= 1 ; i<=a; i++) {
		sum=sum+i;
		}
	return sum;
	}
}

ConcurrentHashMap

java. util. concurrent. ConcurrentHashMap 实现Map接口,是键值对的集合
集合特点:

  • 底层哈希表结构
  • 保证键的唯一性,键对象重写hashCode和equals
  • 是线程安全的集合,半安全
    。不更改集合中的元素,不会锁定
    。改变的元素,使用同步锁
    。操作的是哪个数组的索引,锁定当前的数组索引
  • 不会出现并发修改异常
  • 不能存储null值null键
public static void main(string[] args) {
	//
	HashMap<string,string> map = new HashMap<string, string>();
	ConcurrentHashMap<string,string> map = new ConcurrentHashMap<string, string>();
	map. put("a""1");
	map. put("b""2");
	map. put("C""3");
	Set<string> set = map. keySet();
	Iterator<string> it = set. iterator();
	while(it. hasNext()) {
		String key = it. next();
		map. put("d", "4");
		System. out. println(key + map. get(key));
	}
}
public static void main(string[] args)throws Exception {
	//
	Hashmap<Integer, Integer> map = new HashMap<Integer, Integer> ();
	ConcurrentHashmap<Integer, Integer> map = new ConcurrentHashMap<Integer,Integer>();
	//朱合存储2000个元素
	for(int i = 1; i <= 2000; i++) {
		map. put(i,0);
	}
	//线程,删除Map集合中的前500
	new Thread( new Runnab1e() {
		public void run() {
			for(int i=1;i<=500;i++){
				map. remove(i); 
			}
		}
	} ) .start();
	//线程,删除Map集合,501-1000
	new Thread( new Runnab1e() {
		public void run() {
			for(int i=501;i<=1000;i++){
				map. remove(i); 
			}
		}
	} ) . start();
	Thread. sleep(2000);
	system. out. print1n(map. size());
}

原子操作

原子操作,就是不可分割的操作
i++非原子操作.出现线程的安全问题
AtomicInteger
整数的原子类,实现对整数的操作保证线程的安全

public static void main(String[] args) throws InterruptedException {
	//创建原子类对象,内部变虽默认是0
	AtomicInteger atomicInteger = new AtomicInteger();
	//变量自增
	while(true) {
	int i = atomicInteger . getAndIpcrement();
	System. out . print1n(i);
	Thread. sLeep(100);
	}
}

本文地址:https://blog.csdn.net/qq_45018290/article/details/107663334

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网