目录

[Java核心技术] Java多线程

Java 核心技术读书笔记——Java多线程

1 多进程和多线程简介

简介
  • 当前的操作系统都是多任务OS
  • 每个独立执行的任务就是一个进程
  • OS将时间划分为多个时间片(时间很短)
  • 每个时间片内将CPU分配给每一个任务,时间片结束,CPU将自动回收,再分配给另外的任务

1.1 多进程优点

  • 可以同时运行多个任务
  • 程序因为IO堵塞时,可以释放CPU,让CPU为其他程序服务
  • 当系统有多个CPU时,可以为多个进程同时服务
    • 当前CPU不再提高频率,而是提高核数

1.2 多进程缺点

  • 太笨重,不好管理
  • 太笨重,不好切换

1.3 多线程概念

  • 一个程序可以包括多个子任务,可串/并行
  • 每个子任务可以称为一个线程
  • 如果一个子任务阻塞,程序可以将CPU调度另外一个子任务进行工作。这样CPU还是保留在本程序中,而不是调度到别的程序(进程)去。提高本程序所获得CPU时间和利用率。

1.4 多进程VS多线程

  • 线程共享数据
  • 线程通讯更高效
  • 线程更轻量级更容易切换
  • 多个线程更容易管理
  • 多进程实例
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    
    public class ProcessDemo1 {
    
      public static void main(String[] args) {
        while(true)
        {
          int a = (int) (Math.random() * 100);
          System.out.println(" main thread is running " + a);
          try {
            Thread.sleep(5000); //5000毫秒
          } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
        }   
      }
    }
    
    
  • 多线程实例
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    public class ThreadDemo1
    {
      public static void main(String args[]) throws Exception
      {
        new TestThread1().start();
        while(true)
        {
          System.out.println("main thread is running");
          Thread.sleep(1000);
        }
      }
    }
    
    class TestThread1 extends Thread
    {
      public void run() 
      {
        while(true)
        {
          System.out.println(" TestThread1 is running");
          try {
            Thread.sleep(1000); //1000毫秒
          } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
        }
      }
    } 
    

2 Java多线程实现

2.1 多线程创建

方法一 java.lang.Thread

  • 线程继承Thread类,实现run方法
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    public class Thread1 extends Thread{
      public void run()
      {
        System.out.println("hello");
      }
      public static void main(String[] a)
      {
        new Thread1().start();
      }
    }
    

方法二 java.lang.Runnable接口

  • 线程实现Runnable接口,实现run方法
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    public class Thread2 implements Runnable{
      public void run()
      {
        System.out.println("hello");
      }
      public static void main(String[] a)
      {
        new Thread(new Thread2()).start();
      }
    }
    

Thread vs Runnable

  • Thread占据了父类名额,不如Runnable方便
  • Thread类实现Runnable
  • Runnable启动时需要Thread类的支持
  • Runnable更容易实现多线程中的资源共享
  • 结论:建议实现Runnable接口来完成多线程

2.2 启动

  • start方法,会自动以新进程调用run方法,将会是并行运行(多线程运行)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    
    public class ThreadDemo1
    {
      public static void main(String args[]) throws Exception
      {
        new TestThread1().start();
        while(true)
        {
          System.out.println("main thread is running");
          Thread.sleep(1000);
        }
      }
    }
    
    class TestThread1 extends Thread
    {
      public void run() 
      {
        while(true)
        {
          System.out.println(" TestThread1 is running");
          try {
            Thread.sleep(1000); //1000毫秒
          } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
        }
      }
    } 
    
    /**
     * 
     *  TestThread1 is running
     * main thread is running
     *  TestThread1 is running
     * main thread is running
     * main thread is running
     *  TestThread1 is running
     * ...
     * 
     */
    
  • 直接调用run方法,将变成串行执行

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    
    public class ThreadDemo0
    {
      public static void main(String args[]) throws Exception
      {
        new TestThread0().run();
        while(true)
        {
          System.out.println("main thread is running");
          Thread.sleep(10);
        }
      }
    }
    class TestThread0    
    {
      public void run() 
      {
        while(true)
        {
          System.out.println(" TestThread1 is running");
          try {
            Thread.sleep(1000); //1000毫秒
          } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
        }
      }
    } 
    /**
     * TestThread1 is running
     * TestThread1 is running
     * TestThread1 is running
     * ...
     * 
     */
    
  • 同一个线程,多次start会报错,只执行第一次start方法,多个线程启动,其启动的先后顺序是随机的,由JVM/操作系统来主导

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    
    public class ThreadDemo4
    {
      public static void main(String [] args)
      {
        TestThread4 t=new TestThread4();
        t.start();
        //t.start();
        //t.start();
        //t.start();
        TestThread4 t1=new TestThread4();
        t1.start();   
      }
    }
    
    class TestThread4 extends Thread  
    {
      public void run()
      {
        while(true)
        {
          System.out.println(Thread.currentThread().getName() +
          " is running");
          try {
            Thread.sleep(1000); //1000毫秒
          } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
        }
      }
    }
    /**
     * 
     * Thread-1 is running
     * Thread-0 is running
     * Thread-1 is running
     * Thread-0 is running
     * Thread-1 is running
     * Thread-1 is running
     * Thread-0 is running
     * ...
     * 
     */
    
  • 线程无需关闭,只要其run方法执行结束后,自动关闭

  • main函数(线程)可能早于新线程结束,整个程序并不终止,整个程序终止是等所有的线程都终止(包括main函数线程)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
    public class ThreadDemo2
    {
      public static void main(String args[]) throws InterruptedException
      {
        new TestThread2().start();
      }
    }
     class TestThread2 extends Thread
    {
      public void run() 
      {
        while(true)
        {
          System.out.println("TestThread2 is running");
          try {
            Thread.sleep(1000);
          } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
        }
      }
    } 
    
    /**
     * TestThread2 is running
     * TestThread2 is running
     * TestThread2 is running
     * ...
     * 
     */
    
  • 实现Runnable的对象必须包装在Thread类里面,才可以启动,不可以直接对Runnable的对象进行start方法

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    
    public class ThreadDemo3
    {
      public static void main(String args[])
      {
        //new TestThread3().start();
        //Runnable对象必须放在一个Thread类中才能运行
        TestThread3 tt= new TestThread3();//创建TestThread类的一个实例
        Thread t= new Thread(tt);//创建一个Thread类的实例
        t.start();//使线程进入Runnable状态
        while(true)
        {
          System.out.println("main thread is running");
          try {
            Thread.sleep(1000); //1000毫秒
          } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
        }
      }
    }
    class TestThread3 implements Runnable //extends Thread
    {
      //线程的代码段,当执行start()时,线程从此出开始执行
      public void run()
      {
        while(true)
        {
          System.out.println(Thread.currentThread().getName() +
          " is running");
          try {
            Thread.sleep(1000); //1000毫秒
          } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
        }
      }
    }
    

3 Java多线程信息共享

  • 粗粒度:子线程与子线程之间、和main线程之间缺乏交流
  • 细粒度:线程之间有信息交流通讯
    • 通过共享变量达到信息共享
    • JDK原生库暂不支持发送消息

3.1 通过共享变量在多个线程中共享消息

  • static变量
  • 同一个Runaable类的成员变量
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class ThreadDemo0
{
  public static void main(String [] args)
  {
    new TestThread0().start();
    new TestThread0().start();
    new TestThread0().start();
    new TestThread0().start();
  }
}
class TestThread0 extends Thread  
{
  //private int tickets=100;           //每个线程卖100张,没有共享
  private static int tickets=100;     //static变量是共享的,所有的线程共享
  public void run()
  {
    while(true)
    {
      if(tickets>0)
      {
        System.out.println(Thread.currentThread().getName() +
        " is selling ticket " + tickets);
        tickets = tickets - 1;
      }
      else
      {
        break;
      }
    }
  }
}
/**
 * 
 * 
 * Thread-0 is selling ticket 100
 * Thread-0 is selling ticket 99
 * Thread-0 is selling ticket 98
 * Thread-0 is selling ticket 97
 * Thread-0 is selling ticket 96
 * Thread-0 is selling ticket 95
 * Thread-3 is selling ticket 100
 * Thread-2 is selling ticket 100
 * Thread-1 is selling ticket 100
 * Thread-1 is selling ticket 91
 * Thread-1 is selling ticket 90
 * Thread-1 is selling ticket 89
 * Thread-1 is selling ticket 88
 * ...
 * 
 */

3.2 多线程信息共享问题

  • 工作缓存副本
  • 关键步骤缺乏加锁限制
  • i++,并非原子性操作
    • 读取 主存i(正本)工作缓存(副本)
    • 每个CPU执行 (副本) i+1操作
    • CPU将结果写入到 缓存(副本)
    • 数据从 工作缓存(副本) 刷到 主存(正本)

/java10/01.png
Java多线程内存模型

变量副本问题解决办法

  • 采用volatile关键字修饰变量
  • 保证不同线程对共享变量操作时的可见性
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class ThreadDemo2
{
  public static void main(String args[]) throws Exception 
  {
    TestThread2 t = new TestThread2();
    t.start();
    Thread.sleep(2000);
    t.flag = false;
    System.out.println("main thread is exiting");
  }
}

class TestThread2 extends Thread
{
  //boolean flag = true;          //结果1:子线程不会停止
  volatile boolean flag = true;  //结果2:用volatile修饰的变量可以及时在各线程里面通知
  public void run() 
  {
    int i=0;
    while(flag)
    {
      i++;      
    }
    System.out.println("test thread3 is exiting");
  } 
} 
/**
 * 
 * 结果1:
 * main thread is exiting
 * 
 * 结果2:
 * main thread is exiting
 * test thread3 is exiting
 * 
 * 
 */

关键步骤加锁限制

  • 关键步骤加锁限制
    • 互斥:某一个线程运行一个代码段(关键区),其他线程不能同时运行这个代码段
    • 同步:多个线程的运行,必须按照某一种规定的先后顺序来运行
    • 互斥是同步的一种特例
  • 互斥的关键字是synchronized
    • synchronized代码块/函数,只能一个线程进入
    • synchronized加大性能负担,但是使用简便
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class ThreadDemo3 {
  public static void main(String[] args) {
    TestThread3 t = new TestThread3();
    new Thread(t, "Thread-0").start();
    new Thread(t, "Thread-1").start();
    new Thread(t, "Thread-2").start();
    new Thread(t, "Thread-3").start();
  }
}

class TestThread3 implements Runnable {
  private volatile int tickets = 100; // 多个 线程在共享的
  String str = new String("");

  public void run() {
    while (true) {
      sale();
      try {
        Thread.sleep(100);
      } catch (Exception e) {
        System.out.println(e.getMessage());
      }
      if (tickets <= 0) {
        break;
      }
    }

  }

  public synchronized void sale() { // 同步函数
    if (tickets > 0) {
      System.out.println(Thread.currentThread().getName() + " is saling ticket " + tickets--);
    }
  }
}

/**
 * 
 * Thread-0 is saling ticket 100
 * Thread-3 is saling ticket 99
 * Thread-2 is saling ticket 98
 * Thread-1 is saling ticket 97
 * Thread-3 is saling ticket 96
 * Thread-0 is saling ticket 95
 * Thread-2 is saling ticket 94
 * Thread-1 is saling ticket 93
 * Thread-0 is saling ticket 92
 * ...
 * 
 * 
 */

4 Java多线程管理

4.1 线程状态

  • NEW 刚创建(new)
  • RUNNABLE 就绪态(start)
  • RUNNING 运行中(run)
  • BLOCK 阻塞(sleep)
  • TERMINATED 结束
    /java10/02.png
    Java线程状态图

4.2 线程的方法

  • Thread的部分API已经废弃(不建议使用)
    • 暂停和恢复 suspend/resume
    • 消亡 stop/destroy

线程阻塞/和唤醒

  • sleep,时间一到,自己会醒来
  • wait/notify/notifyAll,等待,需要别人来唤醒
  • join,等待另外一个线程结束
  • interrupt,向另外一个线程发送中断信号,该线程收到信号,会触发InterruptedException(可解除阻塞),并进行下一步处理
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// product/ProductTest.java
package product;
/**
 * 经典生产者与消费者问题
 * 生产者不断的往仓库中存放产品,消费者从仓库中消费产品。
 * 其中生产者和消费者都可以有若干个。
 * 仓库规则:容量有限,库满时不能存放,库空时不能取产品 。
 */
public class ProductTest {
  public static void main(String[] args) throws InterruptedException {
    Storage storage = new Storage();
    
    Thread consumer1 = new Thread(new Consumer(storage));
    consumer1.setName("消费者1");
    Thread consumer2 = new Thread(new Consumer(storage));
    consumer2.setName("消费者2");
    Thread producer1 = new Thread(new Producer(storage));
    producer1.setName("生产者1");
    Thread producer2 = new Thread(new Producer(storage));
    producer2.setName("生产者2");
    
    producer1.start();
    producer2.start();
    Thread.sleep(1000);   
    consumer1.start();
    consumer2.start();    
  }
}

/**
 * 
 * 
 * 生产者2 生产了产品(产品ID:1 产品名称:电话51)
 * producer notifyAll
 * 生产者1 生产了产品(产品ID:1 产品名称:电话39)
 * producer notifyAll
 * 生产者2 生产了产品(产品ID:2 产品名称:电话59)
 * producer notifyAll
 * producer wait
 * producer wait
 * 消费者1 消费了产品(产品ID:2 产品名称:电话59)
 * comsumer notifyAll
 * 生产者1 生产了产品(产品ID:2 产品名称:电话58)
 * producer notifyAll
 * producer wait
 * producer wait
 * ...
 * 
 * 
 */

// product/Storage.java
package product;
/**
 *仓库
 */
class Storage {
  // 仓库容量为3
  private Product[] products = new Product[3];
  private int top = 0;

  // 生产者往仓库中放入产品
  public synchronized void push(Product product) {
    while (top == products.length) {
      try {
        System.out.println("producer wait");
        wait();//仓库已满,等待
      } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
        //把产品放入仓库
    products[top++] = product;
    System.out.println(Thread.currentThread().getName() + " 生产了产品"
        + product);
    System.out.println("producer notifyAll");
    notifyAll();//唤醒等待线程
    

  }

  // 消费者从仓库中取出产品
  public synchronized Product pop() {
    while (top == 0) {
      try {
        System.out.println("consumer wait");
        wait();//仓库空,等待
      } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }

    }

    //从仓库中取产品
    --top;
    Product p = new Product(products[top].getId(), products[top].getName());
    products[top] = null;
    System.out.println(Thread.currentThread().getName() + " 消费了产品" + p);
    System.out.println("comsumer notifyAll");
    notifyAll();//唤醒等待线程
    return p;
  }
}

// product/Producer.java
package product;

import java.util.Random;

/**
 * 生产者
 */
class Producer implements Runnable {
  private Storage storage;

  public Producer(Storage storage) {
    this.storage = storage;
  }

  @Override
  public void run() {
    int i = 0;
    Random r = new Random();
    while(i<10)
    {
      i++; 
      Product product = new Product(i, "电话" + r.nextInt(100));
      storage.push(product);
    }   
  }
}
// product/Product.java
package product;

/**
 * 产品类
 */
class Product {
  private int id;// 产品id
  private String name;// 产品名称

  public Product(int id, String name) {
    this.id = id;
    this.name = name;
  }
  public String toString() {
    return "(产品ID:" + id + " 产品名称:" + name + ")";
  }
  public int getId() {
    return id;
  }
  public void setId(int id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
}

4.3 线程暂停和终止

线程被动暂停和终止

  • 依靠别的线程来拯救自己
  • 没有及时释放资源
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package interrupt;

public class InterruptTest {

  public static void main(String[] args) throws InterruptedException {
    TestThread1 t1 = new TestThread1();

    t1.start();

    // 让线程运行一会儿后中断
    Thread.sleep(2000);
    t1.interrupt();
    System.out.println("main thread is exiting");
  }

}

class TestThread1 extends Thread {
  public void run() {
    // 判断标志,当本线程被别人interrupt后,JVM会被本线程设置interrupted标记
    while (!interrupted()) {
      System.out.println("test thread1 is running");
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
        break;
      }
    }
    System.out.println("test thread1 is exiting");
  }
}

/**
 * 
 * test thread1 is running
 * test thread1 is running
 * test thread1 is running
 * main thread is exiting
 * test thread1 is exiting
 * java.lang.InterruptedException: sleep interrupted
 *  at java.lang.Thread.sleep(Native Method)
 *  at interupt.TestThread1.run(InterruptTest.java:24)
 * 
 * 
 * 
 */

线程主动暂停和终止

  • 定期监测共享变量
  • 如果需要暂停或者终止,先释放资源,再主动动作
  • 暂停:Thread.sleep(),休眠
  • 终止:run方法结束,线程终止
笔记
interrupted()Tread类的方法,用来测试当前线程是否收到一个INTERRUPT的信号。收到返回true,否则返回false
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package interrupt;

public class InterruptTest {

  public static void main(String[] args) throws InterruptedException {
    TestThread2 t2 = new TestThread2();

    t2.start();

    // 让线程运行一会儿后中断
    Thread.sleep(2000);
    t2.flag = false;
    System.out.println("main thread is exiting");
  }

}

class TestThread2 extends Thread {
  public volatile boolean flag = true;
  public void run() {
    // 判断标志,当本线程被别人interrupt后,JVM会被本线程设置interrupted标记
    while (flag) {
      System.out.println("test thread2 is running");
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    System.out.println("test thread2 is exiting");
  }
}
/**
 * 
 * main thread is exiting
 * test thread2 is exiting
 * 
 * 
 */

4.4 多线程死锁

  • 每个线程互相持有别人需要的锁(哲学家吃面问题)
  • 预防死锁,对资源进行等级排序
笔记
TimeUnit是 JDK5 引入的新类,位于java.util.concurrent包中。
它提供了单位时间粒度和一些时间转换、计时和延迟等函数
  • 线程查看工具 jvisualvm
    /java10/03.png
    ThreadDemo5程序死锁图示01
    /java10/04.png
    ThreadDemo5程序死锁图示02
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package deadlock;

import java.util.concurrent.TimeUnit;

public class ThreadDemo5
{
  public static Integer r1 = 1;
  public static Integer r2 = 2;
  public static void main(String args[]) throws InterruptedException
  {
    TestThread51 t1 = new TestThread51();
    t1.start();
    TestThread52 t2 = new TestThread52();
    t2.start();
  }
}

class TestThread51 extends Thread
{
  public void run() 
  {
    //先要r1,再要r2
    synchronized(ThreadDemo5.r1)  //程序卡在此句,希望取得r1
    {
      try {
        TimeUnit.SECONDS.sleep(3); 
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      synchronized(ThreadDemo5.r2)
      {
        System.out.println("TestThread51 is running");
      }
    }
  }
} 
class TestThread52 extends Thread
{
  public void run() 
  {
    //先要r2,再要r1
    synchronized(ThreadDemo5.r2) //程序卡在此句,希望取得r2
    {
      try {
        TimeUnit.SECONDS.sleep(3);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      synchronized(ThreadDemo5.r1) 
      {
        System.out.println("TestThread52 is running");
      }
    }
  }
}

4.5 守护(后台)线程

  • 普通线程的结束,是run方法运行结束
  • 守护线程的结束,是run方法运行结束或 main函数结束
  • 守护线程永远不要访问资源,如文件或数据库等
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package daemon;

public class ThreadDemo4
{
  public static void main(String args[]) throws InterruptedException
  {
    TestThread4 t = new TestThread4();
    t.setDaemon(true);
    t.start();
    Thread.sleep(2000);
    System.out.println("main thread is exiting");
  }
}
 class TestThread4 extends Thread
{
  public void run() 
  {
    while(true)
    {
      System.out.println("TestThread4" + 
      " is running");
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
  }
} 
/**
 * 
 * TestThread4 is running
 * TestThread4 is running
 * TestThread4 is running
 * main thread is exiting
 * 
 */