java 线程安全解决方案-jvm怎么解决线程死锁
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.

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) {

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");

}
}
}
思路其实是通过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();
}

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");
}
}
}

思路同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函数只检测线程中断状态标志。

上一篇
