线程池
线程间通信
概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。
比如:线程A是用来生成包子的,线程B是用来吃包子的,包子可以理解为同一资源,线程A与线程B处理的动作,一个是生产,一个是消费,那么线程A和线程B之间就存在线程通信 的问题。
如何保证线程间通信有效利用资源
多个线程在处理同一个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变量的使用或操作。就是多个线程在操作同一份数据时,避免对同一共享资源的争夺。也就是我们需要通过一定的手段使各个线程之间能够有效的利用资源。而这种手段即-等待唤醒机制。
等待唤醒中的方法
- wait():线程不再活动,不再参与调度,进入wait set中,因此不会浪费CPU资源,也不会去竞争锁了,这时的线程状态即是WAITING。它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象上等待的线程从wait set中释放出来,重新进入到调度队列中(read queue)中
- notify:选取所通知对象的wait set中的一个线程释放
- notifyAll: 释放所通知对象的wait set中的一个线程释放
注意:哪怕只通知了一个等待的线程,被通知的线程也不能立即恢复执行,因为它当初中断的地方是在同步代码块内,而此刻他已经不再持有锁,所以他需要再次去尝试获取锁,成功后才能在当初调用wait方法之后的地方恢复执行。
调用wait方法和notify方法需要注意的细节
- wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程
- wait方法与notify方法是属于Object方法。因为:锁对象可以是任何对象,而任意对象的所属类都是继承了Object类的
- wait方法与notify方法必须要在同步代码块或者同步函数中使用。因为:必须要通过锁对象调用这两个方法。
线程池
线程池思想概述
我们使用一个线程的时候就会去创建一个线程,这样实现起来非常方便,但是会有一个问题:
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁的创建线程和销毁线程需要时间。
线程池:其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作。无需反复创建线程而消耗过多的资源。
线程池的使用
Java线程池的顶级接口是java.util.concurrent.Executor,但严格意义上讲,Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是java.util.concurrent.ExecutorService。
要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在java.util.concurrent.Executors线程工厂类里面提供了一些静态工厂,生成一些常用的线程池。官方建议使用Executors工程类来创建线程池对象。
Executors类中有个创建线程池的方法如下:
public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象(创建的是有界线程池,也就是池中的线程池个数可以指定最大数量)
获取到了一个线程池ExecutorService对象,那么怎么使用呢,在这里定义了一个使用线程池对象的方法如下:
public Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行
Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
使用线程池中线程对象的步骤:
- 创建线程池对象
- 创建Runnable接口子类对象
- 提交Runnable接口子类对象
- 关闭线程池
package com.review;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService service= Executors.newFixedThreadPool(2);
OwnRunnable ownRunnable=new OwnRunnable();
//自己创建线程的方式
//Thread t=new Thread(ownRunnable);
//t.start();
//从线程池中获取线程对象,然后调用OwnRunnable中的run方法
service.submit(ownRunnable);
service.submit(ownRunnable);
service.shutdown();
}
}
class OwnRunnable implements Runnable{
@Override
public void run() {
synchronized (this) {
System.out.println("我要一个教练");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("教练来了:" + Thread.currentThread().getName());
System.out.println("教我游泳,教完后,教练回到了游泳池");
}
}
}