多线程之两阶段终止、以及临界问题

两阶段终止问题

什么是优雅的终止

即如何在线程t1中优雅的终止线程t2,优雅是指给t2线程处理自身关键内容的机会而不是直接强制终止。

错误思路

stop() 方法会真正杀死线程,如果此时线程锁住了共享资源,那么当它被杀死后,没有机会释放锁,则其他线程将永远无法获得锁。

所以比较推荐的方法,是需要终止线程时,给线程发送interrupt(),并且线程判断IsInterrupted()来判断是不是发送了打断指令。这里需要注意的是,线程在sleep()的时候,执行interrupt()会抛出异常,但是isInterrupted()仍然是false,所以,要注意捕获这个异常,并且对应终止线程。

以上提到思路,更适用于线程while(true)时终止的情况,如果正常执行,那么更希望线程可以正常运行完成。

多线程上下文切换的问题

临界区

一个程序运行多个线程本身是没有问题的,问题出现在多个线程访问一个共享资源

多个线程读共享资源也是没有问题的,但是多个线程对共享资源读写操作时,因为时间片切换的问题,会发生指令交错,就会出现问题

一段代码块内,如果存在对共享资源的多线程读写操作,称这段代码块为临界区。

竞态条件

多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件。

应用互斥

为了避免临界区的竞态条件发生,有多种方法可以达到目的。

public class test {
    static int r =0;
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    r++;
                }

            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    r--;
                }
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
      System.out.println("r="+r);
    }
}



结果为
r=-423
r=-615。。。。。。

这个代码就是多个线程读写了共享的资源 r
因为r++和r--操作对应的底层代码有取数加1存数的操作所以在执行到中途时时间片可能用完而切换为另一个线程而线程保存当前的执行结果如线程t1 执行到第二步r=200然后等待存数这时t2执行r--r应该为199但是在切换t1时继续执行r=200的存数操作导致出错

改进:

public class test {
    static int r = 0;
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    synchronized (object) {
                        r++;
                    }
                }

            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    synchronized (object) {
                        r--;
                    }
                }
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("r=" + r);
    }
}

其实synchronized要求锁住一个对象即可,在这段代码中,只要保证线程1和线程2之间由于锁住同一个对象,从而导致互斥即可,所以这里声明了一个object对象,使t1和t2形成互斥即可。