哪些情况下会使用到多线程
1、通过并行计算,提高程序的性能
2、通过开启多线程的方式解决网络IO、响应等待的问题
解释:比如socket的accept,当客户端发送来一个请求,必须等accept之后才能继续往下执行,当多个客户端同时发起请求的时候,Tomcat的就会在线程池里面用多个线程的方式并行处理,从而解决一个一个等待的现象。
多线程常见的3种的实现方式
第一种:继承Thread类:重写run()方法
public class TestThread extends Thread{
public void start(){
System.out.println("执行start");
}
public void run(){
System.out.println("执行run");
}
public static void main(String[] args) {
TestThread m = new TestThread();
m.start();
}
}
第二种:实现Runnable接口,实现run()方法
public class Thread2 implements Runnable {
@Override
public void run() {
System.out.println("启动线程");
}
}
第三种:使用ExecutorService、Callable、Future实现有返回结果的多线程
该实现方式执行多线程是带返回值的,那什么情况下我们需要用到带返回指的多线程呢?
1、你希望一个异步的线程去执行代码逻辑,并且希望子线程执行完毕后主线程可以拿到子线程的执行结果
代码实现:
实现Callable接口,实现call方法
public class MyCallable implements Callable<Object> {
private String taskNum;
MyCallable(String taskNum) {
this.taskNum = taskNum;
}
@Override
public Object call() throws Exception {
System.out.println(">>>" + taskNum + "任务启动");
Date dateTmp1 = new Date();
Thread.sleep(1000);
Date dateTmp2 = new Date();
long time = dateTmp2.getTime() - dateTmp1.getTime();
System.out.println(">>>" + taskNum + "任务终止");
return taskNum + "任务返回运行结果,当前任务时间【" + time + "毫秒】";
}
}
/**
* @Auther: hdx
* @Date: 2020/5/31 0031 09:07
* @Description:
*/
public class TestCallable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("----程序开始运行----");
Date date1 = new Date();
int size = 5;
//创建一个定长的线程池
ExecutorService pool = Executors.newFixedThreadPool(size);
// 创建多个有返回值的任务
ArrayList<Future> lists = new ArrayList<>();
for (int i = 0; i < size; i++) {
Callable myCallable = new MyCallable(i + "");
// 执行任务并获取Future对象
Future f = pool.submit(myCallable);
lists.add(f);
}
//关闭线程池
pool.shutdown();
// 获取所有并发任务的运行结果
for (Future f : lists) {
// 从Future对象上获取任务的返回值,并输出到控制台
System.out.println(">>>" + f.get().toString());
}
Date date2 = new Date();
System.out.println("----程序结束运行----,程序运行时间【"
+ (date2.getTime() - date1.getTime()) + "毫秒】");
}
}
程序输出结果:
----程序开始运行----
>>>1任务启动
>>>2任务启动
>>>0任务启动
>>>3任务启动
>>>4任务启动
>>>2任务终止
>>>1任务终止
>>>0任务终止
>>>0任务返回运行结果,当前任务时间【1000毫秒】
>>>1任务返回运行结果,当前任务时间【1000毫秒】
>>>2任务返回运行结果,当前任务时间【1000毫秒】
>>>3任务终止
>>>3任务返回运行结果,当前任务时间【1000毫秒】
>>>4任务终止
>>>4任务返回运行结果,当前任务时间【1000毫秒】
----程序结束运行----,程序运行时间【1136毫秒】
注意:
ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。
线程的几种状态(六种):
1、新建状态(NEW)
即用new关键字新建一个线程,这个线程就处于新建状态
2、Runnable状态(运行状态):
调用start方法进入就绪状态,调用run进入运行状态,线程执行完成之后,不能试图用start将其唤醒
1、线程调用start(),新建状态转化为就绪状态
2、线程sleep(long)时间到,等待状态转化为就绪状态
3、阻塞式IO操作结果返回,线程变为就绪状态
4、其他线程调用join()方法,结束之后转化为就绪状态
5、线程对象拿到对象锁之后,也会进入就绪状态
3、超时等待状态:不同于等待状态,超时等待是指定了超时时间的
4、阻塞状态:
以下场景线程将会阻塞:
synchronized同步方法,当一个线程先获取到锁之后,其他线程就进入阻塞状态,直到拿到这把锁
5、等待状态:
1、当前线程对象调用wait()方法。
2、LockSupport.park():出于线程调度的目的,禁用当前线程
6、终止状态:
表示线程已经执行完毕
1、run()和call()线程执行体中顺利执行完毕,线程正常终止。
2、线程抛出一个没有捕获的Exception或Error。
注意:主线程和子线程互不影响,子线程并不会因为主线程结束就结束
线程的启动和终止
start、stop,但是stop的方式是比较暴力的,那如何优雅的方式终止一个线程呢?
为什么stop方法终止线程是不推荐的?
假设有这样的一个业务场景,一个线程正在处理一个复杂的业务流程,突然间线程被调用stop而意外终止,这个业务数据还有可能是一致的吗?这样是肯定会出问题的,stop会释放锁并强制终止线程,造成执行一半的线程终止,带来的后果也是可想而知的,这就是为什么jdk不推荐使用stop终止线程的方法的原因,因为它很暴力会带来数据不一致性的问题。
第一种方式:给出一个线程终止的标志,当满足条件的时候就退出就行:
Thread.currentThread().isInterrupted()最开始为false 当调用当前线程的interrupt()之后会变成true.
如何进行线程复位?
Thread.interrupted()
备注:native修饰方法,表名该方法是用其他非java语言实现的方法,是由jvm调用的
第二种方式:使用Volatile 设置线程之间可见变量。