线程
进程与线程
- 进程是系统资源分配的基本单位,线程是处理机调度的基本单位。
- 一个进程至少包含一个线程,比如java中运行main方法的主线程。
- 多线程是在一个程序中同时运行多个子任务。
- 多线程可以更好地利于系统资源,提高处理机利用率。
线程的五种状态
- 新建状态(New):线程对象创建后,即进入新建状态;
- 就绪状态(Runnable):当调用线程对象的start()方法,线程进入就绪状态。处于就绪状态的线程,只是说明该线程已经做好了准备,随时等待CPU调度执行,并不是说执行了start()该线程就会立即执行;
- 运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才开始执行,即进入运行状态。注意,就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
- 阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态。直到其重新进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。
根据阻塞产生的原因不同,阻塞状态又可以分为3种:
- 等待阻塞:运行状态中的线程执行Object.wait()方法
- 同步阻塞:线程在获取synchronized内部锁失败(因为锁被其它线程所占用)
- 其它阻塞:通过调用线程的Thread.sleep()、join()方法或发出I/O请求时,线程会进入到阻塞状态。当sleep()状态超时(sleep休眠结束)、join()等待线程终止或超时、或者I/O处理完毕时,线程重新转入就绪状态
- 死亡状态(Dead):线程执行完毕或者因异常退出了run()方法,该线程结束生命周期,线程销毁。
线程相关方法
- Object提供的方法:
-
wait()
-
notify()
-
notifyAll()
- Thread提供的方法:
-
start()
-
join()
-
yield()
让出处理机 -
sleep()
传入参数:指定睡眠时间(ms、[ns])
使正在执行的线程让出处理机,进入等待状态睡眠一段时间,但是不会释放同步资源锁。
睡眠结束后线程会进入就绪状态。
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(){
@Override
public void run() {
System.out.println("t1");;
}
};
t1.sleep(100);
}
创建线程的方式
- 继承Thread类
- 实现Runnable接口
- 构造FutureTask
1)自定义一个类实现Callable接口并实现其中的call()方法来创建线程任务(Callable的call方法类似于Runnable的run方法)
2)接着把这个线程任务以参数的形式传递给FutureTask,并new出一个FutuerTask对象
3)最后将这个FutureTask对象继续以参数的形式传递给Thread - 通过线程池创建
线程池
- 推荐直接使用ThreadPoolExecutor,不使用Executors
ThreadPoolExecutor
构造
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
{...}
- corePoolSize:核心线程数,线程池中至少有 corePoolSize 个线程可用或在运行。
- maximumPoolSize:最大线程数,当等待队列装满的时候,线程池将线程数目扩充到 maximumPoolSize 个。
- keepAliveTime:存活时间,核心线程外的线程,空闲时间超过 keepAliveTime 后才会被销毁。
- unit:keepAliveTime的时间单位。
- workQueue:等待队列。
- threadFactory:线程工厂,用于创建新线程。
- handler:拒绝策略,线程池满后,对新任务的应对策略。
execute() 与 submit()
- execute():单纯的提交,无返回值,不能确定任务执行成功与否。
public class Main {
private static final String templateS = "Thread %s starts.";
private static final String templateE = "Thread %s starts.";
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5,
10,
3,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(30),
new ThreadPoolExecutor.CallerRunsPolicy()
);
for(int i = 0; i < 10; i++){
executor.execute(()->{
String name = Thread.currentThread().getName();
System.out.println(String.format("Thread: %s, start: %s", name, new Date()));
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(String.format("Thread: %s, end : %s", name, new Date()));
});
}
executor.shutdown();
while (!executor.isTerminated()){
}
System.out.println("finished");
}
}
- submit():返回一个Future对象,可通过该对象获取返回值以及判断任务执行成功与否。
CompletableFuture
import java.util.concurrent.*;
import java.util.function.Consumer;
public class MyCompletableFuture {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// ExecutorService threadPool = Executors.newFixedThreadPool(3);
ExecutorService threadPool = new ThreadPoolExecutor(
5,
10,
3,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10),
new ThreadPoolExecutor.CallerRunsPolicy()
);
CompletableFuture.supplyAsync(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread1: " + Thread.currentThread().getName());
return "t1";
}, threadPool).whenComplete((v, e)->{
if(e == null){
System.out.println(Thread.currentThread().getName() + " value: " + v);
}
}).exceptionally((e)->{
System.out.println(Thread.currentThread().getName());
e.printStackTrace();
return "exception";
});
System.out.println(Thread.currentThread().getName() + " == main");
threadPool.shutdown();
}
}