通常情况下,一个线程在工作完成后就会终止,不需要手工去关闭。但是也存在一些服务线程,它们往往都不是正常终结的(比如无穷循环)。而如果想要关闭这类进程,我们可以使用Thread类提供的stop方法。但是,这个方法已经被标记为废弃方法,不建议使用。
为什么会被废弃? 因为在强制终止一个线程的时候,可能会出现操作只进行了一半,而被强制完成的情况。这样往往会出现数据错误等问题,是必须要避免的。
如果想要使用stop()方法,我们可以定义一个变量,通过这个变量来确定是否结束死循环。例如:
public class StopDemo {
public static class StopThread extends Thread{
volatile boolean stopFlag = false;
@Override
public void run() {
while (true){
if (stopFlag == true){
System.out.println("进程结束");
break;
}
synchronized (this){
// do something
System.out.println("做点什么事 " + stopFlag);
}
}
}
public boolean isStopFlag() {
return stopFlag;
}
public void setStopFlag(boolean stopFlag) {
this.stopFlag = stopFlag;
}
}
public static void main(String[] args) throws InterruptedException {
StopThread thread = new StopThread();
thread.start();
Thread.sleep(20);
thread.setStopFlag(true);
}
}
JAVA中的线程中断的实现,很类似于刚刚写的stop的处理方法。是使用interrupt()、isInterrupted()和Thread.interrupted()三个方法来实现的。interrupt()将当前线程设置为要中止的状态,但是不会直接去影响线程。如果需要退出,则需要线程自己进行退出的方法。
public class InterruptDemo {
public static class InterruptThread extends Thread{
@Override
public void run() {
while (true){
if (this.isInterrupted() == true){
System.out.println("进程结束"+this.isInterrupted());
break;
}
synchronized (this){
// do something
System.out.println("做点什么事 " + this.isInterrupted());
}
}
}
}
public static void main(String[] args) throws InterruptedException {
InterruptThread thread = new InterruptThread();
thread.start();
Thread.sleep(10L);
thread.interrupt();
}
}
这里需要注意的是,Thread.sleep()方法会抛出InterruptedException异常(wait方法也会有同样的情况),这是在sleep()方法执行的时候,如果调用了interrupt方法,就会抛出异常,然后把interrupt重置为false。所以如果这个时候需要结束的话,则要把interrupt再次置为true。
Thread.currentThread().interrupt();
wait()方法是属于Object类的,所以就是说所有的类都可以使用这个方法。但是使用的前提对象必须包含在对应的synchronzied语句中,无论是wait()还是notify()都需要首先获得目标对象的一个监视器。
执行wait后,会把这个对象放入等待队列中,直到对象的notify()或者notifyAll()被调用后才会方开。
而如果有两个线程A和B的时候,他们监视同一个对象(obj)。假如A先调用了obj.wait(),之后B开始执行,并且调用的obj.notify()。假设这个时候放开的是A,那么A也不会立刻去继续执行,而是要等待可以获取到obj监视器(此时obj监视器由B在使用,必须等B使用完后,A才可以继续执行)。例如下方的例子:
public class WaitNotifyDemo {
final static Object obj = new Object();
public static class T1 extends Thread{
@Override
public void run() {
synchronized (obj){
System.out.println(System.currentTimeMillis()+":T1 开始");
try {
System.out.println(System.currentTimeMillis()+":T1等待obj释放");
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis()+":T1结束");
}
}
}
public static class T2 extends Thread{
@Override
public void run() {
synchronized (obj){
System.out.println(System.currentTimeMillis()+":T2开始,释放一个obj");
obj.notify();
System.out.println(System.currentTimeMillis()+":T2结束");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
T1 t1 = new T1();
T2 t2 = new T2();
t1.start();
Thread.sleep(100);
t2.start();
}
}
运行结果为:
其中最后T1结束需要等T2的2秒执行完成之后,才会打印出来(可以观察时间,差了2000左右)
首先,挂起suspend和继续执行resume 在API中都已经置为被放弃的方法,原因是假如因为意外resume在suspend之前被执行,那么就会出现类似于死锁的状态,线程永久的被挂起。而且最严重的是,这个线程的状态还被置为RUNNABLE,更是加大了BUG排查的难度。而造成这种现象的原因是因为suspend在导致线程暂停的时候,并不会释放任何的资源。而wait()则可以正确的释放资源,不会出现这种情况。
举个错误例子:
public class SuspendResumeDemo {
final static Object obj = new Object();
public static class ChangeObjectThread extends Thread{
public ChangeObjectThread(String name) {
super.setName(name);
}
@Override
public void run() {
synchronized (obj){
System.out.println("in " + getName());
Thread.currentThread().suspend();
}
}
}
private static ChangeObjectThread t1 = new ChangeObjectThread("t1");
private static ChangeObjectThread t2 = new ChangeObjectThread("t2");
public static void main(String[] args) throws InterruptedException {
t1.start();
Thread.sleep(100);
t2.start();
System.out.println("继续执行t1.resume();");
//直到这个时候,t1的挂机才被结束,而由于是并行原因,t2会在resume之后才执行suspend(),所以形成了死锁。
t1.resume();
System.out.println("继续执行t2.resume();");
t2.resume();
t1.join();
t2.join();
}
}
而解决的办法则是通过一个全局的变量和wait配合使用即可。
join()方法表示会阻塞当前线程,去执行调用join()的线程。这样可以让线程插队并且顺序执行。
例如:
public class JoinDemo {
public volatile static int i = 0;
public static class AddThread extends Thread{
@Override
public void run() {
for(i=0;i<1000000;i++);
}
}
public static void main(String[] args) throws InterruptedException {
AddThread addThread = new AddThread();
addThread.start();
// addThread.join(); //使用join()方法的结果:1000000
// addThread.join(10); //使用join(10)方法的结果:822897(变动)
//不使用任何join()方法的结果:0
System.out.println(i);
}
}
同时join方法在执行的时候,如果修改了当前线程的interrupt(),也会抛出InterruptedException异常。
而yeild()方法则是说当前线程会将CPU使用权释放,让给别人使用。但是不代表它不继续使用了,只不过把优先级降低。通常一些重要功能完成后,剩余的功能如果担心占用CPU,可以调用这个方法,让它以后慢慢执行。