哪些情况下会使用到多线程

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()方法,会阻塞直到计算完成。

线程的几种状态(六种):

多个请求在一个线程里处理java 多线程并发请求_System

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终止线程的方法的原因,因为它很暴力会带来数据不一致性的问题。

第一种方式:给出一个线程终止的标志,当满足条件的时候就退出就行:

多个请求在一个线程里处理java 多线程并发请求_System_02

Thread.currentThread().isInterrupted()最开始为false 当调用当前线程的interrupt()之后会变成true.

 如何进行线程复位?

Thread.interrupted()

备注:native修饰方法,表名该方法是用其他非java语言实现的方法,是由jvm调用的

第二种方式:使用Volatile 设置线程之间可见变量。