多线程知识点目录
多线程并发(1)- //www.greatytc.com/p/8fcfcac74033
多线程并发(2)-//www.greatytc.com/p/a0c5095ad103
多线程并发(3)-//www.greatytc.com/p/c5c3bbd42c35
多线程并发(4)-//www.greatytc.com/p/e45807a9853e
多线程并发(5)-//www.greatytc.com/p/5217588d82ba
多线程并发(6)-//www.greatytc.com/p/d7c888a9c03c
四、终止线程的4种方式
4.1 正常运行结束
程序运行结束,线程自动结束。
4.2 使用退出标志退出线程
通过一个变量来控制循环,例如:最直接的方法就是设一个boolean类型的标志,并通过设置这个标志为true或false来控制while循环是否退出。
class ThreadSafe1 extends Thread{
// 退出标识(volatile关键字保证同一时刻只能由一个线程来修改其值)
public volatile boolean exitFlag = false;
// 当 exit 为 true 时,while 循环退出
public void run(){
while (!exitFlag){
System.out.println("Thread running ...");
}
}
}
4.3 Interrupt方法结束线程
使用interrupt()方法来中断线程有两种情况:
线程处于阻塞状态
如使用了sleep、同步锁的wait、socket中的receiver、accept等方法时,会使线程处于阻塞状态。当调用线程的interrupt()方法时,会抛出InterruptExcption异常。阻塞中的那个方法抛出这个异常,通过代码捕获该异常,然后break跳出循环状态,从而让我们有机会结束这个线程的执行。
通常认为只要调用interrupt方法线程就会结束,实际上是错的,一定要先捕获InterruptException异常之后,通过break来跳出循环,才能正常结束run方法。线程未处于阻塞状态
使用isInterrupted()判断线程的中断标志来退出循环。当使用interrupt()方法时,中断标志会置true,和使用自定义的标志来控制循环是一样的道理。
class ThreadSafe2 extends Thread {
public void run() {
// 非阻塞状态通过终端标志来退出
while (!isInterrupted()) {
try {
Thread.sleep(5000);
// 阻塞过程捕获中断异常来退出
} catch (InterruptedException e) {
e.printStackTrace();
// 捕获异常后,执行break跳出循环
break;
}
}
}
}
4.4 Stop方法终止线程(线程不安全)
Thread.stop()的工作原理:这个方法会强制终止线程,就像突然关闭计算机电源一样,而不是像正常程序那样进行有序的关机。当 Thread.stop() 被调用时,它会立即终止线程,不论该线程是否处于执行状态。
然而,这种立即的终止可能会导致一些问题。比如,如果线程正在执行一个包含加锁的代码块(通常是为了保护数据的一致性),然后这个线程被 Thread.stop() 强制终止,那么这个锁会被立即释放。这可能导致其他线程在访问这些被保护的数据时,数据已经处于不一致的状态,从而引发应用程序错误。
此外,当一个线程被 Thread.stop() 终止时,创建这个线程的线程会抛出 ThreadDeath 错误。这可能会给开发者带来困扰,因为他们可能需要在捕获和处理这种错误的情况下,重新设计他们的应用程序。
因此,不推荐使用stop方法来终止线程。
一般推荐使用更安全的方法来结束线程。例如,你可以设置一个标志位来让线程自行决定何时结束执行,而不是由外部强制终止。这样,线程就可以在适当的时间和地点释放资源并结束执行,同时不会破坏数据的一致性。
class ThreadSafe3 {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
// 模拟线程执行任务
try {
System.out.println("Thread running ... ");
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("catch InterruptedException ... ");
// 恢复中断状态
Thread.currentThread().interrupt();
break;
}
}
}
});
thread.start();
// 等待一段时间后终止线程
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 中断线程
System.out.println("Begin thread.stop() ... ");
try {
thread.stop();
} catch (ThreadDeath e) {
// 捕获并处理ThreadDeath异常
e.printStackTrace();
}
System.out.println("End thread.stop() ... ");
}
}
五、Sleep和Wait的区别
- Sleep 方法是在Thread类中定义的,它会导致当前线程暂停执行指定的时间。这个方法是public的,可以被任何线程调用。sleep方法通常用于让线程暂停执行一段时间,以实现某些时间延迟或定时操作。
Thread.sleep(1000); // 暂停当前线程1秒钟
- Wait方法是在Object类中定义的,它是synchronized方法,通常用于线程间的协作。当一个线程调用对象的wait方法时,它会释放该对象的锁,使得其他线程可以获取该对象的锁并访问它。然后,该线程会进入等待状态,直到其他线程调用同一个对象的notify或notifyAll方法来唤醒它。
synchronized (someObject) {
while (<condition does not hold>) {
someObject.wait();
}
}
-
主要区别
sleep方法会导致线程进入阻塞状态,不能响应任何中断或异常,直到指定的时间过去后才会重新进入就绪状态。它不会释放任何锁。
wait方法会导致线程进入等待状态,并且会释放对象的锁,使得其他线程可以访问该对象。它可以被中断或超时异常打断,并且可以响应中断。
sleep方法是针对当前线程的,而wait方法是针对对象本身的。在多线程环境中,wait方法常用于实现线程间的协作和同步。
通常,在编写多线程程序时,使用wait和notify方法进行线程间的通信和同步比使用sleep方法更安全和可控。
六、Start和Run的区别
- 位置:run方法属于Thread类中的一个普通方法,而start方法是Thread类中的一个启动方法。
- 执行方式:直接调用run方法,会像普通方法一样在当前线程中顺序执行run方法的内容。而调用start方法会创建一个新的线程,并在新的线程中并行执行run方法的内容。
- 线程状态:当我们调用start()方法启动一个新线程时,该线程会进入就绪状态,等待JVM调度它和其他线程的执行顺序。而当我们直接调用run()方法时,则会在当前线程中执行,不会产生新的线程。
- 作用:run方法的作用是存放任务代码的,而start的方法的作用是启动线程,线程启动以后它会自动去执行run方法。
- 线程数量:run方法在执行过程中不会产生新的线程,而start方法在执行过程中会产生一个新线程。