线程优化
原因:
1.一个线程占默认先占个1M的运行内存,而线程执行完是等待系统回收的,此时会浪费内存
2.每次使用就创建,运行完等待系统回收,需要频繁创建与回收
3.代码编写冗余
解决:
使用线程池:线程池拥有对线程的创建,回收或复用的容器
1.线程池是可以设置线程上限的
2.会复用线程
3.不会频繁的创建与回收线程
4.不用关心线程怎么创建,只需考虑每个线程执行的任务
线程池的体系结构
顶级接口:Executor
提供的方法:
void execute(Runnable command);
作用:执行线程任务,相当于启动线程
子类或子接口:
ExecutorService:
ExecutorService//这是个接口,继承于Executor 下面的都是它的一些方法
void shutdown();//关闭线程池
boolean isShutdown();//获取线程池是否关闭,可以看到其返回值为boolean
Future<?> submit(Runnable task);//提交线程任务,相当于执行线程任务
<T> Future<T> submit(Callable<T> task);//也是提交线程任务
boolean isTerminated();//判断线程是否执行结束(这是接口)
ScheduledExecutorService:调度线程池
ScheduledExecutorService//是个接口 继承于 ExecutorService
是所有调度线程池的父接口
提供的方法:
schedule(Runnable command,//command线程任务
long delay, //delay延迟时间
TimeUnit unit);//unit时间单位
作用:延迟执行
schedule(Callable<V> callable,
long delay,
TimeUnit unit);//同上
scheduleAtFixedRate(Runnable command,//command线程任务
long initialDelay,//延迟时间
long period,//间隔时间
**间隔时间=本次任务开始时间-上次任务开始时间
TimeUnit unit);//unit时间单位
作用:延迟重复任务执行,延迟多长时间后执行线程任务,间隔多久再次重复执行线程任务
如果上次执行任务时间大于间隔时间,那么上次任务执行完毕后再执行本次任务
scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,//间隔时间
**间隔时间=本次任务开始时间-上次任务结束时间
TimeUnit unit);
ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
这个东西最大的构造函数 7参
5参,6参,7参的了解一下
corePoolSize:核心线程数量,即线程中最少有几个线程
maximumPoolSize:线程池最大线程数量
keepAliveTime:销毁时间,空闲线程多久没有时间被销毁,不会销毁核心线程
unit:时间单位
workQueue:存放线程任务的队列
threadFactory:线程工厂
handler:复用机制与算法
线程池工具类
作用:提供了多种线程池创建的静态方法
类名:Executors
提供的方法:
1.
static ExecutorService newCachedThreadPool()
作用:创建一个可变线程池
特点:线程池中线程的数量不固定,根据线程任务的多少进行变化
例:
ExecutorService pool=Executors.newCachedThreadPool();
2.
static ExecutorService newFixedThreadPool(int nThreads)
作用:创建一个固定线程池
参数:线程个数
特点:线程池中线程的数量固定,根据参数(int nThreads)进行变化
例:创建有8个线程的线程池
ExecutorService pool=Executors.newFixedThreadPool(8);
3.
static ExecutorService newSingleThreadExecutor()
作用:创建一个单线程的线程池
ExecutorService pool1 = Executors.newSingleThreadExecutor();
4.
static ExecutorService newWorkStealingPool()
作用:创建一个抢占线程池(JDK1.8出现)
特点:线程池中的线程都是守护线程,有一个窃取算法
5.
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
作用:创建一个调度线程池
参数:线程池中的数量
特点:线程任务可以延迟执行
6.
static ScheduledExecutorService newSingleThreadScheduledExecutor()
作用:创建一个单例调度线程池,线程池中的线程只有一个
线程池的使用步骤
1.创建一个线程池
ExecutorService pool=Executors.newFixedThreadPool(3);
2.提交线程任务
使用:
execute(Runnable command)
submit(Runnable task)(建议使用)
还可以使用调度线程池特有的三个方法(
schedule
scheduleAtFixedRate
scheduleWithFixedDelay)
execute:
1. 只能提交Runnable类型任务
2.直接抛出执行任务时的异常,可以try cath 捕获
3.没返回值
submit:
1.可提交Runnable也可提交Callable类型的任务
2.会吃掉异常,可用Future的get方法将异常抛出
3.有返回值
试一试:
ExecutorService pool=Executors.newFixedThreadPool(3);
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("使用一下");
}
});
pool.execute(new Runnable() {
@Override
public void run() {
System.out.println("使用一下2");
}
});
由于没有关闭,可以看到线程一直在运行
3.关闭线程池
线程池对象.shutdown();
pool.shutdown();
一般情况下:等线程池中线程执行完任务关闭后关闭,调度线程池特有的方法除外,调度线程池中执行延迟重复任务时,当线程池关闭时,此任务也会关闭
例:
可以看到上面的例子直接关了
试试:
1个线程打印数字
1个线程打印小写字母
1个线程打印大写字母
package demo1;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo2 {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(3);
pool.submit(new Runnable() {
@Override
public void run() {
Thread name=Thread.currentThread();
for (int i = 1; i <27; i++) {
System.out.println(name.getName()+" "+i);
}
}
});
pool.submit(new Runnable() {
@Override
public void run() {
Thread name=Thread.currentThread();
for (int i = 97; i <123; i++) {
System.out.println(name.getName()+" "+(char)i);
}
}
});
pool.submit(new Runnable() {
@Override
public void run() {
Thread name=Thread.currentThread();
for (int i = 65; i <91; i++) {
System.out.println(name.getName()+" "+(char)i);
}
}
});
pool.shutdown();
}
}
线程任务优化
有时需要线程计算一些东西,这就要求线程要有返回值
JDK提供的Callable接口,该接口是有返回值的线程任务
1.创建Callable对象 这是个创建任务的。 而上一个创任务的是 Runnable
Callable 对象名 = new Callable<返回值类型>(){
@Override
public 返回值的数据类型 call() throws Exception {
return null;
}
}
2.获取Callable对象的计算结果
submit方法有返回值,返回值类型为Future型
Callable的结果可以通过submit返回的Future对象调用get方法
get方法会阻塞当前线程,直到对应的Callable得到结果
Future<返回数据类型> 对象名2= 线程对象.submit(Callable对象);
返回值数据类型 返回值 = 对象名2.get();
不能配合Thread使用,可以在线程池中使用
试一试:
一个线程计算1-50的和,一个线程计算101~150的和,获取两个线程运算结果,并正在主线程打印输出
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 Test2 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Callable<Integer>c1=new Callable<Integer>() {//创建一个Callable任务
@Override
public Integer call() throws Exception {//重写call方法
int sum=0;
for (int i = 0; i <51; i++) {
sum=i+sum;
}
return sum;
}
};
ExecutorService pool = Executors.newFixedThreadPool(2);//创个有2个线程的线程池
Future<Integer> f1 = pool.submit(c1);//用Future接收一下
Future<Integer> f2 = pool.submit(new Callable<Integer>() {//试试用匿名内部类
@Override
public Integer call() throws Exception {
int sum=0;
for (int i = 101; i < 151; i++) {
sum=i+sum;
}
return sum;
}
});
pool.shutdown();
Integer num1 = f1.get();//记得在这抛出异常,抛出方法的异常
Integer num2 = f2.get();// throws InterruptedException, ExecutionException
System.out.println(num1+num2);
}
}
锁优化
主要便于使用
体系结构
Lock(接口)
提供的方法:
void lock();关锁
void unlock();开锁
synchronized做同步
synchronized (锁对象) {
//代码块
}
效果与Lock做同步相同
Condition ReentrantLock();创建锁对象
锁对象.lock();
//同步代码块
锁对象.unlock();
子类:
ReentrantLock(互斥锁)
ReadwriteLock(接口)
Condition:锁对象
提供的方法
void await():休眠
void signal();唤醒一个
void signal();唤醒所有
ReadwriteLock提供的方法
Lock readLock():获取一个读锁
Lock writeLock():获取一个写锁
注意:
读-读:异步
读-写:同步
写-写:同步
优点:提高了读写的效率
子类: ReentrantReadWriteLock(ReadWriteLock实现类)
例:
ReentrantLock Lock = new ReentrantLock();创建锁对象
while(Number>0) {
Lock.lock();
if(Number<=0) {
Lock.unlock();
return;
}
Number--;
System.out.println(Thread.currentThread().getName());
Lock.unlock();
}