什么是多线程
进程:Process,系统级别的资源调度单位。相当于一个程序实例
线程:Thread,进程内的资源调度单位,在进程内运行的最小单位,有自己的计数器和内存堆栈结构。
多线程的好处
- 多核处理器资源利用更加充分
- 使程序响应时间更快
- 构建更好的编程模型
一个线程的生命周期
一共约7个状态,创建
,预备
,运行
,定时等待
,等待
,阻塞
,终止
而其他书上有简略版的,如:
这些生命周期的状态在Java里就是对应线程的六个状态
线程从创建到结束
1. 线程的创建
// 直接new,或者创建Thread的子类(我用匿名方式替代了一下)
Thread t = new Thread(){
@Override
public void run() {
super.run();
}
};
t.start();
// 用 Runnable (我用lambda替代了一下)
Thread t = new Thread(() -> System.out.println("new t"));
t.start();
// 用Timmer(其实是定时任务,在另一个线程中)
Timer t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(Instant.now());
}
}, 1000, 1000);
// Future来包装Callable
ThreadPoolExecutor executorService = new ThreadPoolExecutor(1, 1, 1L, TimeUnit.MINUTES,
new ArrayBlockingQueue<Runnable>(1), new ThreadPoolExecutor.DiscardPolicy());
System.out.println(Thread.currentThread().getName());
try {
Future t = executorService.submit(() -> Thread.currentThread().getName());
System.out.println(t.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
还有线程池,上面Future就是使用线程池的,线程池一般很少创建单线程的,除非业务需要,所以下回再说,写个demo
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> System.out.println(Thread.currentThread().getName()));
最为传统的方法就是new Thread,但是这种用法在大型软件开发中几乎不用,而是根据线程模型选择Future或者线程池去执行异步任务。这里的例子仅为展示线程的创建方法
2. 线程的终止
当线程逻辑运行完成后会自动退出。
但是如果线程执行过程中,某些条件不满足,导致线程等待或者阻塞,就可能会导致异常终止。
suspend
,resume
,stop
被废弃了,原因是可能造成死锁,数据异常等问题。想要安全退出线程,可以使用中断请求标记来检查线程状态,检查到中断信号后,处理完数据、释放锁,再退出线程即可。
3. 中断
当线程的interrupt方法被其他线程调用的时候,这个线程就会接收到中断请求,如果线程自己检查isInterruted
检查中断状态,也可以使用Thread.interrupted()
静态方法恢复中断状态。
许多抛InterruptedException的方法就是接受到中断请求,一般情况下会清除该线程中断标记,再抛出异常。
线程的类别、属性等参数(优先级,状态)
创建
,预备
,运行
,定时等待
,等待
,阻塞
,终止
。其中预备和运行都被Java定义为RUNABLE状态
状态名称 | 说明 |
---|---|
NEW | 初始状态,线程被构建,但是还没有调用 start() 方法 |
RUNABLE | 运行状态,Java线程将操作系统中的就绪和运行两种状态笼统地称作“运行中” |
BLOCKED | 阻塞状态,表示线程阻塞于锁 |
WAITING | 等待状态,表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断) |
TIME_WAITING | 超时等待状态,不同于WAITING,它是可以在指定时间内自行返回的 |
TERMINATED | 终止状态,表示当前线程已经执行完毕 |
线程分类方式有很多种,除了前面创建方式分类其实还有一种分类方式
Daemon线程和非Daemon线程
Thread直接new的线程可以直接调用setDaemon (true)
将这个线程设为守护线程。设置为守护线程后线程将会随虚拟机一直在幕后工作,前台是基本观察不到的。除非手动关闭,否则直到JVM准备关闭时,这个线程才会随之一同终止。