当前位置: 主页 > JAVA语言

java 线程安全解决方案-jvm怎么解决线程死锁

发布时间:2023-02-08 16:10   浏览次数:次   作者:佚名

1、为什么Thread的stop函数被废弃了?

对于有过多线程开发经验的开发者来说,在开发过程中应该大多遇到过这样的需求,即在某些情况下,想要立即停止一个线程。

例如:在做Android APP开发的时候,在打开一个界面的时候,需要开一个线程去请求网络获取界面的数据,但是有时候因为网速很慢,用户没有耐心等待数据采集​​完成,关闭界面。 这时候应该立即停止该线程的Tasks,否则一般会出现内存泄漏,造成系统资源的浪费。 如果用户不停地打开和关闭界面,内存泄漏就会累积起来,最终导致内存溢出,APP崩溃。

可能有很多开发者使用过Thread的stop来停止线程。 当然这个函数确实可以停止线程,但是Java官方已经弃用了java 线程安全解决方案,不推荐使用。 为什么?

stop 是通过立即抛出 ThreadDeath 异常来停止线程。 这个异常抛出可能发生在任何时间点,包括在catch、finally等语句块中,但是这个异常不会导致程序退出(笔者仅Java8测试过)。 由于抛出异常,线程会释放它持有的所有锁,这很可能会导致线程安全问题。

由于以上2点,用这种方式停止线程是不安全的。

以下是stop(Java8)的源码:

 @Deprecated
 public final void stop() {
 SecurityManager security = System.getSecurityManager();
 if (security != null) {
 checkAccess();
 if (this != Thread.currentThread()) {
 security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
 }
 }
 // A zero status value corresponds to "NEW", it can't change to
 // not-NEW because we hold the lock.

jvm怎么解决线程死锁_怎么用多线程解决高并发_java 线程安全解决方案

if (threadStatus != 0) { resume(); // Wake up thread if it was suspended; no-op otherwise } // The VM can handle all thread states stop0(new ThreadDeath()); } private native void stop0(Object o);

上面源码中的关键代码是stop0(new ThreadDeath())函数,它是一个Native函数,传递的参数是ThreadDeath,是一个异常对象,由Native层抛给Java层,导致线程停止,但这个异常不会导致程序退出。

很多时候,为了保证数据安全,同步代码会写在线程中。 如果此时线程正在执行同步代码时调用stop,则会抛出异常,导致线程持有的锁全部被释放。 此时无法保证数据安全。 为了安全,可能会出现意想不到的乱序数据,需要释放的资源可能没有释放,造成内存泄漏。 所以不推荐使用 stop 来停止线程。

2.用Thread的interrupt结束线程

实际上,调用Thread对象的中断函数并不会立即中断线程,而只是将线程中断状态标志设置为true。 当线程运行并调用其阻塞函数(Thread.sleep、Object.wait、Thread.join等)时,函数调用后会不断轮询检查中断状态标志是否为真。 如果为真,则停止阻塞并抛出InterruptedException异常java 线程安全解决方案,同时重置中断状态标志; 如果为false,则继续阻塞,直到阻塞正常。 结束。

因此,可以利用这种中断机制来控制和结束线程的运行。 只要理解了机制,代码的实现其实还是比较简单的。

2.1. 结束不使用阻塞函数的线程

public class Main {
 public static void main(String[] args) {

jvm怎么解决线程死锁_怎么用多线程解决高并发_java 线程安全解决方案

InnerClass innerClass = new InnerClass(); Thread thread = new Thread(innerClass); thread.start(); long i = System.currentTimeMillis(); while (System.currentTimeMillis() - i < 10 * 1000) { thread.isAlive(); } thread.interrupt(); } static class InnerClass implements Runnable { @Override public void run() { System.err.println("start work"); while (!Thread.currentThread().isInterrupted()) { System.out.println("doing work"); } System.err.println("done work");

怎么用多线程解决高并发_java 线程安全解决方案_jvm怎么解决线程死锁

} } }

思路其实是通过isInterrupted判断线程是否处于中断状态,如果处于中断状态,则跳出正在执行的任务,让线程结束。

2.2. 使用阻塞函数结束线程

public class Main {
 public static void main(String[] args) {
 InnerClass innerClass = new InnerClass();
 Thread thread = new Thread(innerClass);
 thread.start();
 long i = System.currentTimeMillis();
 while (System.currentTimeMillis() - i < 10 * 1000) {
 thread.isAlive();
 }
 thread.interrupt();
 }

jvm怎么解决线程死锁_java 线程安全解决方案_怎么用多线程解决高并发

static class InnerClass implements Runnable { @Override public void run() { System.err.println("start work"); while (!Thread.currentThread().isInterrupted()) { System.out.println("doing work"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } } System.err.println("done work"); } } }

怎么用多线程解决高并发_jvm怎么解决线程死锁_java 线程安全解决方案

思路同2.1。 需要注意的是,当调用sleep函数触发InterruptedException时,需要在catch代码块中调用中断函数,使线程再次处于中断状态,使while循环条件为假,使线程跳出的循环,并结束运行。 如果不调用,while循环就是死循环,线程无法结束。

2.3. 关于Thread的静态函数interrupted和Thread的对象函数isInterrupted

先对比下两个函数的源码:

 public static boolean interrupted() {
 return currentThread().isInterrupted(true);
 }
 public boolean isInterrupted() {
 return isInterrupted(false);
 }
 /**
 * Tests if some Thread has been interrupted. The interrupted state
 * is reset or not based on the value of ClearInterrupted that is
 * passed.
 */
 private native boolean isInterrupted(boolean ClearInterrupted);

从源码可以看出,两个函数都调用了Native函数private native boolean isInterrupted(boolean ClearInterrupted);,前一个调用传递的参数为true,所以调用interrupted函数会检查线程中断状态标志是否为true ,还将中断状态标志重置为假。 isInterrupted函数只检测线程中断状态标志。