线程池
死锁
线程的状态
定时器
一。线程池
使用线程时,我们会临时创建一个线程,然后启动,而线程的创建,以及线程使用完毕之后的销毁,都是需要消耗性能的.
思考: 能不能事先把线程对象创建好,然后需要用到时直接把创建好的线程拿过来使用,
使用完毕,重新归还,等待下次继续使用???
能! 其实这就是线程池的思想
线程池: 其实就是一个保存多个线程对象的容器,其中的线程可以反复使用,节省了创建和销毁的所消耗性能
使用:
线程池的顶层接口:
java.util.concurrent.Executor
线程池的子接口:
java.util.concurrent.ExecutorService
线程池有一个工具类:其作用是帮助我们创建一个线程池对象
java.util.concurrent.Executors
工具类中静态方法:创建一个线程池对象
public static ExecutorService newFixedThreadPool(int nThreads);
创建一个具有指定线程个数的线程池对象
如何向线程池中提交任务呢??
调用ExecutorService接口中规定的方法:
public Future<?> submit(Runnable task); 向线程池中提交无返回值的任务
public Future<T> submit(Callable<T> task);向线程池中提交有返回值的任务,返回Future类型,
表示返回了封装线程执行完毕之后结果的那个对象
效果:
public class TestThreadPoolDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//1.创建一个线程池,使用多态接收
ExecutorService service = Executors.newFixedThreadPool(3);
//2.向线程池中提交 无返回值 任务
// for (int i = 0; i < 10; i++) {
// service.submit(new Runnable() {
// @Override
// public void run() {
// System.out.println(Thread.currentThread().getName()+"执行了....");//
// }
// });
// }
//3.向线程池中提交 有返回值 任务
//提交
Future<Integer> future = service.submit(new Callable<Integer>(){
@Override
public Integer call() {
//求1-100的和任务
int sum = 0;
for (int i = 1; i < 101; i++) {
sum+=i;
}
return sum;
}
});
//从future中取出结果
Integer result = future.get();// 因为方法具有阻塞功能,会等待任务直接完毕之后再返回结果
System.out.println("result = " + result);
//如果想要整个进程停止
//那么需要关闭线程池
service.shutdown();
}
}
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
Executor框架
什么是Executor框架?
我们知道线程池就是线程的集合,线程池集中管理线程,以实现线程的重用,降低资源消耗,提高响应速度等。线程用于执行异步任务,单个的线程既是工作单元也是执行机制,从JDK1.5开始,为了把工作单元与执行机制分离开,Executor框架诞生了,他是一个用于统一创建与运行的接口。Executor框架实现的就是线程池的功能。
扫描二维码关注公众号,回复: 10351600 查看本文章
Executor框架结构图解
1、Executor框架包括3大部分:
(1)任务。也就是工作单元,包括被执行任务需要实现的接口:Runnable接口或者Callable接口;
(2)任务的执行。也就是把任务分派给多个线程的执行机制,包括Executor接口及继承自Executor接口的ExecutorService接口。
(3)异步计算的结果。包括Future接口及实现了Future接口的FutureTask类。
Executor框架的成员及其关系可以用一下的关系图表示:
2、Executor框架的使用示意图
(1)创建Runnable并重写run()方法或者Callable对象并重写call()方法:
class callableTest implements Callable<String >{
@Override
public String call() {
try{
String a = "return String";
return a;
}
catch(Exception e){
e.printStackTrace();
return "exception";
}
}
}
二。死锁
死锁,在多线程中有多把锁,最后导致所有的锁都在等待,造成的现象称为死锁
产生死锁的条件
至少有两个线程
至少有两个锁对象
必须有synchronized的嵌套
死锁现象:
public class TestDeadLock {
public static void main(String[] args) {
//1.先创建2把锁对象
Object obj1 = new Object();
Object obj2 = new Object();
//2.再创建2个线程
new Thread(new Runnable() {
@Override
public void run() {
//3.必须有synchronized嵌套
while (true){
synchronized (obj1){
System.out.println("线程1抢到了obj1,还需要抢obj2..");
synchronized (obj2){
System.out.println("线程1抢到了obj2,那么可以执行了...");
System.out.println(Thread.currentThread().getName()+"执行了..");
}
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
//4.必须有synchronized嵌套
while (true){
synchronized (obj2){
System.out.println("线程2抢到obj2,还需要抢obj1...");
synchronized (obj1){
System.out.println("线程2抢到obj1,那么可以执行了...");
System.out.println(Thread.currentThread().getName()+"执行了....");
}
}
}
}
}).start();
}
}
注意:如果出现了死锁怎么办??
无解!! 我们只能事先尽量避免死锁
三,线程的状态
线程的六种状态:
新建状态:New
刚刚创建的且未调用start方法的线程
可运行状态 Runnable
处于新建状态的线程调用了start方法之后,注意:只有新建状态的线程才能调用start()
受(锁)阻塞状态Blocked
线程运行的过程中遇到了同步方法,同步代码块,Lock锁,但是锁已经被其他线程持有了
限时等待状态Timed_waiting
线程执行到代码Thread.sleep();线程就处于限时等待状态
无限等待状态Waiting
--线程如何进入waiting无线等待状态
a该线程必须先持有锁对象
b调用锁对象的wait方法,进入无限等待
c进入无限等待之前,会自动释放锁对象
--其他线程如何唤醒waiting状态的线程
a其他线程先持有锁对象,就是进入无限等待线程释放的那个锁对象
b调用锁对象的notify方法,唤醒无限等待的线程
c被唤醒的无限等待线程,先进入锁阻塞,直到再次持有锁对象才能进入可运行状态
消亡状态Terminated
当线程的任务执行完毕,此时线程处于消亡状态
注意:处于消亡状态的线程,不能再调用start方法起死回生
四。等待唤醒机制
等待唤醒机制(Wait和Notify)
public class TestWaitingDemo {
public static void main(String[] args) throws InterruptedException {
//如何让一个线程进入 无限等待
//1.创建一把锁对象
Object obj = new Object();
//2.创建一个线程
new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj){
//1.进入这个代码块,说明线程A抢到锁了
System.out.println("线程A持有锁对象obj...");
//2.进入无限等待
System.out.println("线程A进入了无限等待了...");
try {
obj.wait(); //进入无限等待之前会自动释放锁对象
} catch (InterruptedException e) {
e.printStackTrace();
}
//3.醒来后会继续执行
System.out.println("线程A从无限等待中醒来了...");
}
}
}).start();
Thread.sleep(2000);
//3.再来一个线程,负责唤醒线程A
new Thread(new Runnable() {
@Override
public void run() {
//1.持有相同的锁对象
synchronized (obj){
System.out.println("线程B持有锁对象obj...");
System.out.println("线程B唤醒了线程A....");
//2.调用锁对象的notify方法
obj.notify();
//3.线程执行一下任务
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
}
}).start();
}
}
注意:
a.只有线程进入了无限等待,其他线程调用锁对象.notify()才有作用,否则也可以调用,但是没有任何作用(不会报错)
b.锁对象.notify方法只能唤醒一个线程,具体是哪一个是随机的
c.锁对象.notifyAll方法可以唤醒多个线程,谁抢到锁谁执行
2.生产者和消费者问题:
需求
需求:
生产者消费者案例
需要两个线程: 线程1包子铺线程,负责生产包子,线程2吃货线程,负责吃包子
如果没有包子,那么吃货线程等待,包子铺线程执行(做包子),做完之后唤醒吃货线程
如果有包子,那么包子铺线程等待,吃货线程执行(吃包子),吃饭之后唤醒包子铺线程
public class TestDemo {
public static void main(String[] args) {
//1.创建一个集合
ArrayList<String> arr = new ArrayList<String>();
arr.add("包子");
//2.创建一个锁
Object obj = new Object();
//3.线程1:包子铺线程
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
//a.同步代码块
synchronized (obj){
//b.判断
if (arr.size() > 0) {
//c.有包子,那么无限等待
try {
System.out.println("包子铺发现有包子,进入无限等待...");
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//d.没有包子,做包子
System.out.println("包子铺做了一个包子....");
arr.add("包子");
//e.通知吃货线程
System.out.println("包子铺做完包子,唤醒吃货...");
obj.notify();
}
}
}
});
//4.线程2:吃货线程
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
//a.同步代码块
synchronized (obj){
//b.判断
if (arr.size() == 0) {
try {
System.out.println("吃货发现没有包子,进入无限等待....");
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//c.有包子,吃包子
System.out.println("吃货吃了包子...");
arr.remove(0);
//d.通知包子铺做
System.out.println("吃货吃完包子,唤醒包子铺...");
obj.notify();
}
}
}
});
//5.启动
t1.start();
t2.start();
}
}
五。定时器
可以让某个线程在某个时间做指定的任务,或者某个时间以后指定的时间间隔中反复做某个任务!!
public Timer():构造一个定时器
public void schedule(TimerTask task, long delay); //在指定的时间之后执行指定的任务
public void schedule(TimerTask task, long delay, long period);//在指定的时间之后开始周期性的执行任务,周期的时间间隔是period
public void schedule(TimerTask task, Date time);//在指定的时间点执行指定的任务
public void schedule(TimerTask task, Date firstTime,long period);//在指定的时间点第一次执行任务,继续周期性执行任务,周期的时间间隔period
-------
public class TestTimer {
public static void main(String[] args) {
//1.创建一个定时器
Timer timer = new Timer();
//2.任务1
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("任务1执行...");
}
},2000);
//3.任务2
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("任务2执行...");
}
},2000,1000);
//4.任务3
Calendar cc = Calendar.getInstance();
cc.add(Calendar.SECOND,5);
Date date = cc.getTime();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("任务3执行了...");
}
},date);
//5.任务4
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("任务4执行...");
}
},date,1000);
}
}
==============
public static void main(String[] args) throws Exception {
ExecutorService es = Executors.newFixedThreadPool(3);
Callable<Double> cal = new Callable<Double>() {
@Override
public Double call() throws Exception {
Random ran = new Random();
Double sum = 0.0;
for (int i=0;i<10;i++){
Double rom =ran.nextDouble()*99+1;
sum = sum+rom;
}
return sum/10;
}
};
Double avg1 = es.submit(cal).get();
Double avg2 = es.submit(cal).get();
Double avg3 = es.submit(cal).get();
Double avg = (avg1+avg2+avg3)/3;
System.out.println( String.format("==第一个线程的平均数为:%.2f",avg1));
System.out.println(String.format("==第二个线程的平均数为:%.2f",avg2));
System.out.println(String.format("==第三个线程的平均数为:%.2f",avg3));
System.out.println(String.format("==三个线程的平均数的平均数为:%.2f",avg));
}
参考博客: