线程八大基础核心

在java多线程并发编程中,有八大基础核心。它们分别是:
  1.创建线程的方式
  2.线程启动
  3.线程停止
  4.线程生命周期
  5.线程相关的方法
  6.线程相关的属性
  7.线程异常处理
  8.线程安全

一、创建线程的方式:

  在网络上,关于创建线程的方式。主流的说法有四种:继承Thread、实现Runnable、线程池、Callable与Future
  在官方文档,关于创建线程的方式。有且仅有两种:继承Thread、实现Runnable
  在java编程语言中,创建线程的方式,我们以官方文档为准,只有两种。关于说线程池、Callable与Future是项目开发中常用的方式,它们的底层其实还是Runnable。

  1.1 继承Thread

package com.anan.thread.createthread;

public class ThreadDemo {

    public static void main(String[] args) {
        // 创建线程对象,并启动
        Thread t1 = new MyThread();
        t1.start();
    }
}

/**
 * 继承Thread方式,创建线程
 */
class MyThread extends Thread{

    @Override
    public void run() {
        System.out.println("继承Thread方式,创建线程");
    }
}

  1.2 实现Runnable接口

package com.anan.thread.createthread;

public class RunnableDemo {

    public static void main(String[] args) {
        // 创建线程对象,并启动
        Runnable r1 = new MyRunnable();
        Thread t1 = new Thread(r1);

        t1.start();

    }
}

/**
 * 实现Runnable接口,创建线程
 */
class MyRunnable implements Runnable{

    public void run() {
        System.out.println("实现Runnable接口,创建线程");
    }
}

  在实际开发中,推荐使用实现Runnable接口方式,原因有:

    1.   实现Runnable接口方式,可以将线程处理业务,与创建线程分离,从而解耦
    2.   实现Runnable接口方式,扩展性更好,因为在java编程语言中,只能继承一个父类,而可以实现多个接口
    3.   实现Runnable接口方式,配合线程池使用,有更好的资源利用率和性能

二、启动线程

  2.1 在java编程语言中,创建好线程对象后,通过调用start方法,启动线程。

    在start方法内部启动流程是:

      2.1.1.判断当前线程状态,是否是NEW状态

      2.1.2.如果当前线程是NEW状态,将当前线程加入线程组

      2.1.3.调用本地方法start0(),开启线程

  2.2 这里有两个问题:

    1.问题一:可以调用两次start方法吗?

      不能,因为线程只有在NEW状态下,可以启动

    2.问题二:可以调用run方法,启动线程吗?

      不能,因为直接调用run方法,相当于在主线程main下调用普通方法,并没有开启新的线程

三、停止线程

  在java编程语言中,线程停止指的是线程的生命周期结束,分为正常执行结束和意外结束。

  3.1 正常停止

  启动线程,调用run方法执行,当run方法正常执行结束,则线程正常停止。

package com.anan.thread.stopthread;

/**
 * 启动线程,调用run方法执行,当run方法正常执行结束,则线程正常停止
 */
public class NormalStopThreadDemo {

    public static void main(String[] args) {
        // 创建线程对象
        Runnable r1 = new MyRunnable();
        Thread t1 = new Thread(r1);

        // 启动线程
        t1.start();

    }
}

/**
 * 实现Runnable接口,创建线程
 */
class MyRunnable implements Runnable{

    public void run() {
        System.out.println("线程准备开始执行......");
        System.out.println("......线程执行中......");
        System.out.println("线程执行结束......");
    }
}

  3.2 意外停止

  启动线程,调用run方法执行,当run方法发生异常后,则线程意外停止。

 1 package com.anan.thread.stopthread;
 2 
 3 /**
 4  * 启动线程,调用run方法执行,当run方法发生异常后,则线程意外停止
 5  */
 6 public class AccidentStopThreadDemo {
 7 
 8     public static void main(String[] args) {
 9         // 创建线程对象
10         Runnable r1 = new MyRunnable2();
11         Thread t1 = new Thread(r1);
12 
13         // 启动线程
14         t1.start();
15 
16     }
17 }
18 
19 /**
20  * 实现Runnable接口,创建线程
21  */
22 class MyRunnable2 implements Runnable{
23 
24     public void run() {
25         System.out.println("线程准备开始执行......");
26         System.out.println("......线程执行中......");
27         // 发生意外
28         int i = 1/0;
29 
30         System.out.println("线程执行结束......");
31     }
32 }

  3.3 中断停止

  3.3.1 中断信号停止

  启动线程,调用run方法执行,通过线程对象调用interrupt中断方法,发送中断信号停止线程。

 1 package com.anan.thread.stopthread;
 2 
 3 import java.util.concurrent.TimeUnit;
 4 
 5 /**
 6  * 启动线程,调用run方法执行,通过线程对象调用interrupt中断方法,发送中断信号停止线程
 7  */
 8 public class InterruptSignalStopThread {
 9 
10     public static void main(String[] args) throws InterruptedException {
11         // 创建线程对象
12         Runnable r1 = new MyRunnable3();
13         Thread t1 = new Thread(r1);
14 
15         // 启动线程
16         t1.start();
17         // 主线程休眠1毫秒后,向t1线程发送中断信号
18         TimeUnit.MILLISECONDS.sleep(1);
19         t1.interrupt();
20     }
21 }
22 
23 /**
24  * 实现Runnable接口,创建线程
25  */
26 class MyRunnable3 implements Runnable{
27 
28     public void run() {
29         System.out.println("线程准备开始执行......");
30         // 循环执行任务,直到收到中断信号为止
31         // isInterrupted()方法,返回线程是否被中断
32         int i = 0;
33         while ( ! Thread.currentThread().isInterrupted()){
34             i++;
35             System.out.println("......线程第【" + i +"】次执行中......");
36         }
37         System.out.println("收到中断信号,线程在第【" + i + "】次执行结束......");
38     }
39 }

  3.3.2 响应中断停止

  启动线程,调用run方法执行,在run方法中有可响应中断的操作,比如:sleep、wait等。通过线程对象调用interrupt中断方法,发送中断信号响应中断停止。

 1 package com.anan.thread.stopthread;
 2 
 3 import java.util.concurrent.TimeUnit;
 4 
 5 /**
 6  * 启动线程,调用run方法执行,在run方法中有可响应中断的操作,
 7  * 比如:sleep、wait等。通过线程对象调用interrupt中断方法,发送中断信号响应中断停止
 8  */
 9 public class ResponseInterruptSignalStopThread {
10 
11     public static void main(String[] args) throws InterruptedException {
12         // 创建线程对象
13         Runnable r1 = new MyRunnable4();
14         Thread t1 = new Thread(r1);
15 
16         // 启动线程
17         t1.start();
18         // 主线程休眠1毫秒后,向t1线程发送中断信号
19         TimeUnit.MILLISECONDS.sleep(1);
20         t1.interrupt();
21     }
22 }
23 
24 /**
25  * 实现Runnable接口,创建线程
26  */
27 class MyRunnable4 implements Runnable{
28 
29     public void run() {
30         System.out.println("线程准备开始执行......");
31         // 循环执行任务,直到收到中断信号,通过sleep方法响应中断
32         int i = 0;
33         try{
34 
35             while ( i <= 100000000){
36                 i++;
37                 System.out.println("......线程第【" + i +"】次执行中......");
38 
39                 // 休眠10毫秒
40                 TimeUnit.MILLISECONDS.sleep(10);
41             }
42         }catch (InterruptedException e){
43             System.out.println("收到中断信号,sleep方法响应中断,线程在第【" + i + "】次执行结束......");
44             e.printStackTrace();
45         }
46 
47     }
48 }

1.在java编程语言中,线程停止的情况有哪些?
1.1.在java编程语言中,线程停止的情况有:正常停止、意外停止、中断停止
1.2.在实际项目开发中,我们追求正常停止、中断停止。避免意外停止

2.在java编程语言中,如何人为优雅的停止线程?
2.1.在java编程语言中,通过调用线程对象的interrupt()方法,发送中断信号的方式,人为优雅的停止线程
2.2.所谓人为优雅,即指与线程协商的方式停止线程,而不是强制停止线程,最终的停止权交由线程本身来控制

3.在java编程语言中,为什么说坚决不要调用stop方法停止线程?
3.1.因为如果使用stop方法停止线程,它是一种暴力手段,即强制停止线程,有可能会导致线程任务执行的不完整,不安全。且在较新版本的jdk中,已经设置为过期的方法,不再推荐使用。

四、线程生命周期

  

 

 

  状态转换案例

  4.1 状态 NEW/RUNNABLE/TERMINATED

  通过该案例,演示线程的NEW/RUNNABLE/TERMINATED状态。

 1 package com.anan.thread.threadstate;
 2 
 3 import java.util.concurrent.TimeUnit;
 4 
 5 /**
 6  * 演示线程状态:NEW/RUNNABLE/TERMINATED
 7  */
 8 public class ThreadStateDemo1 {
 9 
10     public static void main(String[] args) throws InterruptedException{
11         // 创建线程对象
12         String tName = "my-thread";
13         Runnable r1 = new MyRunnable();
14         Thread t1 = new Thread(r1,tName);
15 
16         // 输出线程状态:NEW
17         System.out.println("1.新建线程:" + tName + "当前状态:" + t1.getState());
18 
19         // 启动线程,等待1毫秒,输出线程状态:RUNNABLE
20         t1.start();
21         TimeUnit.MILLISECONDS.sleep(1);
22         System.out.println("2.启动线程后:" + tName + "当前状态:" + t1.getState());
23 
24         // 发送中断信号,等待1毫秒,输出线程状态:TERMINATED
25         t1.interrupt();
26         TimeUnit.MILLISECONDS.sleep(1);
27         System.out.println("3.给线程发送中断信号后:" + tName + "当前状态:" + t1.getState());
28 
29     }
30 }
31 
32 /**
33  * 实现Runnable接口,创建线程
34  */
35 class MyRunnable implements Runnable{
36     public void run() {
37         while (!Thread.currentThread().isInterrupted()){
38             ;// 不断循环,等待中断信号发生,然后结束线程运行
39         }
40         System.out.println("中断信号发生," + Thread.currentThread().getName() + "准备结束运行.");
41     }
42 }

 

   4.2 状态:BLOCKED

  通过该案例,演示线程的BLOCKED状态。业务描述:

  1.模拟获取共享资源银行:Bank账户余额信息,在获取账户余额时,需要加上同步锁

  2.创建两个线程,并发获取银行账户余额,模拟当一个线程加锁操作中,另外一个线程只能阻塞等待

  3.在主线程中,输出两个线程的状态

 1 package com.anan.thread.threadstate;
 2 
 3 import java.util.concurrent.TimeUnit;
 4 
 5 /**
 6  * 演示线程状态:BLOCKED
 7  */
 8 public class ThreadStateBlockedDemo {
 9     // 公共锁对象
10     public static final Object LOCK = new Object();
11 
12     /**
13      * 2.创建两个线程,并发获取银行账户余额,
14      * 模拟当一个线程加锁操作中,另外一个线程只能阻塞等待
15      */
16     public static void main(String[] args) {
17 
18         // 创建Runnable对象
19         Runnable r1 = new MyRunnable1();
20 
21         // 创建两个线程对象
22         String tName_1 = "my-thread-1";
23         Thread t1 = new Thread(r1,tName_1);
24 
25         String tName_2 = "my-thread-2";
26         Thread t2 = new Thread(r1,tName_2);
27 
28         // 启动线程t1,t2
29         t1.start();
30         t2.start();
31 
32         // 输出两个线程:t1,t2当前状态
33         System.out.println("1.主线程"+ Thread.currentThread().getName() +
34                 "打印,线程:" + t1.getName() + "当前状态:" + t1.getState());
35         System.out.println("2.主线程"+ Thread.currentThread().getName() +
36                 "打印,线程:" + t2.getName() + "当前状态:" + t2.getState());
37 
38 
39     }
40 
41     /**
42      * 1.模拟获取共享资源银行:Bank
43      * 账户余额信息,在获取账户余额时,需要加上同步锁
44      */
45     public static void getBankMoney() {
46         synchronized (LOCK){
47             System.out.println(Thread.currentThread().getName() + "线程,获取到锁###.");
48             // 休眠1秒,模拟业务操作
49             try {
50                 TimeUnit.SECONDS.sleep(1);
51                 // 打印输出账户余额
52                 System.out.println("线程:" + Thread.currentThread().getName() +
53                 "获取到账户余额了......");
54             } catch (InterruptedException e) {
55                 e.printStackTrace();
56             }
57         }
58 
59         System.out.println(Thread.currentThread().getName() + "线程,释放锁###.");
60     }
61 
62 }
63 
64 /**
65  * 实现Runnable接口,创建线程
66  */
67 class MyRunnable1 implements Runnable{
68 
69     public void run() {
70         // 获取账户余额
71         ThreadStateBlockedDemo.getBankMoney();
72     }
73 }

 

   4.3 状态:WAITING

  通过该案例,演示线程的WAITING状态。

 1 package com.anan.thread.threadstate;
 2 
 3 import java.util.concurrent.TimeUnit;
 4 
 5 /**
 6  * 演示线程状态:WAITING
 7  */
 8 public class ThreadStateWaitingDemo {
 9 
10     // 创建公共锁
11     public final static Object LOCK = new Object();
12 
13     public static void main(String[] args) throws InterruptedException{
14         // 创建线程对象
15         Runnable r1 = new MyRunnable3();
16         String tName_1 = "my-thread-1";
17         Thread t1 = new Thread(r1,tName_1);
18 
19         // 启动线程,休眠1秒后,获取线程状态
20         t1.start();
21         TimeUnit.SECONDS.sleep(1);
22         System.out.println("1.主线程"+ Thread.currentThread().getName() +
23                 "打印,线程:" + t1.getName() + "当前状态:" + t1.getState());
24 
25         // 主线程唤醒t1线程,再次输出线程状态
26         synchronized (LOCK){
27             LOCK.notify();
28         }
29         TimeUnit.SECONDS.sleep(1);
30         System.out.println("2.主线程"+ Thread.currentThread().getName() +
31                 "打印,线程:" + t1.getName() + "当前状态:" + t1.getState());
32     }
33 }
34 
35 /**
36  * 实现Runnable接口,创建线程
37  */
38 class MyRunnable3 implements Runnable{
39 
40     public void run() {
41         System.out.println("线程:" + Thread.currentThread().getName() +
42                 "即将进入等待:ThreadStateWaitingDemo.LOCK.wait(),等待主线程输出状态.");
43         synchronized (ThreadStateWaitingDemo.LOCK){
44             try {
45                 ThreadStateWaitingDemo.LOCK.wait();
46             } catch (InterruptedException e) {
47                 e.printStackTrace();
48             }
49         }
50         System.out.println("线程:" + Thread.currentThread().getName() +
51                 "被唤醒执行结束,等待主线程输出状态.");
52     }
53 }

 

   4.4 状态:TIMED_WAITING

  通过该案例,演示线程的TIMED_WAITING状态。

 1 import java.util.concurrent.TimeUnit;
 2 
 3 /**
 4  * 演示线程状态:TIMED_WAITING
 5  */
 6 public class ThreadStateTimedWaitingDemo {
 7 
 8     public static void main(String[] args) throws InterruptedException{
 9 
10         // 创建线程对象
11         Runnable r1 = new MyRunnable2();
12         String tName_1 = "my-thread-1";
13         Thread t1 = new Thread(r1,tName_1);
14 
15         // 启动线程,休眠1秒后,获取线程状态
16         t1.start();
17         TimeUnit.SECONDS.sleep(1);
18         System.out.println("1.主线程"+ Thread.currentThread().getName() +
19                 "打印,线程:" + t1.getName() + "当前状态:" + t1.getState());
20 
21     }
22 }
23 
24 /**
25  * 实现Runnable接口,创建线程
26  */
27 class MyRunnable2 implements Runnable{
28 
29     public void run() {
30         System.out.println("线程:" + Thread.currentThread().getName() +
31         "准备休眠3秒:TimeUnit.SECONDS.sleep(3),等待主线程输出状态.");
32         try {
33             TimeUnit.SECONDS.sleep(3);
34         } catch (InterruptedException e) {
35             e.printStackTrace();
36         }
37     }
38 }

1、线程生命周期中有哪些状态吗?

1.1.在java编程语言中,线程的生命周期总共有6种状态

2、各种状态对应的含义吗?

2.1.分别是:

新建:NEW

可运行:RUNNABLE

已终止:TERMINATED

阻塞:BLOCKED

等待:WAITING

计时等待:TIMED_WAITING

五、线程相关方法

  1.在java编程语言中,与线程相关的方法主要有:
    1.1.Object.wait/Object.notify/Object/notifyAll
    1.2.Thread.sleep/Thread.join/Thread.yield

  2、案例:wait/notify/notifyAll

  方法简述:

  wait:释放线程拥有的当前锁对象,让线程进入WAITING状态

  notify:唤醒当前锁对象等待池(WaitSet)中,某一个线程

  notifyAll:唤醒当前锁对象等待池(WaitSet)中,所有线程

  业务描述:

  1.通过wait与notify(notifyAll)实现线程协同,实现经典的生产者消费者模式

  2.编写生产者,向队列中生产数据,如果队列满,生产者进行等待,并唤醒消费者进行消费

  3.编写消费者,从队列中消费数据,如果队列空,消费者进行等待,并唤醒生产者进行生产

  4.在主类main方法中,创建两个生产者线程进行生产

  5.在主类main方法中,创建一个消费者线程进行消费

 1 import java.util.ArrayList;
 2 
 3 /**
 4  * 生产者消费者模型:wait与notify(notifyAll)
 5  */
 6 public class ProducerConsumerDemo {
 7 
 8     // 定义公共资源:队列容量、队列
 9     public final static Integer CAPACITY = 6;
10     public final static ArrayList<Integer> QUEUE = new ArrayList<Integer>(CAPACITY);
11 
12     // 定义锁对象
13     public final static Object LOCK = new Object();
14 
15     public static void main(String[] args) {
16 
17         // 创建两个生产者线程
18         Runnable r1 = new Producer();
19         Thread producer0 = new Thread(r1,"producer-0");
20         producer0.start();
21 
22         Thread producer1 = new Thread(r1,"producer-1");
23         producer1.start();
24 
25         // 创建消费者线程
26         Runnable r2 = new Consumer();
27         Thread consumer1 = new Thread(r2,"consumer-0");
28         consumer1.start();
29 
30     }
31 }
32 
33 /**
34  * 生产者
35  */
36 class Producer implements Runnable{
37 
38     public void run() {
39         while(true){
40             synchronized (ProducerConsumerDemo.LOCK){
41                 // 检查队列是否满,推荐用while而不是if
42                 while(ProducerConsumerDemo.QUEUE.size() ==
43                         ProducerConsumerDemo.CAPACITY){
44                     // 如果队列满,则等待消费者消费
45                     try {
46                         System.out.println("队列满size:" +ProducerConsumerDemo.QUEUE.size()+ ",线程【" +
47                                 Thread.currentThread().getName() + "】正在等待消费者消费.");
48                         ProducerConsumerDemo.LOCK.wait();
49                     } catch (InterruptedException e) {
50                         e.printStackTrace();
51                     }
52                 }
53 
54                 // 如果队列不满,则正常生产
55                 ProducerConsumerDemo.QUEUE.add(1);
56                 System.out.println("线程【" + Thread.currentThread().getName() +
57                         "】生产了数据.当前队列size:" + ProducerConsumerDemo.QUEUE.size());
58 
59                 // 唤醒消费者
60                 ProducerConsumerDemo.LOCK.notifyAll();
61             }
62         }
63     }
64 }
65 
66 /**
67  * 消费者
68  */
69 class Consumer implements Runnable{
70 
71     public void run() {
72         while(true){
73             synchronized (ProducerConsumerDemo.LOCK){
74                 // 检查队列是否空,推荐用while而不是if
75                 while(ProducerConsumerDemo.QUEUE.isEmpty()){
76                     // 如果队列空,则等待生产者生产
77                     try {
78                         System.out.println("队列空size:" +ProducerConsumerDemo.QUEUE.size()+ ",线程【" +
79                                 Thread.currentThread().getName() + "】正在等待生产者生产.");
80                         ProducerConsumerDemo.LOCK.wait();
81                     } catch (InterruptedException e) {
82                         e.printStackTrace();
83                     }
84                 }
85 
86                 // 如果队列不空,则正常消费
87                 ProducerConsumerDemo.QUEUE.remove(0);
88                 System.out.println("线程【" + Thread.currentThread().getName() +
89                         "】消费了数据.当前队列size:" + ProducerConsumerDemo.QUEUE.size());
90 
91                 // 唤醒生产者
92                 ProducerConsumerDemo.LOCK.notifyAll();
93             }
94         }
95     }
96 }

 1 1.你知道wait/notify/notifyAll方法的作用吗?
 2  1.1.wait/notify/notifyAll都是Object中的方法
 3  1.2.通过等待与唤醒,实现线程之间的协同
 4  1.3.wait方法会释放当前锁对象,让线程进入WAITING状态
 5  1.4.notify方法用于:
 6   1.4.1.唤醒当前锁对象等待池(WaitSet)中,正在等待
 7    当前锁对象的某一个线程
 8   1.4.2.让该线程进入RUNNABLE状态,并移入锁池(EntrySet)中,
 9   重新竞争锁对象
10  1.5.notifyAll方法用于:
11   1.5.1.唤醒当前锁对象等待池(WaitSet)中,正在等待
12    当前锁对象的所有线程
13   1.5.2.让等待当前锁对象的所有线程,进入RUNNABLE状态,并
14    移入锁池(EntrySet)中,重新竞争锁对象
15   
16 2.你知道notify与notifyAll方法的区别吗?
17  2.1.通过以上1.4点、1.5点已经说明了notify
18  与notifyAll的区别
19  2.2.这里可能有朋友不明白锁池(EntrySet),与
20  等待池(WaitSet)的概念。我们通过一个图进行说明:
21   
22 #流程文字描述:
23 1.锁池(EntrySet):代表正在等待同一锁对象的线程集合,线程进入BLOCKED状态
24 2.拥有者(Owner):代表已经获取到锁对象的线程
25 3.等待池(WaitSet):代表已经进入WAITING状态,等待被唤醒的线程集合
26 4.流程描述:
27   4.1.enter:线程准备获取锁对象,此时锁被其它线程占有,线程进入EntrySet
28   4.2.acquire:当其它线程释放锁对象,EntrySet中的某个线程获取占有锁对象
29   4.3.release:占有锁对象线程,通过wait方式释放锁对象,进入WaitSet中,等待被唤醒
30   4.4.acquire:当有其它线程,通过notify/notifyAll方法唤醒WaitSet中线程,线程将重新进入EntrySet,重新竞争获取锁对象
31   4.5.release and exit:占有锁对象线程,释放锁并退出执行,线程生命周期结束

 

 

   方法:sleep

  方法简述:

  1.让线程进入TIMED_WAITING状态

  2.如果线程占有锁对象资源,不会释放锁

 1 import java.util.Random;
 2 import java.util.concurrent.TimeUnit;
 3 
 4 /**
 5  * 线程方法sleep:
 6  *  1.让线程进入TIMED_WAITING状态
 7  *  2.如果线程占有锁对象资源,不会释放锁
 8  */
 9 public class ThreadMethodSleepDemo {
10 
11     //
12     public final static Object LOCK = new Object();
13 
14     public static void main(String[] args) {
15         // 创建Runnable对象
16         Runnable r1 = new MyRunnable();
17 
18         // 创建两个线程对象
19         Thread t1 = new Thread(r1,"thread-0");
20         Thread t2 = new Thread(r1,"thread-1");
21 
22         // 启动线程
23         t1.start();
24         t2.start();
25 
26     }
27 }
28 
29 /**
30  * 实现Runnable接口,创建线程
31  */
32 class MyRunnable implements Runnable{
33     public void run() {
34         System.out.println("【" + Thread.currentThread().getName() + "】准备休眠.");
35         // 加锁,演示sleep不释放锁
36         synchronized (ThreadMethodSleepDemo.LOCK){
37             System.out.println("【" + Thread.currentThread().getName() + "】获取到锁.休眠进行中.");
38             int time = 0;
39             try {
40                 Random random = new Random();
41                 time = random.nextInt(6);
42                 TimeUnit.SECONDS.sleep(time);
43             } catch (InterruptedException e) {
44                 e.printStackTrace();
45             }
46             System.out.println("【" + Thread.currentThread().getName() + "】随机休眠:" +time+ "秒时间到.释放锁.");
47         }
48         System.out.println("【" + Thread.currentThread().getName() + "】结束休眠.");
49     }
50 }

 

 

   方法:join

  方法简述:

  1.方法join表示在一个线程中,加入另外一个线程

  2.被加入线程,会等待【加入线程】执行完成

 1 import java.util.concurrent.TimeUnit;
 2 
 3 /**
 4  * 线程方法join:
 5  *      1.如果有线程加入我们,我们就等待加入线程执行完成
 6  */
 7 public class ThreadMethodJoinDemo {
 8 
 9     public static void main(String[] args) throws InterruptedException{
10         // 创建线程对象
11         Runnable r1 = new MyRunnable1();
12         Thread t1 = new Thread(r1,"thread-0");
13         t1.start();
14 
15         // join方法
16         t1.join();
17 
18         System.out.println("主线程main方法执行中.");
19 
20     }
21 }
22 
23 /**
24  * 实现Runnable接口,创建线程
25  */
26 class MyRunnable1 implements Runnable{
27     public void run() {
28         for(int i = 0; i < 3; i++){
29             System.out.println(Thread.currentThread().getName() +
30                     "第:【" + i + "】次执行.");
31 
32             // 休眠10毫秒
33             try {
34                 TimeUnit.MILLISECONDS.sleep(10);
35             } catch (InterruptedException e) {
36                 e.printStackTrace();
37             }
38         }
39         System.out.println(Thread.currentThread().getName() + "执行结束.");
40     }
41 }

 

 

   方法:yield

  方法简述:

  1.线程主动让出CPU执行时间

  2.孔融让梨,和谐社会大家好

  3.需要注意:这只是一种美德,不能对它产生依赖

 1 /**
 2  * 线程方法yield:
 3  *      1.线程主动让出CPU执行时间
 4  *      2.孔融让梨,和谐社会大家好
 5  */
 6 public class ThreadMethodYieldDemo {
 7 
 8     public static void main(String[] args) {
 9         // 创建两个线程
10         Runnable r1 = new MyRunnable2();
11 
12         Thread t0 = new Thread(r1, "thread-0");
13         Thread t1 = new Thread(r1, "thread-1");
14 
15         // 线程t0
16         t0.start();
17 
18         // yield方法,线程t1
19         t1.start();
20         t1.yield();
21 
22     }
23 }
24 
25 /**
26  * 实现Runnable接口,创建线程
27  */
28 class MyRunnable2 implements Runnable{
29     public void run() {
30         for(int i = 0; i < 3; i++){
31             System.out.println("【" + Thread.currentThread().getName() +
32                     "】第:【" + i + "】次执行.");
33 
34         }
35         System.out.println("【" + Thread.currentThread().getName() +
36                 "】执行结束.");
37     }
38 }

 

 

 

1.你知道sleep方法的作用吗?
 1.1.让线程进入TIMED_WAITING状态
 1.2.如果线程占有锁对象资源,不会释放锁
 
2.你知道wait与sleep方法的区别吗?
 2.1.wait方法,让线程进入WAITING状态,或者TIMED_WAITING状态
 2.2.sleep方法,让线程进入TIMED_WAITING状态
 2.3.区别:
     wait方法,会释放占有的锁对象资源
     sleep方法,【不】释放占有的锁对象资源
     
3.你知道join方法的作用吗?
 3.1.join表示在一个线程中,加入另外一个线程
 3.2.被加入线程,会等待【加入线程】执行完成
 
4.你知道yield方法的作用吗?
 4.1.线程主动让出CPU执行时间
 4.2.孔融让梨,和谐社会大家好
 4.3.需要注意:
      该方法,只是一种美德,我们不应该依赖它

六、线程属性

在我们日常多线程编程中,需要关心线程的线程优先级有:
  线程Id
  线程名称
  是否是守护线程
  线程优先级
1、线程Id

  简述:

  1.线程Id,是线程的唯一身份标识

  2.用于JVM执行过程中,识别线程

 1 /**
 2  * 线程属性:Id
 3  * 1.线程Id,是线程的唯一身份标识
 4  * 2.用于JVM执行过程中,识别线程
 5  */
 6 public class ThreadIdDemo {
 7 
 8     public static void main(String[] args) {
 9         // 创建线程对象
10         Runnable r1 = new MyRunnable();
11         Thread t1 = new Thread(r1);
12         t1.start();
13 
14         // 打印主线程main的Id
15         System.out.println("【主】线程Id:" + Thread.currentThread().getId());
16     }
17 
18 }
19 
20 /**
21  * 实现Runnable接口,创建线程
22  */
23 class MyRunnable implements Runnable{
24     public void run() {
25         System.out.println("【子】线程Id:" + Thread.currentThread().getId());
26     }
27 }

 

 

 

 

 

   2、线程名称

  简述:

  1.线程名称,可以在创建线程对象的时候设置(有默认名称)

  2.用于在开发过程中,方便调试,或者友好阅读

 1 import java.util.concurrent.TimeUnit;
 2 
 3 /**
 4  * 线程属性:Id
 5  * 1.线程名称,可以在创建线程对象的时候设置(有默认名称)
 6  * 2.用于在开发过程中,方便调试,或者友好阅读
 7  */
 8 public class ThreadNameDemo {
 9 
10     public static void main(String[] args) throws InterruptedException{
11         // 创建两个线程对象
12         Runnable r1 = new MyRunnable1();
13         // 使用默认线程名称
14         Thread t1 = new Thread(r1);
15         t1.start();
16 
17         // 休眠1秒,实现顺序打印
18         TimeUnit.SECONDS.sleep(1);
19 
20         // 指定线程名称
21         Thread t2 = new Thread(r1,"my-thread-2");
22         t2.start();
23     }
24 
25 }
26 
27 /**
28  * 实现Runnable接口,创建线程
29  */
30 class MyRunnable1 implements Runnable{
31     public void run() {
32         System.out.println("线程名称:" + Thread.currentThread().getName());
33     }
34 }

 

 

1.你知道线程Id的作用吗?
  1.1.线程Id,是线程的唯一身份标识
  1.2.用于JVM执行过程中,识别线程
  
2.你知道线程名称的作用吗?
  2.1.线程名称,可以在创建线程对象的时候设置(有默认名称)
  2.2.用于在开发过程中,方便调试,或者友好阅读
  
3.你知道什么是守护线程吗?
  3.1.守护线程,是在后台运行的线程
  
4.你知道守护线程与用户线程的区别吗?
  4.1.我们平常开发,创建的都是用户线程
  4.2.守护线程,与用户线程总体没有区别
  4.3.区别在于:
   4.3.1.守护线程,不会影响JVM的退出执行
   4.3.2.用户线程,当JVM退出的时候,需要等待用户线程执行完成
  
5.你知道有哪些线程优先级吗?
  5.1.线程优先级,表示获取CPU执行时间机会的多少,优先级越高,越有机会
  5.2.在java编程语言中,总共有10种优先级设置:
   最小优先级:1
   默认优先级:5
   最大优先级:10

  
6.你知道为什么我们在实际开发中,不应该依赖线程优先级吗?
  6.1.原因一:
    java的线程,是基于操作系统内核线程实现
  6.2.原因二:
    不同的操作系统,优先级定义不一样
  6.3.原因三:
    在不同的操作系统上,java线程优先级映射到操作系统内核会不一样,因此我们不应该依赖线程的优先级

七、异常处理

  1、难以发现的子线程异常

  简述:

  1.在子线程child-exception-0中,抛出异常

  2.主线程main依然正常执行,在实际项目中,会导致难以发现子线程的异常情况

 

 1 /**
 2  * 主线程main不能发现子线程异常
 3  */
 4 public class NotFoundChildThreadException {
 5 
 6     public static void main(String[] args) {
 7         // 创建线程对象
 8         Runnable r1 = new MyRunnable();
 9         Thread t1 = new Thread(r1,"child-exception-0");
10         t1.start();
11 
12         // 主线程循环打印输出,忽略子线程异常
13         for (int i = 0; i < 5; i++) {
14             System.out.println("主线程main输出:风景这边独好!当前索引【"
15                     + i + "】");
16         }
17 
18     }
19 
20 }
21 
22 /**
23  * 实现Runnable,创建线程
24  */
25 class MyRunnable implements Runnable{
26     public void run() {
27         System.out.println(Thread.currentThread().getName() +
28                 "准备抛出异常了...start");
29         // 抛出异常
30         throw  new RuntimeException("子线程抛出了异常.");
31     }
32 }

 

 

   2、不能捕获的子线程异常

  简述:

  1.创建3个子线程:thread-0、thread-1,thread-2

  2.每个子线程都会抛出异常

  3.在主线程main中,进行捕获处理。期望如果thread-0抛出了异常,那么thread-1/thread-2线程,不要创建执行

 

 1 /**
 2  * 不能捕获的子线程异常
 3  */
 4 public class NotCaughtChildException {
 5 
 6     public static void main(String[] args) {
 7         // 预期子线程会抛出异常,通过try{}catch(){}捕获处理
 8         try{
 9             // 创建子线程0
10             Runnable r1 = new MyRunnable1();
11             Thread t0 = new Thread(r1,"thread-0");
12             t0.start();
13 
14             // 创建子线程1
15             Thread t1 = new Thread(r1,"thread-1");
16             t1.start();
17 
18             // 创建子线程2
19             Thread t2 = new Thread(r1,"thread-2");
20             t2.start();
21 
22         }catch (RuntimeException e){
23             System.out.println("捕获到了异常.");
24         }
25     }
26 }
27 
28 /**
29  * 实现Runnable,创建线程
30  */
31 class MyRunnable1 implements Runnable{
32     public void run() {
33         System.out.println(Thread.currentThread().getName() +
34                 "准备抛出异常了...start");
35         // 抛出异常
36         throw  new RuntimeException("子线程抛出了异常.");
37     }
38 }

  3、全局异常处理

  简述:

  1.在2.中,不能通过try{}catch(){}捕获子线程异常。因为try{}catch(){}只能捕获当前线程的异常

  2.如果要对所有子线程,进行统一异常处理,需要一个全局异常处理器

  3.全局异常处理器接口:Thread.UncaughtExceptionHandler

  4.实现方式:

4.1.编写全局异常处理器,实现接口:Thread.UncaughtExceptionHandler
4.2.注册使用全局异常处理器

  3.1 全局异常处理器:

1 /**
2  * 自定义全局异常处理器类
3  */
4 public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
5 
6     public void uncaughtException(Thread t, Throwable e) {
7         System.out.println(t.getName() + "发生了异常,异常消息:" + e.getMessage());
8     }
9 }

  3.2 注册使用全局异常处理器

 1 /**
 2  * 注册使用自定义全局异常处理器
 3  */
 4 public class UseUncaughtExceptionHandler {
 5 
 6     public static void main(String[] args) {
 7 
 8         // 关键代码:设置全局异常处理器
 9         Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
10 
11         // 创建子线程0
12         Runnable r1 = new MyRunnable1();
13         Thread t0 = new Thread(r1,"thread-0");
14         t0.start();
15 
16         // 创建子线程1
17         Thread t1 = new Thread(r1,"thread-1");
18         t1.start();
19 
20         // 创建子线程2
21         Thread t2 = new Thread(r1,"thread-2");
22         t2.start();
23     }
24 }
25 
26 /**
27  * 实现Runnable,创建线程
28  */
29 class MyRunnable2 implements Runnable{
30     public void run() {
31         System.out.println(Thread.currentThread().getName() +
32                 "准备抛出异常了...start");
33         // 抛出异常
34         throw  new RuntimeException("子线程抛出了异常.");
35     }
36 }

 

 

1.你知道java的异常体系吗?
  1.1.java异常体系中,老祖宗是Throwable
  1.2.在Throwable下,有Exception异常体系(日常开发中,见得最多)
  1.3.在Throwable下,有Error错误体系(日常开发中,较少关注)
    
2.你知道哪一种异常处理方式比较好吗?
  2.1.通过案例演示,我们知道不能通过try{}catch(){},跨线程捕获异常。try{}catch(){}只能捕获当前线程自己的异常
  
  2.2.处理方式一:
   2.2.1.可以在当前线程中进行try{}catch(){},捕获处理当前线程的异常
   2.2.2.该种方式处理起来代码量多、繁琐。不推荐使用
   
  2.3.处理方式二:
   2.3.1.通过设置全局异常处理器:UncaughtExceptionHandler
   2.3.2.实现异常全局统一处理。推荐使用
   
3.你知道如何使用UncaughtExceptionHandler吗?
  3.1.编写全局异常处理器,实现接口:
   Thread.UncaughtExceptionHandler
  3.2.注册使用全局异常处理器:
   // 关键代码:设置全局异常处理器
        Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());

Java异常体系类图

 

 

1.你知道多线程的理论基础有哪些吗?
   1.1.进程与线程的区别
   1.2.线程实现方式
   1.3.线程安全三要素
   1.4.java内存模型JMM
   1.5.锁
   
2.你知道进程与线程的区别吗?
  2.1.进程是操作系统【分配资源】的最小单位
  2.2.线程是操作系统【调度】的最小单位
   
3.你知道线程的实现方式有哪些吗?
  3.1.基于操作系统内核实现方式(内核线程)
  3.2.基于用户进程实现方式(用户态线程,即协程)
  3.3.java的线程实现方式是:内核线程实现方式

4.你知道多线程安全的三要素吗?
  4.1.线程安全要素一:原子性
  4.2.线程安全要素二:可见性
  4.3.线程安全要素三:有序性

5.你知道java的内存模型JMM吗?
  5.1.参见附图
  
6.你知道java编程中,线程安全的常规手段吗?
  6.1.线程安全常规手段一:加锁
  6.2.线程安全常规手段二:消除共享资源
    
7.你知道java中的volatile关键字吗?
  7.1.volatile关键字是一种轻量级线程安全实现方式
  7.2.volatile关键字的底层原理:保证可见性,禁止重排序
  7.3.使用volatile关键字注意事项:
    a.volatile关键字修饰变量值修改,不依赖原来的值;或者只有单一线程进行修改
    b.volatile关键字修饰的变量,不与其它变量一起参与原子性约束
    c.满足a、b两条,那么volatile关键字修饰的变量,在多线程下是线程安全的

 java内存模型JMM:

 

posted @ 2020-12-03 13:39  时光浮夸乱了遍地流年  阅读(208)  评论(0编辑  收藏  举报