Thread
一个线程就是一个执行流程. 每个线程都可以按照顺序执行自己的代码, 而多个线程可以同时执行多份代码
1.生命周期
Thread有六个生命周期
- 新建(NEW)
当一个Thread对象被创建时
,它处于新建状态。在这个阶段,线程还没有启动。
public static void main(String[] args) throws Exception{
System.out.println("Thread State is:"+new Thread().getState());
}
- 就绪(Runnable)
当调用线程对象的start()
方法后,线程进入就绪状态。在就绪状态下,线程已经准备好运行,但还没有得到 CPU 时间片。
public static void main(String[] args) {
new Thread(() -> {
System.out.println("Thread State is:"+Thread.currentThread().getState());
}).start();
}
- 运行(Running)
线程获得CPU时间片
后,进入运行状态。在运行状态下,线程执行具体的任务代码。 - 阻塞(Blocked)
线程在某些情况下会由于某些原因放弃CPU时间片
,进入阻塞状态。例如,线程等待某个资源的释放,或者调用了sleep()方法。
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();//锁
BlockThread t1 = new BlockThread(lock,"T1");
BlockThread t2 = new BlockThread(lock,"T2");
t1.start(); //线程 T1开始运行
t2.start(); //线程 T2开始运行
Thread.sleep(100); //阻塞主线程,等待T1,T2抢锁
System.out.println("Thread T1 State is " + t1.getState()); //获取T1线程状态
System.out.println("Thread T2 State is " + t2.getState()); //获取T2线程状态
}
}
class BlockThread extends Thread {
private String name; //当前线程名称
private Object lock; //锁
public BlockThread(Object lock, String name) {
this.lock = lock;
this.name = name;
}
@Override
public void run() {
System.out.println("Thread " + name + " State is " + Thread.currentThread().getState());
synchronized (lock) {
System.out.println("Thread " + name + " hold the lock");
try {
System.out.println("Thread " + name + " State is " + Thread.currentThread().getState());
Thread.sleep(1000 * 10); //抢到锁的线程执行逻辑,这里用睡眠模拟
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread " + name + " release the lock");
}
}
}
- 等待(Waiting)
线程在等待某个条件的触发时
进入等待状态。例如,调用Object.wait()方法或者 Thread.join()方法。
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
WaitingThread t = new WaitingThread("T", lock);
t.start();
Thread.sleep(1000);
System.out.println("Thread T State is " + t.getState());
System.out.println("Thread "+Thread.currentThread().getName()+" State is " + Thread.currentThread().getState());
}
}
class WaitingThread extends Thread {
private Object lock;
public WaitingThread(String name, Object lock) {
super(name);
this.lock = lock;
}
@Override
public void run() {
System.out.println("Thread " + Thread.currentThread().getName()+" try to wait");
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 超时等待(Timed Waiting): 类似于等待状态,但有一个超时时间。例如,调用 Thread.sleep() 方法或者带有超时参数的 Object.wait() 方法。
- 终止(Terminated)
线程执行完毕或者因异常退出时,进入终止状态。线程一旦终止,就不能再进入其他状态。
阻塞和等待的区别
阻塞状态 | 等待状态 | |
---|---|---|
原因 | 当线程试图获得一个对象的锁, 但该锁已经被其他线程持有时, 线程会进入阻塞状态 | 当线程在某个对象上调用Object.wait()方法时, 它会进入等待状态, 等待期间会释放对象的锁 |
解除 | 当持有锁的线程释放锁时, 被阻塞的线程有机会重新竞争锁, 并进入就绪状态 | 线程可以通过调用Object.notify()或Object.notifyAll()来被唤醒,也可以在等待期间等待超时 |
是否会释放持有的锁 | 在阻塞状态下, 线程仍然保持对锁的所有权 | 在等待状态下, 线程会释放锁, 让其他线程有机会获得锁 |
阻塞状态结束后, 线程重新进入就绪状态,
在等待状态中, 线程可以被唤醒, 被唤醒的线程将重新进入就绪状态, 并尝试获取对象的锁
2.构造方法
Thread() :创建一个默认设置的线程对象实例
Thread(Runnable target) :创建一个包含可执行对象的线程实例
Thread(Runnable target, String name) :创建一个包含可执行对象,指定名称的线程对象
Thread(String name):创建一个指定名称的线程对象
Thread(ThreadGroup group, Runnable target) :创建一个指定线程组,包含可执行对象的线程对象实例
Thread(ThreadGroup group, Runnable target, String name) :创建一个指定线程组,包含可执行对象,指定线程名称的线程对象实例
Thread(ThreadGroup group, Runnable target, String name, long stackSize) :创建一个指定线程组、包含可执行对象、指定名称以及堆栈大小的线程对象实例
Thread(ThreadGroup group, String name):创建一个指定线程组,线程名称的线程实例
3.创建线程的方式
1.继承Thread类
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
// 继承Thread
MyThread thread1 = new MyThread();
thread1.start();
// 匿名内部类
Thread thread2 =new Thread() {
@Override
public void run() {
System.out.println("Hello World 2");
}
};
thread2.start();
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Hello World");
}
}
2.实现Runnable接口
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
// 继承Thread
Thread thread1 = new Thread(new MyRunnable());
thread1.start();
// 匿名内部类
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello World 2");
}
});
thread2.start();
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Hello World");
}
}
3.实现Callable接口
FutureTask 是一个类,它实现了 Runnable 和 Future 接口
Callable不能直接交给Thread来运行, 我们可以使用FutureTask包装Callable, 让它变成一个Runnable
public class FutureTaskThreadDemo {
public static void main(String[] args) {
// 创建一个实现 Callable 接口的匿名类实例,该实例在 call() 方法中返回一个字符串
Callable<String> callableTask = new Callable<String>() {
@Override
public String call() throws Exception {
// 在这里执行你的任务,并返回结果
String result = "Hello from Callable!";
return result;
}
};
// 使用 Callable 任务创建一个 FutureTask 实例
FutureTask<String> futureTask = new FutureTask<>(callableTask);
// 创建一个新线程,并将 FutureTask 作为参数传递给它
Thread taskThread = new Thread(futureTask);
// 启动新线程,这将执行 FutureTask 中的 Callable 任务
taskThread.start();
try {
// 从 FutureTask 对象中获取计算结果,如果任务尚未完成,此方法会阻塞等待
String result = futureTask.get();
System.out.println("Result: " + result);
} catch (InterruptedException e) {
// 当前线程在等待过程中被中断时抛出 InterruptedException
e.printStackTrace();
} catch (ExecutionException e) {
// 计算任务抛出异常时抛出 ExecutionException
e.printStackTrace();
}
}
}
4.常用的方法
getId() 获取线程的ID
getName() 获取线程的名称
getState() 获取线程的状态
getPriority() 获取线程的优先级
isDaemon() 是否为后台线程
isAlive() 判断线程是否存活
interrupt() 中断线程
isInterrupted() 判断线程是否被中断
join() 让一个线程等待另一个线程执行结束
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
int count = 5;
while (count-- != 0) {
System.out.println("子线程");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "thread-0");
//设置线程为后台线程
thread.setDaemon(true);
thread.start();
System.out.println("线程的ID(JVM):"+thread.getId());
System.out.println("线程的名字:"+thread.getName());
//TimeUnit.SECONDS.sleep(1);
System.out.println("线程的状态:"+thread.getState());
System.out.println("线程的优先级:"+thread.getPriority());
System.out.println("线程是否为后台线程:"+thread.isDaemon());
//thread.interrupt();
System.out.println("线程是否被打断:"+thread.isInterrupted());
System.out.println("线程是否存活:"+thread.isAlive());
thread.join();
System.out.println("线程是否存活:"+thread.isAlive());
}
什么是后台线程?
前台线程 | 后台线程 | |
---|---|---|
作用 | 通常用于执行与客户端的交互任务, 前台线程可以影响用户体验,因此它们的响应速度较高 | 用于执行不需要用户直接参与或感知的任务, 通常用于处理一些较为耗时的操作,如数据加载、文件处理等 |
是否阻塞 | 通常用于执行与客户端的交互任务, 前台线程可以影响用户体验,因此它们的响应速度较高 | 用于执行不需要用户直接参与或感知的任务, 通常用于处理一些较为耗时的操作,如数据加载、文件处理等 |
生命周期 | 启动和结束通常与应用程序的启动和结束相一致 | 生命周期通常独立于应用程序的生命周期 |
线程优先级有什么用?
用于告诉操作系统调度器在多个线程可运行时, 应该选择哪个先执行
具有较高优先级的线程在竞争CPU时更有可能被调度执行。高优先级的线程更有可能更早地得到执行,因此可以更快地响应某些事件。这对于实时系统和需要低延迟响应的应用程序非常重要
什么情况下会使用interrupt方法
- 优雅的终止线程
通过调用 interrupt() 方法,可以通知目标线程应该中断并终止执行。线程在检测到中断状态后可以采取适当的措施,例如释放资源、完成未完成的任务等。 - 中断阻塞操作
如果线程在执行阻塞操作(如 sleep()、wait()、join() 等)时,可以通过调用 interrupt() 来中断线程,以提前结束阻塞状态,使线程能够响应中断并采取相应措施。 - 线程间通信
interrupt() 方法可以用作线程间的一种通信机制。当一个线程希望通知其他线程进行某种操作时,可以通过中断其他线程来引起其注意。 - 检查中断状态
线程可以通过调用 isInterrupted() 或 Thread.interrupted() 来检查自己是否被中断,并相应地进行处理。这可以帮助线程在合适的时候做出决策,例如提前结束执行或执行一些清理工作。 - 线程池关联
在使用线程池时,interrupt() 方法可以用于中断某个任务的执行。线程池可以通过 shutdownNow() 方法中断所有线程池中的任务,这将调用每个任务的 interrupt() 方法。
interrupt()和stop()方法的区别
interrupt和stop方法都是用来中断线程
的方法, 但它们在实现上有很大的不同, 在java中建议使用interrupt()而不是stop, 两者的区别如下
interrupt() | stop() | |
---|---|---|
作用 | Thread类的实例方法, 用于中断目标线程的执行 | Thread类的实例方法, 用于立即停止目标线程的执行 |
调用 | 将线程的中断状态设置为true | 会导致线程立即终止,不会有机会去执行清理工作或完成未完成的任务 |
interrupt()是否会中断目标线程的执行
- 如果线程处于阻塞状态(如 sleep()、wait()、join() 等)
它会抛出InterruptedException, 并清除中断状态 - 如果线程处于非阻塞状态
仅仅只是将线程的中断状态设置为true
interrupt() 方法主要是一种协作机制,通知线程应该中断并终止执行
join()方法的作用
作用是让一个线程等待另一个线程执行结束
如果线程A中调用了线程B的join()方法, 那么线程A将会等待线程B执行完成, 然后再继续执行
join方法有以下几个特点:
- 等待: 调用join的线程将阻塞, 直到被调用的线程执行完成
- 顺序执行: 如果在主线程中调用多个线程的join方法, 那么这些线程将按照调用join的顺序依次执行
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new MyRunnable(), "Thread-1");
Thread thread2 = new Thread(new MyRunnable(), "Thread-2");
// 启动线程1
thread1.start();
thread1.join();
// 启动线程2
thread2.start();
}
}
class MyRunnable implements Runnable {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
try {
Thread.sleep(100); // 模拟一些耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}