线程优化相关
线程优化
原因
1.一个线程大约占1mb的运行内存,而我们的线程在执行完后是等待系统回收,那么会大量浪费运行内存
2.每次使用线程就创建,运行完后就等待系统回收,频繁的创建与回收线程
3.代码编写冗余
解决思路
线程池: 用于对线程进行创建,回收或复用的容器
优点:
1.线程池中的线程是可以设置上限的
2.会复用线程
3.不会频繁的创建与回收线程
4.无需再去关心线程如何创建,只需要考虑每个线程执行的任务
线程池的体系结构
Executor(接口)
提供的方法:
void execute(Runnable command)
作用: 执行线程任务,相当于我们以前启动线程
子类或子接口
ExecutorService(子接口)
提供的方法:
void shutdown();
作用: 关闭线程池
boolean isShutdown();
作用: 获取线程池是否关闭,返回值为true表示关闭
Future<?> submit(Runnable task);
作用: 提交线程任务,相当于执行线程任务
<T> Future<T> submit(Callable<T> task);
作用: 提交线程任务,相当于执行线程任务
子类或子接口
ScheduledExecutorService(ExecutorService的子接口)
概念: 所有调度线程池的父接口
特有方法:
schedule(
Runnable command,
long delay,
TimeUnit unit);
作用: 延迟执行
参数:
command: 线程任务
delay: 延迟时间
unit: 时间单位
scheduleAtFixedRate(
Runnable command,
long initialDelay,
long period,
TimeUnit unit);
作用: 延迟重复执行
延迟多长时间后执行线程任务,间隔多久再次执行线程任务,重复
参数:
command: 线程任务
initialDelay: 延迟时间
period: 间隔时间
本次任务开始时间 - 上次任务开始时间 = 间隔时间
如果上次任务执行时间(任务结束时间 - 任务开始时间)大于间隔时间,那么等上次任务执行完毕后,立即执行本次任务
unit: 时间单位
scheduleWithFixedDelay(
Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
作用: 延迟重复执行
延迟多长时间后执行线程任务,间隔多久再次执行线程任务,重复
参数:
command: 线程任务
initialDelay: 延迟时间
period: 间隔时间
本次任务开始时间 - 上次任务结束时间 = 间隔时间
unit: 时间单位
ThreadPoolExecutor(了解)
构造函数:
ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize: 核心线程数量,即线程中最少有几个线程
maximumPoolSize: 线程池最大线程数量
keepAliveTime: 销毁时间,空闲线程多久没有使用被销毁,不会销毁核心线程
unit: 时间单位
workQueue: 存放线程任务的队列
threadFactory: 线程工厂
handler: 复用机制与算法
ThreadPoolExecutor了解的原因
因为在白皮书(阿里公司内部文档)中提出,建议大家使用ThreadPoolExecutor创建线程池对象,以便于加深对线程池的理解
所以有些公司在面试时会问到ThreadPoolExecutor创建对象时参数的含义
线程池工具类
作用: 提供了多种线程池创建的静态方法
类名: Executors
提供的方法有:
static ExecutorService newCachedThreadPool()
作用: 创建一个可变线程池
特点:
线程池中线程数量不固定,根据线程任务的多少进行变化
static ExecutorService newFixedThreadPool(int nThreads)
作用: 创建一个固定线程池
特点:
线程池中线程数量恒定
参数:
线程池中线程的数量
static ExecutorService newSingleThreadExecutor()
作用: 创建一个单例线程池
特点:
线程池中只有一个线程
static ExecutorService newWorkStealingPool()
作用: 创建一个抢占线程池(JDK1.8出现),了解
特点:
线程池中线程都是守护线程
有一个窃取算法
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
作用: 创建一个调度线程池
参数: 线程池中线程数量
特点: 线程任务可以延迟执行
static ScheduledExecutorService newSingleThreadScheduledExecutor()
作用: 创建一个单例调度线程池
特点: 创建一个调度线程池,该线程池中线程数量为1
线程池使用步骤
1.创建线程池
2.提交线程任务
可以使用:
void execute(Runnable command)
submit(Runnable task)(建议使用)
调度线程池还可以使用三个特有方法
3.关闭线程池
线程池对象.shutdown();
一般情况下: 等线程池中线程任务执行完任务后关闭,调度线程池特有方法除外
调度线程池执行延迟重复任务时,当线程池关闭,其中的延迟重复任务也将被关闭
线程任务优化
案例:
使用两个线程分别计算1~10的和与10的阶乘
然后将两个线程结果相加
JDK提供Callable接口,该接口是有返回值的线程任务
创建Callable对象
Callable 对象名 = new Callable<返回值的数据类型>() {
@Override
public 返回值的数据类型 call() throws Exception {
// TODO Auto-generated method stub
return null;
}
}
获取Callable对象的计算结果
submit方法有返回值,该返回值为Future类型
Callable的结果可以通过submit返回的Future对象调用get方法
注意: get方法会阻塞当前线程,直到对应的线程任务执行完毕获取到结果
注意:
不能配合Thread类使用,可以在线程池中用
锁优化
锁的优点
便于使用
体系结构
Lock(接口)
提供的方法
void lock(): 关锁
void unlock(): 开锁
Condition newCondition(): 创建锁对象
一个Lock可以有多个锁对象
子类:
ReentrantLock(互斥锁)
Condition: 锁对象
提供的方法
void await(): 休眠
void signal(): 唤醒一个
void signalAll(): 唤醒所有
ReadWriteLock(接口)
提供的方法
Lock readLock(): 获取一个读锁
Lock writeLock(): 获取一个写锁
注意:
读-读: 异步
读-写: 同步
写-写: 同步
优点: 提高了读写的效率
子类:
ReentrantReadWriteLock(ReadWriteLock实现类)
作用:
当多个线程对一个内容进行读写时,可以提高执行效率
注意:
synchronized做同步
synchronized (锁对象) {
//要同步的代码
}
...
效果等于上面
Lock做同步
lock对象.lock();
要同步的代码
lock对象.unlock();
练习
1.生成者与消费者模式,要求使用线程池
2.两个线程一个打印字母,一个打印数字,要求使用线程池
打印结果为12a34b56c78d...5152z
3.一个线程计算1~50的和,一个线程计算101~150的和,获取两个线程运算结果,并在主线程打印输出
package test01;
public class Factory {
private int num = 0; //商品数量
private final int MAX_NUM = 100; //最大商品数量
//生产方法
public synchronized void product() {
String name = Thread.currentThread().getName();
if(num < MAX_NUM) {
num++;
System.out.println(name + "生产了1件商品, 当前库存" + num + "件");
this.notifyAll();
}
else {
System.out.println("库存已满, " + name + "停止生产");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//销售方法
public synchronized void sell() {
String name = Thread.currentThread().getName();
if(num > 0) {
num--;
System.out.println(name + "销售了1件商品, 当前库存" + num + "件");
this.notifyAll();
}
else {
System.out.println("库存已空, " + name + "停止销售");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package test01;
import java.util.Random;
public class ProductRunnable implements Runnable{
private Factory factory;
public ProductRunnable(Factory factory) {
this.factory = factory;
}
@Override
public void run() {
Random random = new Random();
while(true) {
int i = random.nextInt(10) + 1;
try {
Thread.sleep(i * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
factory.product();
}
}
}
package test01;
import java.util.Random;
public class SellRunnable implements Runnable{
private Factory factory;
public SellRunnable(Factory factory) {
this.factory = factory;
}
@Override
public void run() {
Random random = new Random();
while(true) {
int i = random.nextInt(10) + 1;
try {
Thread.sleep(i * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
factory.sell();
}
}
}
package test01;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] args) {
Factory factory = new Factory();
ProductRunnable pr = new ProductRunnable(factory);
SellRunnable sr = new SellRunnable(factory);
ExecutorService pool = Executors.newFixedThreadPool(6);
pool.submit(pr);
pool.submit(pr);
pool.submit(pr);
pool.submit(sr);
pool.submit(sr);
pool.submit(sr);
pool.shutdown();
}
}
运行结果
package test02;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] args) {
String lock = "我是锁";
//打印数字线程任务
Runnable runnable01 = new Runnable() {
@Override
public void run() {
for(int i = 1; i < 53; i++) {
System.out.print(i);
if(i % 2 == 0) {
synchronized (lock) {
lock.notify();
if(i != 52) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
};
//打印字母线程任务
Runnable runnable02 = new Runnable() {
@Override
public void run() {
try {
//保证先打印数字
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i = 97; i < 123; i++) {
char c = (char) i;
System.out.print(c);
synchronized (lock) {
lock.notify();
if(i != 122) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
};
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.submit(runnable01);
pool.submit(runnable02);
pool.shutdown();
}
}
运行结果
package test03;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Test {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//计算1~50的和
Callable<Integer> c1 = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for(int i = 1; i <= 50; i++) {
sum += i;
}
return sum;
}
};
//计算101~150的和
Callable<Integer> c2 = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for(int i = 101; i <= 150; i++) {
sum += i;
}
return sum;
}
};
ExecutorService pool = Executors.newFixedThreadPool(2);
Future<Integer> submit1 = pool.submit(c1);
Future<Integer> submit2 = pool.submit(c2);
Integer sum1 = submit1.get();
Integer sum2 = submit2.get();
System.out.println("线程1计算1~50的和为: " + sum1);
System.out.println("线程2计算101~150的和为: " + sum2);
pool.shutdown();
}
}
运行结果