新建线程

新建一个线程,只要使用 new 关键字创建一个线程对象,并调用这个线程对象的 start() 方法启动这个线程,启动线程后,线程会去调用 run() 方法,run() 方法就是线程具体执行的内容。

创建线程方式一

继承 Thread 类,重写 run() 方法

  1. package com.demo.base;
  2. public class ThreadDemo {
  3. public static void main(String[] args) {
  4. // 创建一个线程对象
  5. Thread thread = new MyThread();
  6. // 启动线程
  7. thread.start();
  8. }
  9. }
  10. class MyThread extends Thread {
  11. @Override
  12. public void run() {
  13. System.out.println("当前线程名为: " + Thread.currentThread().getName());
  14. }
  15. }

说明:

  • Thread.currentThread() 返回当前线程
  • 线程对象直接调用 run() 方法,不会开启新线程,只会在当前线程中串行执行

创建线程方式二

实现 Runnable 接口,实现 run() 方法

  1. package com.demo.base;
  2. public class ThreadDemo {
  3. public static void main(String[] args) {
  4. // 创建一个线程对象
  5. Thread thread = new Thread(new MyRunnable());
  6. // 启动线程
  7. thread.start();
  8. }
  9. }
  10. class MyRunnable implements Runnable {
  11. @Override
  12. public void run() {
  13. System.out.println("实现Runnable接口,当前线程名为: " + Thread.currentThread().getName());
  14. }
  15. }

注意:

  • 在创建 Thread 对象时,可以传入一个字符串参数,该字符串可以用来定义该线程的名字

    public Thread(Runnable target, String name)

终止线程

一般来说,线程执行完毕就会结束,无需手工关闭。但我们也可以调用 stop() 方法来手动关闭线程。

  1. package com.demo.base;
  2. public class ThreadDemo {
  3. public static void main(String[] args) {
  4. // 创建一个线程对象
  5. Thread thread = new Thread(() -> {
  6. String threadName = Thread.currentThread().getName();
  7. System.out.println("线程:" + threadName + " 开始执行。。。");
  8. try {
  9. // 线程睡眠1秒钟
  10. Thread.sleep(1000);
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. System.out.println("线程:" + threadName + " 结束执行");
  15. });
  16. // 启动线程
  17. thread.start();
  18. try {
  19. // 线程睡眠0.5秒钟
  20. Thread.sleep(500);
  21. } catch (InterruptedException e) {
  22. e.printStackTrace();
  23. }
  24. // 终止线程
  25. thread.stop();
  26. System.out.println("线程:" + Thread.currentThread().getName() + " 结束执行");
  27. }
  28. }

注意:

  • stop() 已经被废弃,不推荐使用,原因是 stop() 方法过于暴力,强行把执行到一半的线程终止,可能会引起一些数据不一致的问题。若想终止线程,可以使用线程中断。
  • 线程的 run() 方法不允许抛出异常,若调用的方法抛出异常,需要捕获。
  • sleep() 方法是 Thread 类的静态方法,使当前线程睡眠 n 毫秒。

线程中断

线程中断并不会使线程立即退出,而是给线程发送一个通知,告诉目标线程,希望它退出执行,至于目标线程接到通知后如何处理,则完全由目标线程自行决定。
线程中断涉及 Thread 类的三个方法:

  1. public void interrupt() // 中断线程
  2. public static boolean interrupted() // 判断是否被中断,并清除当前中断状态
  3. public boolean isInterrupted() // 判断是否被中断
  1. package com.demo.base;
  2. public class ThreadDemo {
  3. public static void main(String[] args) {
  4. // 创建一个线程对象
  5. Thread thread = new Thread(() -> {
  6. String threadName = Thread.currentThread().getName();
  7. System.out.println("线程:" + threadName + " 开始执行。。。");
  8. while (true){
  9. // 判断线程是否被中断
  10. if(Thread.currentThread().isInterrupted()){
  11. System.out.println("线程:" + threadName + " 被中断");
  12. break;
  13. }
  14. }
  15. System.out.println("线程:" + threadName + " 结束执行");
  16. });
  17. // 启动线程
  18. thread.start();
  19. try {
  20. // 线程睡眠2秒钟
  21. Thread.sleep(2000);
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. }
  25. // 中断线程
  26. thread.interrupt();
  27. }
  28. }

说明:

  • 线程 sleep() 方法进入睡眠或者 wait() 方法进入等待时,中断该线程,线程会抛出 InterruptedException 异常,并且清除中断状态,所以在捕获这个异常后,可以结束该线程执行,若不想在此处中断,则需要再次设置中断标志。

    等待和通知

    等待 wait() 方法和通知 notify() 方法,主要是为了多线程之间的协作,这两个方法并不是在 Thread 类中,而是定义在 Object 类中。 ```java package com.demo.base;

public class ThreadDemo {

  1. public static void main(String[] args) throws InterruptedException {
  2. Object o = new Object();
  3. // 创建一个线程对象
  4. Thread thread = new Thread(() -> {
  5. String threadName = Thread.currentThread().getName();
  6. System.out.println("线程:" + threadName + " 开始执行。。。");
  7. synchronized (o){
  8. try {
  9. System.out.println("线程:" + threadName + " 将进入等待状态");
  10. // 线程等待
  11. o.wait();
  12. System.out.println("线程:" + threadName + " 被唤醒");
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }
  16. }
  17. System.out.println("线程:" + threadName + " 结束执行");
  18. });
  19. // 启动线程
  20. thread.start();
  21. Thread.sleep(2000);
  22. synchronized (o){
  23. // 唤醒线程
  24. o.notify();
  25. Thread.sleep(2000);
  26. System.out.println("线程:" + Thread.currentThread().getName() + " 退出同步块");
  27. }
  28. }

}

  1. 注意:
  2. - 调用等待 wait() 方法或通知 notify() 方法,都需要先获取锁,并在该锁对象中调用。
  3. - 调用等待 wait() 方法,该线程会进入等待状态,并释放锁。
  4. - notify() 方法只会**随机**唤醒**一个**等待在**该锁对象**上线程,若想唤醒**该锁对象**上所以等待的线程,可以使用 notifyAll() 方法。
  5. - 线程被唤醒后,需要重新获取锁才能继续执行。
  6. - wait() 方法有两个重载的方法 wait(long timeout) wait(long timeout, int nanos),这两个重载方法是线程等待指定时间后,若还没被唤醒,则自动唤醒。
  7. - notify()方法必须在wait()方法之后被调用,不然线程不会被唤醒。
  8. <a name="GIiBa"></a>
  9. #### wait() 方法和 sleep() 的区别
  10. 调用wait()会使线程释放锁,等线程被唤醒之后,会去竞争锁。而调用sleep()则不会释放锁。
  11. <a name="iKKbV"></a>
  12. ## 挂起和继续执行线程
  13. 线程挂起 suspend() 和继续执行 resume() 都是定义在 Thread 类中的实例方法,这两个操作是一对相反的操作,当线程挂起后,必须要等到 resume() 方法才能继续执行。
  14. ```java
  15. package com.demo.base;
  16. public class ThreadDemo {
  17. public static void main(String[] args) throws InterruptedException {
  18. Object o = new Object();
  19. // 创建一个线程对象
  20. Thread thread = new Thread(() -> {
  21. String threadName = Thread.currentThread().getName();
  22. System.out.println("线程:" + threadName + " 开始执行。。。");
  23. synchronized (o) {
  24. System.out.println("线程:" + threadName + " 将挂起");
  25. // 线程挂起
  26. Thread.currentThread().suspend();
  27. System.out.println("线程:" + threadName + " 继续执行");
  28. }
  29. System.out.println("线程:" + threadName + " 结束执行");
  30. });
  31. // 启动线程
  32. thread.start();
  33. Thread.sleep(1000);
  34. new Thread(() -> {
  35. String threadName = Thread.currentThread().getName();
  36. System.out.println("线程:" + threadName + " 开始执行。。。");
  37. synchronized (o) {
  38. System.out.println("线程:" + threadName + " 获取到锁");
  39. }
  40. System.out.println("线程:" + threadName + " 结束执行");
  41. }).start();
  42. Thread.sleep(2000);
  43. System.out.println("线程:" + thread.getName() + " 的状态: " + thread.getState().name());
  44. // 线程继续执行
  45. thread.resume();
  46. }
  47. }

输出结果

  1. 线程:Thread-0 开始执行。。。
  2. 线程:Thread-0 将挂起
  3. 线程:Thread-1 开始执行。。。
  4. 线程:Thread-0 的状态: RUNNABLE
  5. 线程:Thread-0 继续执行
  6. 线程:Thread-0 结束执行
  7. 线程:Thread-1 获取到锁
  8. 线程:Thread-1 结束执行

注意:

  • 线程挂起 suspend() 和继续执行 resume() 都已经被标注为废弃方法。原因是 suspend() 方法导致线程被挂起后,不会释放锁,其他线程想访问被它占用的锁时,无法正常执行,并且如果 resume() 方法在 suspend() 方法之前执行或者没有执行,则被挂起的线程很难再继续执行,被它占用的锁也不会被释放,并且线程被挂起之后,线程的状态还为 RUNNABLE,严重影响我们对系统的判断。

等待线程结束和谦让

等待线程结束 jion() 方法是当前线程在指定线程结束后,再执行。

  1. package com.demo.base;
  2. public class ThreadDemo {
  3. public static void main(String[] args) throws InterruptedException {
  4. // 创建一个线程对象
  5. Thread thread = new Thread(() -> {
  6. String threadName = Thread.currentThread().getName();
  7. System.out.println("线程:" + threadName + " 开始执行。。。");
  8. try {
  9. Thread.sleep(2000);
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. System.out.println("线程:" + threadName + " 结束执行");
  14. });
  15. // 启动线程
  16. thread.start();
  17. // 等待线程结束
  18. thread.join();
  19. System.out.println("线程:" + Thread.currentThread().getName() + " 结束执行");
  20. }
  21. }

注意:

  • join() 方法在哪个线程中调用,则该线程为等待线程。在哪个线程对象上调用,则该线程为被等待线程。等待线程要等到被等待线程执行完毕才会继续执行。
  • join() 方法有两个重载的方法 join(long millis) 和 join(long millis, int nanos),都是等待指定时间后,就不再等待。

谦让 yield() 方法,是一个静态方法,它会使当前线程让出CPU,当前线程让出CPU后,还会进行CPU资源的争夺。