java多线程-基础

177 阅读3分钟

能干什么

  1. 异步执行

    开启线程后任务是异步执行的,主线程不需要等待,可以做别的事。

    同步异步区别:同步是指请求方发起请求数据到数据最后完成的这段过程中都需要自己参与,异步是指请求方发起请求数据后就不在参与过程。

    阻塞非阻塞区别:阻塞是指请求方发起请求数据时,需要一直等待数据准备完成,非阻塞则直接返回(如果数据未准备完成,可以返回错误信息)

  2. 提高效率

    在多核CPU下,通过多线程可以并行执行来提高效率。单核CPU没有作用(单核CPU引入多线程反而导致上下文切换效率更低)。

创建方式

  1.  // 重写Thread的run方法
     // 缺点:1. 任务和线程紧耦合 2. 任务无法继承其它类
     Thread t1 = new Thread("t1") {
         @Override
         public void run() {
             log.debug("hello");
         }
     };
     t1.start();
    
  2.  // 使用Runnable接口
     // 推荐使用此种方式,改善了继承Thread的缺点
     Runnable runnable = new Runnable() {
         public void run(){
             log.debug("hello");
         }
     };
     Thread t = new Thread(runnable);
     t.start();
    
  3.  // FutureTask实现了Runable接口,增强了有返回值的功能
     FutureTask<Integer> futureTask = new FutureTask<>(new Callable<Integer>() {
         @Override
         public Integer call() throws Exception {
             return 1;
         }
     });
     new Thread(futureTask, "futureTask").start();
     // 主线程阻塞,等待执行完毕
     Integer result = task3.get();
     log.debug("结果是:{}", result);
    

运行机制

JVM中有个虚拟机栈,是线程私有的,在每个线程启动时会为线程在虚拟机栈中分配一块区域(线程栈),用来存储当前线程运行的信息。

在线程处理方法时,每个方法会作为一个栈帧(存储局部变量,返回地址等)压入栈。所以在处理局部变量时不会有线程安全问题,但处理成员变量和静态变量会有安全问题。

线程状态

从操作系统层面来说线程有五种状态 image.png

但是Java中分为了六种状态,定义在Thread.State枚举类中。

  • NEW

    线程刚被创建,还没调用start方法

  • RUNNABLLE

    包含操作系统层面的可运行,运行中,BIO阻塞状态(JAVA无法区分)

  • BLOCKED

    在锁处理的情况中,等待锁的线程就是阻塞状态

  • WAITING

    线程在一个可能无限的时间内被阻塞,如join方法

  • TIMED_WAITING

    线程在一个有限的时间内被阻塞,如sleep方法。

  • TERMINATED

    线程任务运行完成,线程终止状态。

Thread 常用方法

  • start() 与 run() 的区别

    start() 是启动线程来执行任务逻辑,run() 只是一个方法而已。

  • sleep()

    线程调用 sleep 会让从运行状态进入有时限的等待(阻塞)状态

    其它线程可以使用 interrupt 方法打断正在睡眠的线程(会抛出 InterruptedException)

    睡眠结束后的线程进入可运行状态

    建议用 TimeUnit 的 sleep() 代替 Thread 的 sleep() 来获得更好的可读性

  • yield()

    线程调用 yield() 会让当前线程从运行状态进入可运行状态

    不是说调用 yield() 了就肯定会让别的线程执行任务,有可能CPU时间片还是给这个线程

  • join()

    线程调用 join() 会让当前执行这行代码的线程等待其执行完任务(如在main方法中调用线程 t1.join(),那么main线程就会等待线程t1执行完再执行)

    join() 可以在多线程中保持同步

    join() 底层实现是wait/notiflyAll

  • interrupt() / isInterrupted() / interrupted() (static)

    线程调用 interrupt() 表示打断终止这个线程运行。isInterrupted() 获取线程打断状态(默认false)。interrupted(static) 获取线程打断状态,并且获取后会重置为false

    当线程正常运行时,调用 interrupt() ,不会终止线程运行,但线程中可以获取打断状态来处理打断逻辑

    当线程阻塞时,(如 sleep() , wait() , join())这些方法会抛出受检异常 InterruptedException,此时调用 interrupt() 则会使线程抛出 InterruptedException,并且使用 isInterrupted() / interrupted() (static) 获取打断状态也是为false