Java多线程篇- 202-05-12 21:20
categories:
- java
- 多线程


一、实现线程的4种方式

1.继承Thread类

2.实现Runnable接口

优雅实现Runnable类的两种方式

1)使用内部匿名类实现,demo:com.learn.test.demo.myThread.runnable.AnonymousCreateDemo
2)使用Lambda表达式实现,demo:
com.learn.test.demo.myThread.runnable.LambdaCreateThreadDemo
注:使用内部匿名类和使用lambda表达式实现的区别并不大

实现Runnable类创建线程的优缺点

缺点:
1.无法直接访问Thread类相关属性和方法,也无法直接控制线程,需要借助Thread类的静态方法currentThread获取当前线程来实现相关操作
2.实现Runnable的类并不是线程类,而是线程类的target执行目标类,使用时需要将实现类的实例作为入参传入Thread类的构造,实现类的run方法才可以被调用 优点: 1.解耦,可扩展性好
2.适用于多个线程需要共享资源的场景,例如库存的增减 demo:com.learn.test.demo.myThread.ThreadDemo.goodsStockTest

3.使用Callable和FutureTask类创建线程池

线程池创建线程的步骤

1.实现Callable接口
2.将Callable接口的实现类作为FutureTask构造的入参传入
3.将FutureTask实例作为target属性传入Thread类的构造
4.调用Thread类的start方法,启动线程 demo:com.learn.test.demo.myThread.ThreadDemo.callable

实现调用链路

Thread.start()->Thread.run()->Callable实现类.call()->将call接口执行结果赋值给FutureTask的属性outcome FutureTask.get()->outcome属性
实现逻辑(详情可参见 尼恩 《Java并发核心编程(卷2)》多线程篇):
FutureTask是RunnableFuture的默认实现类,RunnableFuture是继承了Runnable和Future,所以FutureTask同时具有Runnable的run方法和Future的get方法,
具有异步执行和获取异步返回值的能力。在调用FutureTask的run方法时,run方法内部会回调Future的call方法。

4.使用线程池

使用线程池创建线程(实际生产中禁止使用Executors创建线程)
1.execute和submit方法的区别:
(入参)execute的入参是Runnable的实例,submit的入参可以是带FutureTask的实例也可以是Runnable的实例还可以是Thread的实例。
(返回值)execute无返回值,submit有返回值。
execute是Executors的方法,submit是子类ExecutorService的方法。

继承Thread类和实现Runnable类的区别

1.继承Thread类可以更好的实现多线程的并发,使每个线程更专注的完成各自的任务
2.实现Runnable类可以更好的实现多个线程并发的完成同一个任务,访问共享资源
3.当实现Runnable类并且多个线程共享资源时,需要使用原子类数据类型或是限制同步操作

继承Thread类和实现Runnable类的缺点

两者都没有返回异步执行结果,在某些需要关心返回值的场景下,这两种创建方式就不适用了

二、线程优先级

线程优先级的产生其实是跟CPU调度方式有关,目前CPU主流的调度方法是:基于CPU时间片进行线程调度。线程的调度一般分为两种:分时调度模型和抢占式调度模型。JAVA使用抢占式调度方式,也就是根据线程优先级高低来决定优先级被调用的级别。
JAVA线程优先级范围是从1到10,默认线程优先级是5,最大10,最小1,优先级越高越会被优先执行。Thread类中定义了3个常量值,表示线程的最高,最低和默认值。新建的线程优先级默认是5。

  1. /**
  2. * The minimum priority that a thread can have.
  3. */
  4. public final static int MIN_PRIORITY = 1;
  5. /**
  6. * The default priority that is assigned to a thread.
  7. */
  8. public final static int NORM_PRIORITY = 5;
  9. /**
  10. * The maximum priority that a thread can have.
  11. */
  12. public final static int MAX_PRIORITY = 10;

三、线程状态

1、JAVA的6种线程状态

Thread类中定义了State枚举类,State枚举里定义了JAVA的6种线程状态:NEW(新建状态)、RUNNABLE(运行状态)、BLOCKED(阻塞状态)、WAITING(等待状态)、TIMED_WAITING(超时等待状态)、TERMINATED(终止状态)。

  1. public enum State {
  2. /**
  3. * Thread state for a thread which has not yet started.
  4. */
  5. NEW,
  6. /**
  7. * Thread state for a runnable thread. A thread in the runnable
  8. * state is executing in the Java virtual machine but it may
  9. * be waiting for other resources from the operating system
  10. * such as processor.
  11. */
  12. RUNNABLE,
  13. /**
  14. * Thread state for a thread blocked waiting for a monitor lock.
  15. * A thread in the blocked state is waiting for a monitor lock
  16. * to enter a synchronized block/method or
  17. * reenter a synchronized block/method after calling
  18. * {@link Object#wait() Object.wait}.
  19. */
  20. BLOCKED,
  21. /**
  22. * Thread state for a waiting thread.
  23. * A thread is in the waiting state due to calling one of the
  24. * following methods:
  25. * <ul>
  26. * <li>{@link Object#wait() Object.wait} with no timeout</li>
  27. * <li>{@link #join() Thread.join} with no timeout</li>
  28. * <li>{@link LockSupport#park() LockSupport.park}</li>
  29. * </ul>
  30. *
  31. * <p>A thread in the waiting state is waiting for another thread to
  32. * perform a particular action.
  33. *
  34. * For example, a thread that has called <tt>Object.wait()</tt>
  35. * on an object is waiting for another thread to call
  36. * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
  37. * that object. A thread that has called <tt>Thread.join()</tt>
  38. * is waiting for a specified thread to terminate.
  39. */
  40. WAITING,
  41. /**
  42. * Thread state for a waiting thread with a specified waiting time.
  43. * A thread is in the timed waiting state due to calling one of
  44. * the following methods with a specified positive waiting time:
  45. * <ul>
  46. * <li>{@link #sleep Thread.sleep}</li>
  47. * <li>{@link Object#wait(long) Object.wait} with timeout</li>
  48. * <li>{@link #join(long) Thread.join} with timeout</li>
  49. * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
  50. * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
  51. * </ul>
  52. */
  53. TIMED_WAITING,
  54. /**
  55. * Thread state for a terminated thread.
  56. * The thread has completed execution.
  57. */
  58. TERMINATED;
  59. }

NEW(新建状态)

线程被new出来后,还未调用start方法时,线程处于新建状态。

RUNNABLE(运行状态)

当线程调用start方法后,java线程状态就会从NEW变成RUNNABLE状态,RUNNABLE其实对应内核线程的两种状态:就绪和运行状态,而JAVA中是没有就绪这个状态的。
当JAVA线程的Thread实例调用start方法后,JAVA线程会进入RUNNABLE状态,而JAVA线程对应的内核线程会进入就绪状态,等待CPU分配时间片,等待CPU调用执行,线程会一直在就绪和运行状态来回切换,直到线程执行完成或是因为异常而终止。

BLOCKED(阻塞状态)

WAITING(等待状态)

TIMED_WAITING(限时等待状态)

使线程进入TIMED_WAITING状态的几种操作

  1. Thread.sleep(int n):使得当前线程进入限时等待状态,等待时间为n毫秒。
  2. Object.wait():带时限的抢占对象的monitor锁。
  3. Thread.join():带时限的线程合并。
  4. LockSupport.parkNanos():让线程等待,时间以纳秒为单位。
  5. LockSupport.parkUntil():让线程等待,时间可以灵活设置。

    TERMINATED(终止状态)

    当执行完线程实例run()方法后,线程就会进入TERMINATED状态。如果线程在执行过程中出现异常并没有别捕获到异常,也会使线程直接因为异常导致终止进入TERMINATED状态。

    2、使用Jstack工具查看线程状态

    命令

    1. Jstack <pid> //pid java进程ID,可用jps命令查看

    四、线程核心方法

    1.合并线程:join()

    什么是合并线程?

    简单来说有两个线程 线程A依赖线程B执行结果,线程A在执行过程中去调用线程B的join()方法,然后线程A让出CPU等待线程B执行完毕,线程A再继续执行。线程A就是合并线程,线程B就是被合并线程。

    怎么使用合并线程?

    join()方法有3种重载方式:join方法没有返回值,无法直观的知道被合并线程的执行结果 ```java /**
  • 无入参:合并线程等待被合并线程执行完毕 */ public final void join() throws InterruptedException;

/**

  • 入参:millis 合并线程等待时间
  • 合并线程在等待指定millis后,不论被合并线程是否执行完毕,都不再等待 / public final synchronized void join(long millis) throws InterruptedException; /*
  • 入参:millis 合并线程等待时间
  • 入参:nanos 额外的纳秒等待
  • 合并线程在等待指定millis+nanos后,不论被合并线程是否执行完毕,都不再等待 */ public final synchronized void join(long millis, int nanos)throws InterruptedException;
  1. <a name="wZ3A9"></a>
  2. #### 无返回结果Demo
  3. ```java
  4. /**
  5. * join方法:合并线程
  6. * 在线程A中去调用线程B的join方法,那么线程A就是主动合并线程,线程B就是被动合并线程。
  7. * join有3种重载方式
  8. *
  9. * @author Bai
  10. * @date 2022/5/23 22:38
  11. */
  12. public class JoinDemo {
  13. static class SleepThread extends Thread {
  14. public SleepThread (String name) {
  15. super(name);
  16. }
  17. @Override
  18. public void run () {
  19. PrintUtils.print(getName() + "开始执行了");
  20. SleepUtils.sleep(2000L);
  21. PrintUtils.print(getName() + "执行结束了");
  22. }
  23. }
  24. public static void main (String[] args) {
  25. PrintUtils.print("main-执行了");
  26. SleepThread sleepThread1 = new SleepThread("sleepThread1");
  27. try {
  28. sleepThread1.start();
  29. sleepThread1.join();
  30. } catch (InterruptedException e) {
  31. }
  32. SleepThread sleepThread2 = new SleepThread("sleepThread2");
  33. try {
  34. sleepThread2.start();
  35. sleepThread2.join(1000); //等待执行1000ms后继续执行main线程
  36. } catch (InterruptedException e) {
  37. }
  38. SleepThread sleepThread3 = new SleepThread("sleepThread3");
  39. try {
  40. sleepThread3.start();
  41. sleepThread3.join(1000, 1000);//等待执行1000ms+1000nanos后继续执行main线程
  42. } catch (InterruptedException e) {
  43. }
  44. PrintUtils.print("main-执行结束了");
  45. }
  46. }
  1. main-执行了
  2. sleepThread1开始执行了
  3. sleepThread1执行结束了
  4. sleepThread2开始执行了
  5. sleepThread3开始执行了
  6. main-执行结束了
  7. sleepThread2执行结束了
  8. sleepThread3执行结束了

获取join执行结果