JAVA高并发学习笔记(二)

线程的操作

一、终止线程

通常情况下,一个线程在工作完成后就会终止,不需要手工去关闭。但是也存在一些服务线程,它们往往都不是正常终结的(比如无穷循环)。而如果想要关闭这类进程,我们可以使用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)和通知(notify)

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

运行结果为:

JAVA高并发学习笔记(二)_第1张图片

其中最后T1结束需要等T2的2秒执行完成之后,才会打印出来(可以观察时间,差了2000左右)

四、挂起(suspend)和继续执行(resume)

首先,挂起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)和谦让(yield)

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,可以调用这个方法,让它以后慢慢执行。

你可能感兴趣的:(JAVA)