线程的基本认识及操作
程序员文章站
2022-03-08 18:58:16
...
线程的基本介绍
什么是线程
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
进程的执行逻辑
为什么会有线程
多线程的本质是:合理的利用多核心CPU资源来实现线程的并行处理,来实现同一个进程内的多个任务的并行执行,同时基于线程本身的异步执行特性,提升任务处理的效率。
- 在多核CPU中,利用多线程可以实现真正意义上的并行执行
- 在一个应用进程中,会存在多个同时执行的任务,如果其中一个任务被阻塞,将会引起不依赖该任务的任务也被阻塞。通过对不同任务创建不同的线程去处理,可以提升程序处理的实时性
- 线程可以认为是轻量级的进程,所以线程的创建、销毁比进程更快
线程的应用场景
为什么要用多线程
- 异步执行
- 利用多CPU资源实现真正意义上的并行执行
线程的价值
线程的应用场景
- 使用多线程实现文件下载
- 后台任务:如定时向大量(100W以上)的用户发送邮件
- 异步处理:记录日志
- 多步骤的任务处理,可根据步骤特征选用不同个数和特征的线程来协作处理,多任务的分割,由一个主线程分割给多个线程完成
如何在Java中应用多线程
Java中使用多线程的方式
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
public class ThreadDemo1 extends Thread{
@Override
public void run() {
System.out.println("当前线程:"+Thread.currentThread().getName());
}
public static void main(String[] args) {
ThreadDemo1 t = new ThreadDemo1();
t.start();
}
}
public class ThreadRunableDemo implements Runnable {
@Override
public void run() {
System.out.println("当前线程:"+Thread.currentThread().getName());
}
public static void main(String[] args) {
ThreadRunableDemo t = new ThreadRunableDemo();
Thread t1 = new Thread(t);
t1.start();
}
}
public class ThreadCallableDemo implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("当前线程:"+Thread.currentThread().getName());
return "线程:"+Thread.currentThread().getName()+"执行成功";
}
public static void main(String[] args) {
try {
ExecutorService service = Executors.newFixedThreadPool(1);
Future<String> submit = service.submit(new ThreadCallableDemo());
System.out.println(submit.get());
} catch (Exception e){
e.printStackTrace();
}finally {
System.out.println("执行完成");
}
}
}
Java线程的生命周期
Java线程从创建到销毁,一共经历6个状态
- NEW:初始状态,线程被构建,但是还没有调用start方法
- RUNNABLED:运行状态,JAVA线程把操作系统中的就绪和运行两种状态统一称为“运行中”
- BLOCKED:阻塞状态,表示线程进入等待状态,也就是线程因为某种原因放弃了CPU使用权,阻塞也分为几种情况
- WAITING: 等待状态
- TIME_WAITING:超时等待状态,超时以后自动返回
- TERMINATED:终止状态,表示当前线程执行完毕
Thread.join的使用及原理
Thread.join的作用是保证线程执行结果的可见性
Thread.join的原理
Thread.join
的本质其实是wait/notifyall
使用示例:
wait和notify的使用
案例:
ThreadProducer 类:
public class ThreadProducer implements Runnable{
private Queue<String> bags;
int size;
public ThreadProducer(Queue<String> bags, int size) {
this.bags = bags;
this.size = size;
}
@Override
public void run() {
int i = 0;
while (true) {
i++;
synchronized (bags) {
while (size == bags.size()) {
System.out.println("背包满了");
try {
bags.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
bags.add("bag" + i);
System.out.println("生产者生产:" + i);
bags.notifyAll();
}
}
}
}
ThreadConsumer 类:
public class ThreadConsumer implements Runnable{
private Queue<String> bags;
int size;
public ThreadConsumer(Queue<String> bags, int size) {
this.bags = bags;
this.size = size;
}
@Override
public void run() {
while (true) {
synchronized (bags) {
while (bags.isEmpty()) {
System.out.println("背包为空");
try {
bags.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者消费:" + bags.remove());
bags.notifyAll();
}
}
}
}
测试类:
public class TestBags {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
int size =10;
ThreadProducer producer = new ThreadProducer(queue,size);
ThreadConsumer consumer = new ThreadConsumer(queue,size);
Thread t1 = new Thread(producer);
Thread t2 = new Thread(consumer);
t1.start();
t2.start();
}
}
执行结果:
- 从上面的案例来看,其实wait/notify本质上其实是一种条件的竞争,至少来说,wait和notify方法一定是互斥存在的,既然要实现互斥,那么synchronized就是一个很好的解决方法
- wait和notify是用于实现多个线程之间的通信,而通信必然会存在一个通信的载体,比如我们小时候玩的游戏,用两个纸杯,中间用一根线连接,然后可以实现比较远距离的对话。而这根线就是载体,那么在这里也是一样,wait/notify是基于synchronized来实现通信的。也就是两者必须要在同一个频道也就是同一个锁的范围内
如何正确终止一个线程
正确的终止一个线程一定是run方法里面的代码执行完成。
Thread.interrupted & Thread.interrupt
当其他线程通过调用当前线程的interrupt方法,表示向当前线程打个招呼,告诉他可以中断线程的执行了,至于什么时候中断,取决于当前线程自己。
hread.interrupted()对设置中断标识的线程复位,并且返回当前的中断状态。
代码示例:
public class InterruptDemo {
private static int i;
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
while(true){
//true标识被中断过
if(Thread.currentThread().isInterrupted()){
System.out.println("before:"+Thread.currentThread().isInterrupted());
Thread.interrupted(); // 对中断标识复位 false
System.out.println("after:"+Thread.currentThread().isInterrupted());
}
}
});
thread.start();
TimeUnit.SECONDS.sleep(1);
thread.interrupt(); //中断
}
}
输出:
before:true
after:false
这里为什么不用stop是因为stop使用后线程立即停止,并不能保证run方法的完成执行完成。