java线程开发
一、内容回顾
1、线程的实现方式
2、线程的5种状态,及状态的转换
3、线程安全问题:多个线程操作同一资源
4、synchronized
5、死锁
二、今日内容
1、生产者、消费者设计模式
wait() notifyall()
2、线程池
3、Callable方式实现线程
4、线程安全的集合
1、生产者、消费者设计模式
商店:货架—数组5
面包商:有多个 多线程 要往数组里放面包
顾客:买面包,只要买走了,唤醒面包商
2、线程池
package com.qf.pro2103.day19;
import java.lang.reflect.Executable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建固定数量的线程池
ExecutorService es=Executors.newFixedThreadPool(4);
//创建动态数量的线程池
//ExecutorService es2=Executors.newCachedThreadPool();
//线程池里只有一个线程
//ExecutorService es=Executors.newSingleThreadExecutor();
//创建任务
Runnable runnable=new Runnable(){
private int ticket=50;
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
synchronized (this) {
if(ticket<=0){
break;
}
System.out.println(Thread.currentThread().getName()+"买了"+ticket+"张票");
ticket--;
}
}
}
};
//到线程池种获取线程来执行售票任务
for(int i=0; i<5; i++){
es.submit(runnable);
}
//销毁线程池,等待线程池里的所有线程都执行结束后才销毁
es.shutdown();
}
}
3、Callable实现线程
package com.qf.pro2103.day19;
import java.util.concurrent.Callable;
public class CallableDemo implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName()+"开始计算");
int sum=0;
for(int i=1; i<=100; i++){
sum+=i;
Thread.sleep(100);
}
return sum;
}
}
package com.qf.pro2103.day19;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class TestCallableDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
CallableDemo callable=new CallableDemo();
//把Callable接口的实现类,作为任务传递给Future
FutureTask<Integer> task=new FutureTask<Integer>(callable);
//再创建线程类
Thread t1=new Thread(task);
t1.start();
//接收线程的返回结果
try {
//等待call执行完毕后,才返回结果
int sum=task.get();
System.out.println("计算结果是:"+sum);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
4、Lock锁
读写锁:
互斥规则:写-写 互斥
读-写 互斥
读-读 不互斥
适用场景:读操作较多,写操作较少时,提升了读的效率
package com.qf.pro2103.day19;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
public class ReadWriteLock {
//创建读写锁
private ReentrantReadWriteLock rrl=new ReentrantReadWriteLock();
//从读写锁中获取 读 锁
private ReadLock readLock=rrl.readLock();
//从读写锁中获取 写 锁
private WriteLock writeLock=rrl.writeLock();
private Lock lock=new ReentrantLock();
private String value;
//读
public String getValue() {
//使用读锁即可 如果多个线程同时读取,不互斥
//readLock.lock();
lock.lock();
try{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("读取:"+this.value);
return value;
}finally{
//readLock.unlock();
lock.unlock();
}
}
//写
public void setValue(String value) {
//writeLock.lock();
lock.lock();
try{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("写入:"+this.value);
this.value = value;
}finally{
//writeLock.unlock();
lock.unlock();
}
}
}
package com.qf.pro2103.day19;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestReadWrite {
public static void main(String[] args) {
// TODO Auto-generated method stub
ExecutorService es=Executors.newFixedThreadPool(20);
ReadWriteLock readWriteDemo=new ReadWriteLock();
//任务
Runnable read=new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
readWriteDemo.getValue();
}
};
Runnable write=new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
readWriteDemo.setValue("贾玉堃:"+new Random().nextInt(100));
}
};
//获取开始时间
long start=System.currentTimeMillis();
for(int i=0; i<2; i++){
es.submit(write);
}
for(int i=0;i<18;i++){
es.submit(read);
}
es.shutdown();
while(!es.isTerminated()){
}
long end=System.currentTimeMillis();
System.out.println("用时:"+(end-start));
}
}
5、线程安全的集合
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lG2sLQJo-1629379446337)(pics\image-20210819162407049.png)]
非线程安全的集合如何变为线程安全?
package com.qf.pro2103.day19;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
public class Demo1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<String> list=new ArrayList<String>();
//线程安全的集合---加同步锁
//效率低:读读之间 读写之间 写写之间 都互斥
List<String> list2= Collections.synchronizedList(list);
//线程安全
//读读之间 读写之间 不互斥 只有写写才互斥 效率优于线程安全的ArrayList
CopyOnWriteArrayList<String> list3=new CopyOnWriteArrayList<>();
list.add("hello");
HashSet<String> set=new HashSet<String>();
Set<String> set2=Collections.synchronizedSet(set);
//读读之间 读写之间 不互斥 只有写写才互斥 效率优于线程安全的HashSet
Set<String> set3= new CopyOnWriteArraySet<>();
HashMap<String, String> map=new HashMap<String,String>();
//Map加锁后,等同于HashTable 对整个数组加锁
Map<String, String> map2= Collections.synchronizedMap(map);
//线程安全的效率高的Map:不是对整个数组加锁,而是对数组元素及链表加锁
//分段锁,最优情况下ConcurrentHashMap是线程安全的HashMap效率的16倍
ConcurrentHashMap<String, String> cMap=new ConcurrentHashMap<String,String>();
}
}
6、队列
package com.qf.pro2103.day19;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class Demo2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
//声明队列
//参数:队列里允许存放的值的个数
Queue<String> stuQueue=new ArrayBlockingQueue<String>(3);
Queue<String> stuQueue2=new LinkedBlockingQueue<String>(3);
//添加数据
//add() 如果达到队列的上限,会抛出异常
//offer() 添加元素,达到上限后,返回false
stuQueue.add("jiayukun");
stuQueue.add("dongjiaxin");
stuQueue.add("zhuhongtao");
//先进先出
//poll() 获取队列中的第一个元素,获取后删除
//peek() 获取第一个元素,但是不删除
String getStr=stuQueue.poll();
System.out.println(getStr);
String getStr2=stuQueue.poll();
System.out.println(getStr2);
}
}
总结:
1、阻塞: sleep() join() sync同步锁 wait()
处于wait的线程需要notify()唤醒
【面试题】wait和sleep的区别?
- 1、所属类不同 sleept是Thread类的 wait是Object
- 2、sleep()可以用在任何位置(有、无锁都可以) wait()只能用在锁中
- 3、sleep()在睡眠时,锁依然有效,而wait()会释放锁
2、生产者消费者模式
3、线程池 为什么用? 线程池的创建方式
4、实现Callable接口实现线程
5、Lock
重点: 读写锁
6、线程安全的集合
7、队列
作业:
1、整理技能点
2、做课堂案例
总结:
1、阻塞: sleep() join() sync同步锁 wait()
处于wait的线程需要notify()唤醒
【面试题】wait和sleep的区别?
- 1、所属类不同 sleept是Thread类的 wait是Object
- 2、sleep()可以用在任何位置(有、无锁都可以) wait()只能用在锁中
- 3、sleep()在睡眠时,锁依然有效,而wait()会释放锁
2、生产者消费者模式
3、线程池 为什么用? 线程池的创建方式
4、实现Callable接口实现线程
5、Lock
重点: 读写锁
6、线程安全的集合
7、队列
作业:
1、整理技能点
2、做课堂案例