多线程:多生产者与多消费者(线程通信)、线程池
多线程
单生产者与单消费者(线程通信)
多生产者与多消费者(线程通信)
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
上一篇: 集合框架--Map