欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-04-12 22:09:55
多线程单生产者与单消费者(线程通信)多生产者与多消费者(线程通信)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