多线程核心8-1:线程三大安全问题之运行结果错误

两个线程导致的错误

  • 两个线程进行index++操作,两个线程会相互干扰,导致结果不一定准确,现在想找出出错的位置和出错次数

    public class MultiThreadError1 implements Runnable {
          
          
    
        static MultiThreadError1 instance = new MultiThreadError1();
        int index = 0;
        public static void main(String[] args) throws InterruptedException {
          
          
            Thread thread1 = new Thread(instance);
            Thread thread2 = new Thread(instance);
            thread1.start();
            thread2.start();
            thread1.join();
            thread2.join();
            System.out.println(instance.index);
        }
    
        @Override
        public void run() {
          
          
            for (int i = 0; i < 10000; i++) {
          
          
                index++;
            }
        }
    }
    

改造过程

1、标记

  • 改造思路:先用一个标记数组来 marked[index] 将index存起来,如果一个线程已经操作了index++,另一个就不要再进行了,结合原子整型来获得真实执行次数

    public class MultiThreadError1 implements Runnable {
          
          
    
        static MultiThreadError1 instance = new MultiThreadError1();
        int index = 0;
        final boolean marked[] = new boolean[100000];
    
        static AtomicInteger realIndex = new AtomicInteger();
        static AtomicInteger errorCount = new AtomicInteger();
    
        public static void main(String[] args) throws InterruptedException {
          
          
            Thread thread1 = new Thread(instance);
            Thread thread2 = new Thread(instance);
            thread1.start();
            thread2.start();
            thread1.join();
            thread2.join();
            System.out.println("表面"+instance.index);
            System.out.println("实际"+realIndex.get());
            System.out.println("错误"+errorCount.get());
        }
    
        @Override
        public void run() {
          
          
            for (int i = 0; i < 10000; i++) {
          
          
                index++;
                realIndex.incrementAndGet();
                if (marked[index]){
          
          
                    System.out.println("error: " + index);
                    errorCount.incrementAndGet();
                }
                //置为true表示已经进行了操作
                marked[index] = true;
            }
        }
    }
    
    

    在这里插入图片描述

  • 错误原因:在if (marked[index])位置发生错误,例如程序开始运行,线程1执行run,执行完index++后,index变为1,这时marked[1]为false,于是要将marked[1]置为true,但还未来得及执行时;切换到了线程2,依然执行index++,index变为1,但线程1本来要将index为1标记,这时线程2检测不到,依然往下执行,于是发生错误

2、同步

  • 发生了这个错误之后,便想用synchronized来解决,使不同线程间同步访问

    public class MultiThreadError1 implements Runnable {
          
          
    
        static MultiThreadError1 instance = new MultiThreadError1();
        int index = 0;
        final boolean marked[] = new boolean[100000];
    
        static AtomicInteger realIndex = new AtomicInteger();
        static AtomicInteger errorCount = new AtomicInteger();
    
        public static void main(String[] args) throws InterruptedException {
          
          
            Thread thread1 = new Thread(instance);
            Thread thread2 = new Thread(instance);
            thread1.start();
            thread2.start();
            thread1.join();
            thread2.join();
            System.out.println("表面"+instance.index);
            System.out.println("实际"+realIndex.get());
            System.out.println("错误"+errorCount.get());
        }
    
        @Override
        public void run() {
          
          
            for (int i = 0; i < 10000; i++) {
          
          
                index++;
                realIndex.incrementAndGet();
                synchronized (instance) {
          
          
                    if (marked[index]) {
          
          
                        System.out.println("error: " + index);
                        errorCount.incrementAndGet();
                    }
                    //置为true表示已经进行了操作
                    marked[index] = true;
                }
            }
        }
    }
    

    结果依然不对
    在这里插入图片描述

  • 这回冲突发生在index++位置了,线程1执行完index++,也将marked[1]置为true;轮到线程2执行,执行完index++,还没来得及进入同步代码块检查;又切回到线程1,执行index++,index为2;切到线程2,刚才还没来得及检查的1已经变成2了,发生错误。简单来说就是有的线程执行得太快了

3、限制

  • 这时要用一道道闸门来限制以下线程的速度,使用CyclicBarrier,加了闸门的地方要两个线程都到达了这个位置才能继续向下执行,在index++前面加上是防止有的线程执行太快,而在后面加上是防止一个线程还没来得及marked的时候,切到另一个线程执行index++,然后又切换回时,发生错误

    public class MultiThreadError1 implements Runnable {
          
          
    
        static MultiThreadError1 instance = new MultiThreadError1();
        int index = 0;
        final boolean marked[] = new boolean[100000];
    
        static AtomicInteger realIndex = new AtomicInteger();
        static AtomicInteger errorCount = new AtomicInteger();
    
        static CyclicBarrier cyclicBarrier1 = new CyclicBarrier(2);
        static CyclicBarrier cyclicBarrier2 = new CyclicBarrier(2);
    
        public static void main(String[] args) throws InterruptedException {
          
          
            Thread thread1 = new Thread(instance);
            Thread thread2 = new Thread(instance);
            thread1.start();
            thread2.start();
            thread1.join();
            thread2.join();
            System.out.println("表面"+instance.index);
            System.out.println("实际"+realIndex.get());
            System.out.println("错误"+errorCount.get());
        }
    
        @Override
        public void run() {
          
          
            for (int i = 0; i < 10000; i++) {
          
          
                try {
          
          
                    cyclicBarrier2.reset();
                    cyclicBarrier1.await();
                } catch (InterruptedException e) {
          
          
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
          
          
                    e.printStackTrace();
                }
                index++;
                try {
          
          
                    cyclicBarrier1.reset();
                    cyclicBarrier2.await();
                } catch (InterruptedException e) {
          
          
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
          
          
                    e.printStackTrace();
                }
                realIndex.incrementAndGet();
                synchronized (instance) {
          
          
                    if (marked[index]) {
          
          
                        System.out.println("error: " + index);
                        errorCount.incrementAndGet();
                    }
                    //置为true表示已经进行了操作
                    marked[index] = true;
                }
            }
        }
    }
    

    两道栅栏加完但还是错误
    在这里插入图片描述
    debug发现只有偶数位被置为true,因为在index++之后加了闸门,所以两个线程,每个执行一次index++,index从0开始,所以只有偶数位被置为true
    在这里插入图片描述

  • 事实上这并不是发生了线程冲突导致的,而是由于synchronized的可见性,线程能知晓相互之间的状态,线程1和线程2进入同步代码块后,都能看见偶数位被置为true,认为发生错误,其实并没有发生冲突,只是跳过了奇数位的标记,因此需要完善以下冲突判断条件marked[index] && marked[index - 1]

    synchronized (instance) {
          
          
                    if (marked[index] && marked[index - 1]) {
          
          
                        System.out.println("error: " + index);
                        errorCount.incrementAndGet();
                    }
                    //置为true表示已经进行了操作
                    marked[index] = true;
    }
    

完整代码

public class MultiThreadError1 implements Runnable {
    
    

    static MultiThreadError1 instance = new MultiThreadError1();
    int index = 0;
    final boolean marked[] = new boolean[100000];

    static AtomicInteger realIndex = new AtomicInteger();
    static AtomicInteger errorCount = new AtomicInteger();

    static CyclicBarrier cyclicBarrier1 = new CyclicBarrier(2);
    static CyclicBarrier cyclicBarrier2 = new CyclicBarrier(2);

    public static void main(String[] args) throws InterruptedException {
    
    
        Thread thread1 = new Thread(instance);
        Thread thread2 = new Thread(instance);
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println("表面"+instance.index);
        System.out.println("实际"+realIndex.get());
        System.out.println("错误"+errorCount.get());
    }

    @Override
    public void run() {
    
    
        for (int i = 0; i < 10000; i++) {
    
    
            marked[0]=true;
            try {
    
    
                cyclicBarrier2.reset();
                cyclicBarrier1.await();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
    
    
                e.printStackTrace();
            }
            index++;
            System.out.println(Thread.currentThread().getName()+" :cyclicBarrier前: "+index);
            try {
    
    
                cyclicBarrier1.reset();
                cyclicBarrier2.await();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
    
    
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" :cyclicBarrier后: "+index);
            realIndex.incrementAndGet();
            synchronized (instance) {
    
    
                if (marked[index] && marked[index - 1]) {
    
    
                    System.out.println("error: " + index);
                    errorCount.incrementAndGet();
                }
                //置为true表示已经进行了操作
                marked[index] = true;
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_44863537/article/details/112592869