Java Thread 类基本用法总结

  1. 线程创建
  2. 线程中断
  3. 线程等待
  4. 线程休眠
  5. 获取线程实例

一、线程创建

Java 中通过Thread类创建线程主要有两种方式:继承Thread类和实现Runnable接口。

1. 继承 Thread 类

通过继承Thread类,并重写其run()方法来定义线程执行体:

// 继承Thread类
class MyThread extends Thread{
    public void run(){// run方法相当于回调函数
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);// 休眠1000毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
}
public class Demo {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new  MyThread();
        //真正在系统创建出一个线程
        t.start();// 启动线程,会调用run()方法真正在系统中创建线程
    }
}

}

run()为线程的入口方法,新的线程启动了,就要执行这里的代码,run 不需要咱们手动调用,新的线程创建好了之后,自动的去执行

2. 实现 Runnable 接口

实现Runnable接口的run()方法,然后将实现类实例作为参数传递给Thread构造函数:

// 实现Runnable接口
class MyRunnable implements Runnable{
    @Override
    public void run() {
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);// 休眠1000毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
      
    }
}
public class Demo2 {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable=new MyRunnable();
        Thread t=new Thread(runnable);
        t.start(); // 启动线程
    }
}

最终还是要通过 Thread, 真正创建线程线程里要干啥, 通过 Runnable 来表示 (而不是通过直接重写 Thread run 来表示了)

两种方式对比:
  • 继承Thread类:无法再继承其他类,不够灵活
  • 实现Runnable接口:可以同时继承其他类,适合多线程共享资源的场景
  • 推荐使用实现Runnable接口的方式,遵循 “组合优于继承” 的设计原则
其他变形:
  • 匿名内部类创建 Thread ⼦类对象
//使⽤匿名类创建 Thread ⼦类对象
public class Demo3 {
    public static void main(String[] args) {
        Thread t=new Thread(){
            @Override
            public void run() {
                System.out.println("使⽤匿名类创建 Thread ⼦类对象");
            }
        };
        t.start();
    }
}
  • 匿名内部类创建 Runnable ⼦类对象
//使⽤匿名类创建 Runnable ⼦类对象
public class Demo4 {
    public static void main(String[] args) {
        Thread t=new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("使⽤匿名类创建 Runnable ⼦类对象");
            }
        });
        t.start();
    }
}
  • lambda 表达式创建 Runnable ⼦类对象(推荐使用)
//lambda 表达式创建 Runnable ⼦类对象
public class Demo5{
   public static void main(String[] args){
			Thread t=new Thread(()->{
				public void run() {
                System.out.println("使⽤匿名类创建 Runnable ⼦类对象");
           	}
		});
		t.start();
	}

}

二、线程中断

线程中断并不是立即终止线程,而是给线程设置一个中断标志,线程可以根据这个标志来决定是否终止自己。

Thread类提供了三个与中断相关的方法:

  1. public void interrupt():设置线程的中断标志
  2. public boolean isInterrupted():判断线程是否被中断(不清除中断标志)
  3. public static boolean interrupted():判断当前线程是否被中断(会清除中断标志)
public class Demo6 {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("线程运行中...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // 当线程在休眠时被中断,会抛出InterruptedException
                    // 此时中断标志会被清除,需要重新设置
                    System.out.println("线程被中断");
                    Thread.currentThread().interrupt(); // 重新设置中断标志
                }
            }
            System.out.println("线程退出");
        });
        
        thread.start();
        
        // 主线程休眠3秒后中断子线程
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt(); // 中断线程
    }
}

注意:当线程处于wait()、sleep()、join()等阻塞状态时,调用interrupt()会导致线程抛出InterruptedException并清除中断标志。

三、线程等待

有时,我们需要等待⼀个线程完成它的⼯作后,才能进⾏⾃⼰的下⼀步⼯作。

 public static void main(String[] args) {
        Thread t=new Thread(()->{
            for (int i = 0; i <3000 ; i++) {
                System.out.println("hello Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("t 线程结束");
        });
        t.start();
        System.out.println("main 线程结束");
    }

由于多个线程之间“并发执行”,随机调度,所以站在个人得角度就存在我想先t线程先执行结束,却是main线程先结束了,有什么办法可以要求多个线程结束得顺序呢,这就引入了join

Thread类的join()方法用于让当前线程等待目标线程执行完毕后再继续执行。

常用的join()方法重载:

  • public void join():无限期等待,直到目标线程执行完毕
  • public void join(long millis):最多等待指定的毫秒数
  • public void join(long millis, int nanos):最多等待指定的毫秒数加纳秒数
public class Demo7 {

    public static void main(String[] args) {
        Thread t=new Thread(()->{
            for (int i = 0; i <3000 ; i++) {
                System.out.println("hello Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("t 线程结束");
        });
        t.start();
        t.join(3000);//在主线程中调用 t.join就是让主线程等待 t线程先结束。
        System.out.println("main 线程结束");// 线程mian会在线程1执行3秒后才开始执行
    }
}

join()方法的主要应用场景是需要等待多个子线程完成后再进行汇总操作的情况。

四、线程休眠

Thread类的静态方法sleep()用于让当前线程暂停执行指定的时间,释放 CPU 资源,但不会释放锁。
方法定义:

  • public static void sleep(long millis):休眠指定的毫秒数
  • public static void sleep(long millis, int nanos):休眠指定的毫秒数加纳秒数
public class Demo8 {
    public static void main(String[] args) {
        System.out.println("程序开始执行: " + System.currentTimeMillis());
        
        try {
            // 休眠2秒
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println("程序继续执行: " + System.currentTimeMillis());
        
        // 带纳秒的休眠
        try {
            Thread.sleep(1000, 500000000); // 1.5秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println("程序执行结束: " + System.currentTimeMillis());
    }
}

注意:线程休眠(sleep)”和“线程中断(interrupt)是完全不同的状态,没有直接等同关系”。
简单说:
sleep(休眠):是线程主动“暂停执行”的一种状态(TIMED_WAITING),是线程自己的“休息”行为,休眠结束后会自动恢复运行,期间没有被任何外部信号打断。
interrupt(中断):是外部对线程发送的“中断信号”,目的是让线程停止当前任务。它不是一种线程状态,而是一个“信号标记”,需要线程主动响应。
休眠是“计划内的暂停”,比如线程说“我要睡10秒,到点再干活”,是主动且可控的。
中断是“计划外的提醒”,比如其他线程对它说“别睡了/别干活了”,是外部发起的信号,线程需要自己决定是否响应。

五、获取线程实例

Thread类提供了currentThread()静态方法来获取当前正在执行的线程实例。这个⽅法我们已经⾮常熟悉了,前面线程中断中已经涉及到

public class Demo6 {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {//无法直接通过线程对象引用获取当前执行的线程,必须通过这个方法动态获取。
                System.out.println("线程运行中...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // 当线程在休眠时被中断,会抛出InterruptedException
                    // 此时中断标志会被清除,需要重新设置
                    System.out.println("线程被中断");
                    Thread.currentThread().interrupt(); // 重新设置中断标志
                }
            }
            System.out.println("线程退出");
        });
        
        thread.start();
        
        // 主线程休眠3秒后中断子线程
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt(); // 中断线程

通过线程实例,我们可以获取和设置线程的各种属性,如:

  • 线程名称:getName()/setName()
  • 线程 ID:getId()(只读)
  • 线程优先级:getPriority()/setPriority()
  • 线程状态:getState()(只读)
  • 是否为守护线程:isDaemon()/setDaemon()

总结

Thread类是 Java 多线程编程的基础,本文介绍了其最常用的功能:

  • 线程创建:继承Thread类或实现Runnable接口
  • 线程中断:通过interrupt()设置中断标志,isInterrupted()判断中断状态
  • 线程等待:使用join()方法等待其他线程完成
  • 线程休眠:使用sleep()方法暂停线程执行
  • 获取线程实例:通过currentThread()获取当前线程

掌握这些基本用法是进行多线程编程的基础,但在实际开发中,还需要注意线程安全、资源竞争等问题。对于复杂的并发场景,Java 并发包(java.util.concurrent)提供了更强大的工具和框架,如线程池、Callable、Future等,可以进一步学习和使用。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐