在 Java 刚诞生时,Thread 类就已经有了不少方法,但这些方法因为一些缘由(有一些明显的bug或者设计不合理)有些已经废弃了,可是他们的方法名倒是很是的好,真的是浪费。咱们在进行并发必编程的时候必定要注意这些。java
JDK 源码:面试
该方法被定义了 @Deprecated 注解,并在注释中说明了为何废弃:编程
该方法具备固有的不安全性。用 Thread.stop 来终止线程将释放它已经锁定的全部监视器(做为沿堆栈向上传播的未检查 ThreadDeath 异常的一个天然后果)。若是之前受这些监视器保护的任何对象都处于一种不一致的状态,则损坏的对象将对其余线程可见,这有可能致使任意的行为。stop 的许多使用都应由只修改某些变量以指示目标线程应该中止运行的代码来取代。目标线程应按期检查该变量,而且若是该变量指示它要中止运行,则从其运行方法依次返回。若是目标线程等待很长时间(例如基于一个条件变量),则应使用 interrupt 方法来中断该等待。api
很官方对不对?仍是用楼主的话来解释一下吧。最重要的缘由就i是 stop 太粗鲁了,强行把执行到一半的线程终止,引发数据不一致。好比有些数据处理到一半,该方法就强行中止线程,致使数据不一致。安全
可以使用一个条件判断来代替此功能,好比设置一个变量,若是这个变量是ture 则跳出循环,结束线程的执行。反正不要使用该方法就对了。并发
JDK 源码: 函数
这两个方法都被标注为过时,楼主解释一下为何不能使用。测试
suspend 方法的做用是挂起方法,而 resume 方法的做用是继续执行,能够说这两个方法是对应的,先挂起,而后继续执行。这两个动做是相反的。可是为何不建议使用呢?缘由就彷佛 suspend 方法在致使线程暂停的同时,并不会释听任何锁资源。此时,其余任何线程想要访问被他暂用的锁时,都会被牵连,致使没法正常运行。知道对应的 resume 方法被调用,被挂起的线程才能继续。可是,请注意,这里严格要求 resume 方法在 suspend 方法后面执行,若是 resume 方法意外的在suspend 方法以前执行了,就会致使死锁,该线程拥有不会恢复。spa
最坑的是,当产生死锁的时候,你确定会使用 jps 命令和 jstack 命令去查看死锁。可是你会发现你根本找不到,由于这个线程的状态是 Rannable。你根本没法判断是哪一个线程被挂起了,因此,该方法必定要废弃。命令行
好比楼主写了一个例子:
package cn.think.in.java.two;
public class BadSuspend {
static Object u = new Object();
static ChangeObjectThread t1 = new ChangeObjectThread("t1");
static ChangeObjectThread t2 = new ChangeObjectThread("t2");
static class ChangeObjectThread extends Thread {
public ChangeObjectThread(String name) {
super.setName(name);
}
public void run() {
synchronized (u) {
System.out.println("in " + getName());
// 暂停
Thread.currentThread().suspend();
}
}
}
public static void main(String[] args) throws InterruptedException {
t1.start();
Thread.sleep(100);
// 此时 t1 已经暂停
t2.start();
// t1 恢复
t1.resume();
// t2 这时恢复,可是 t2在恢复以后进入了暂停,致使死锁。
// 除非使用 sleep 让 t2 先暂停就能够。
// Thread.sleep(100);
t2.resume();
t1.join();
t2.join();
}
}
复制代码
该方法会发生死锁。然而咱们在命令行中使用 jstack 命令查看时,会发现该线程状态是 Rannable。
所以在之后的并发编程必定不要使用该方法。
关于线程中断还有3个方法:
public void interrupt() public boolean isInterrupted() public static boolean interrupted() 复制代码
public void interrupt() 做用:中断线程,也就是设置中断标记,注意,是设置标记,不会中断。 public boolean isInterrupted() 做用:判断线程是否中断 static boolean Thread interrupted 做用:判断是否中断,并清除当前中断状态。
咱们解释解释这三个方法: 在 java 中,线程中断是一种重要的线程协做机制。能够用来代替 stop方法,严格来说, 线程中断并不会使线程当即退出, 而是给线程发一个通知,告知目标线程,有人但愿你退出了。而何时退出,彻底由线程本身自行决定,避免了stop 的问题。可是该方法只是设置标记,因此须要本身判断状态而后跳出循环之类的结束线程运行。
那么咱们怎么使用这三个方法进行并发编程呢?下面楼主写了一个例子:
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (; ; ) {
}
});
t1.start();
Thread.sleep(2000);
// 不会起任何做用,因此须要判断他的中断位状态
t1.interrupt();
}
复制代码
该测试方法在死循环了一个线程,而后启动 interrupt 方法,根本不会起任何做用,因此各位不要这样使用该方法。那么如何使用呢?示例代码以下:
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (; ; ) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("interrupt");
break;
}
Thread.yield();
}
});
t1.start();
Thread.sleep(2000);
t1.interrupt();
}
复制代码
使用 isInterrupted 方法进行判断,若是返回 ture ,表示有中断标记,那么则 break 循环。结束运行。
还有一个须要注意的地方就是,若是线程在 sleep 或者 wait 状态,若是你调用 interrput 方法就会致使InterruptedException 异常,可是,抛出异常时会清除中断标记,所以,线程也就中断不了了,若是你想在异常后仍然中断线程,那么你须要在 catch 中 继续设置状态,也就是调用 interrupt 方法。咱们来个例子看看:
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (; ; ) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("interrupt");
break;
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
System.err.println("Interrupt When Sleep");
// 因为在 sleep 之间中断线程致使抛出异常,此时,他会清楚中断位,因此须要在这里从新设置中断位,下次循环则会直接判断中断标记,从而break。
Thread.currentThread().interrupt();
// 该方法会清除中断状态,致使上面的一行代码失效
// boolean isInterrupt = Thread.interrupted();
// System.out.println(isInterrupt);
}
Thread.yield();
}
});
t1.start();
Thread.sleep(1000);
t1.interrupt();
}
复制代码
运行结果:
interrupt Interrupt When Sleep
该测试方法中,在线程中调用了 sleep 方法,并在 main 线程中调用了 interrupt 方法,所以致使该线程异常,可是,若是咱们不在 catch 中从新设置中断位,该线程永远不会中止。这个时须要注意的。
还有一个静态方法,Thread.interrupted(),其实咱们上面的例子也测试了,该方法会返回线程是否中断,而且会清除状态,使用的时候须要注意。
JDK 源码:
该方法注释写到:等待该线程直到死。。。。还真是痴情啊。说正经的的。该方法实际上时等待线程结束。说明意思呢?
假如你有2个线程,A线程在算 1+1 ,而B线程须要 A线程算出的结果,那么B线程就须要等待A线程,那么这时候,B线程就须要调用 A线程的 join 方法,调用该方法后, B线程就会被挂起,直到A线程死亡,B线程才会被唤醒。实际上,若是看 join 的源码,会发现内部调用了A线程的 wait 方法。也就是说,B 线程 wait 在了 A 线程上。A 线程执行完毕会调用 notifyAll 方法,唤醒B线程。
咱们写个demo:
package cn.think.in.java.two;
public class JoinTest {
static int i;
public static void main(String[] args) throws InterruptedException {
AddThread addThread = new AddThread();
addThread.start();
// 主函数等待 addThread
// join 的本质是调用了 wait方法,让调用线程 wait 在当前线程对象实例上。也就是main线程 wait 在 addThread 线程实例上。
// 当 addThread 执行结束后,会调用 notifyAll 方法,注意,不要再程序中调用线程的 wait 或者 notify 方法,
// 可能会影响系统API 的工做。
addThread.join();// 重载方法 join(long) 若是达到给定的毫秒数,则不等了
System.out.println(i);
}
static class AddThread extends Thread {
public void run() {
for (; i < 10000000; i++) {
}
}
}
}
复制代码
该测试方法运行了一个对变量 i 自增运算的线程,而且主线程在等待 addThread 线程执行完才打印 i 的结果。若是不使用 join , 那么 ,打印 i 的值永远会小于10000。
而 join 的内部实现,咱们刚刚说了,使用 wait 方法,咱们看看该方法:
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
复制代码
该方法时同步的,同时内部调用了自身的 wait 方法。注意:咱们最好不要调用线程的 wait 方法和 notify 方法,可能会致使系统 api 出现问题。
这个方法就比较简单了。这是一个静态方法,yield 谦让出CPU时间片;
yieid 会让出时间片,可是是随机的。若是你以为一个线程不是很重要,那就能够适当的调用该方法,给予其余线程更多的机会。
虽然用的不少,但有必要说一下,该方法不会释放当前线程的锁。面试中常有该问题,wait 方法和 sleep 方法有什么不一样,wait 方法会释放锁,sleep 方法不会释放锁。
仅当当前线程在指定的对象上保持监视器锁时,才返回 true。该方法旨在使程序可以断言当前线程已经保持一个指定的锁。 参数: obj - 用于测试锁所属权的对象 返回: 若是当前线程在指定的对象上保持监视器锁,则返回 true。
设置该线程的上下文 ClassLoader。上下文 ClassLoader 能够在建立线程设置,并容许建立者在加载类和资源时向该线程中运行的代码提供适当的类加载器。 首先,若是有安全管理器,则经过 RuntimePermission("setContextClassLoader") 权限调用其 checkPermission`方法,查看是否能够设置上下文 ClassLoader。该方法在违反 JDK 默认的类加载模型时能起到很大做用。
参数: 该线程的上下文 ClassLoader
抛出: SecurityException - 若是当前线程没法设置上下文 ClassLoader。