线程安全

一、运行结果错误

例子:a++多线程下会出现消失的现象

因为a++实际上有3个步骤,在多线程情况下如果线程1实现了求和但还没有写入结果就切换到了线程2,线程2执行玩后切回线程1,再执行写入但是这次写入的结果就是线程的结果,导致了2个线程的求和是同一结果,就出现了求和结果不正确

原因图解:

样例代码:

/**
 * 演示:线程安全问题1:运行结果错误
 *
 * @author wjh
 * @date 2020-03-07 11:41
 */
public class MultiThreadsError implements Runnable {

    int index = 0;

    final boolean[] marked = new boolean[100000];

    static MultiThreadsError error = new MultiThreadsError();
    static AtomicInteger realIndex = new AtomicInteger();
    static AtomicInteger wrongCount = new AtomicInteger();
    static volatile CyclicBarrier cyclicBarrier1 = new CyclicBarrier(2);
    static volatile CyclicBarrier cyclicBarrier2 = new CyclicBarrier(2);

    @Override
    public void run() {
        marked[0] = true;
        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 (error) {
                if (marked[index] && marked[index - 1]) {
                    System.out.println("发生线程冲突" + index);
                    wrongCount.incrementAndGet();
                }
                marked[index] = true;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread threadA = new Thread(error);
        Thread threadB = new Thread(error);
        threadA.start();
        threadB.start();
        threadA.join();
        threadB.join();
        System.out.println("表面上的结果" + error.index);
        System.out.println("真正运行的次数" + realIndex);
        System.out.println("出现错误的次数" + wrongCount);
    }
}

结果:


二、活跃性问题 

例子:死锁

代码:线程1拿到了lock1,线程2拿到了lock2,但是2个线程都还要拿到对方的锁才能完成工作,但都不释放自己锁,这就出现了死锁

/**
 * 死锁的演示
 *
 * @author wjh
 * @date 2020-03-07 12:35
 */
public class MultiThreadError implements Runnable {
    boolean flag = false;

    static Object lock1 = new Object();
    static Object lock2 = new Object();

    public static void main(String[] args) {
        MultiThreadError r1 = new MultiThreadError();
        MultiThreadError r2 = new MultiThreadError();
        r1.flag = false;
        r2.flag = true;

        new Thread(r1).start();
        new Thread(r2).start();
    }

    @Override
    public void run() {
        System.out.println("flag = " + flag);
        if (!flag) {
            synchronized (lock1) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (lock2) {
                    System.out.println("1");
                }
            }
        } else {
            synchronized (lock2) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1) {
                    System.out.println("0");
                }
            }
        }
    }
}

结果:


三、对象发布和初始化的时候的安全问题

 1、发布逸出

逸出:

  • 方法返回一个private对象(解决方法:返回副本
  • 还没有完全初始化,就把对象提供给外界(解决方法:工厂模式

1、在构造函数中未初始化完毕就this赋值

2、隐式逸出-注册监听事件

3、构造函数中运行线程


​​​​​​​2、演示

  • 方法返回一个private对象
/**
 * 发布逸出
 *
 * @author wjh
 * @date 2020-03-07 12:44
 */
public class MultiThreadError3 {
    private Map<String, String> states;

    public MultiThreadError3() {
        this.states = new HashMap<>();
        states.put("1", "周一");
        states.put("2", "周二");
        states.put("3", "周三");
    }

    public Map<String, String> getStates() {
        return this.states;
    }

    public static void main(String[] args) {
        MultiThreadError3 multiThreadError3 = new MultiThreadError3();

        Map<String, String> states = multiThreadError3.getStates();
        System.out.println(states.get("1"));
        states.remove("1");
        System.out.println(states.get("1"));
    }
}

 结果:是私有变量能在其他地方进行操作

 解决方法:返回副本

    public Map<String, String> getStatesImproved() {
        return new HashMap<>(this.states);
    }

  • 在构造函数中未初始化完毕就this赋值
/**
 * 演示未初始化完毕就this赋值
 *
 * @author wjh
 * @date 2020-03-07 12:58
 */
public class MultiThreadError4 {
    static Point point;

    public static void main(String[] args) throws InterruptedException {
        new PointMaker().start();
//        Thread.sleep(10);
        Thread.sleep(105);
        if (point != null) {
            System.out.println(point);
        }
    }
}

class PointMaker extends Thread {
    @Override
    public void run() {
        try {
            new Point(1, 1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Point {
    private final int x, y;

    public Point(int x, int y) throws InterruptedException {
        this.x = x;
        MultiThreadError4.point = this;
        Thread.sleep(100);
        this.y = y;
    }

    @Override
    public String toString() {
        return "Point{" +
                "x=" + x +
                ", y=" + y +
                '}';
    }
}

结果:出现不同时间获取point的值不同

等待10ms:

等待105ms:


  • 隐式逸出-注册监听事件
/**
 * @author wjh
 * @date 2020-03-07 13:06
 */
public class MultiThreadError5 {
    int count;

    public static void main(String[] args) throws InterruptedException {
        MySource mySource = new MySource();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                mySource.eventCome(new Event() {
                });
            }
        }).start();

        MultiThreadError5 multiThreadError5 = new MultiThreadError5(mySource);

    }

    public MultiThreadError5(MySource source) throws InterruptedException {
        source.registerListener(new EventListener() {
            @Override
            public void onEvent(Event e) {
                System.out.println("我的到的数字是:" + count);
            }
        });

        Thread.sleep(1000);

        count = 100;
    }

    static class MySource {
        private EventListener listener;

        void registerListener(EventListener eventListener) {
            this.listener = eventListener;
        }

        void eventCome(Event e) {
            if (listener != null) {
                listener.onEvent(e);
            } else {
                System.out.println("还未初始化完毕");
            }
        }
    }

    interface EventListener {
        void onEvent(Event e);
    }

    interface Event {

    }
}

结果:


  • 构造函数中运行线程
/**
 * 在构造函数中新开线程去初始化
 *
 * @author wjh
 * @date 2020-03-07 13:06
 */
public class MultiThreadError6 {
    private Map<String, String> states;

    public MultiThreadError6() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                states = new HashMap<>();
                states.put("1", "周一");
                states.put("2", "周二");
                states.put("3", "周三");
            }
        }).start();
    }

    public Map<String, String> getStates() {
        return this.states;
    }

    public static void main(String[] args) {
        MultiThreadError6 multiThreadError6 = new MultiThreadError6();

        Map<String, String> states = multiThreadError6.getStates();
        System.out.println(states.get("1"));
        states.remove("1");
        System.out.println(states.get("1"));
    }
}

结果:

因为子线程运行是需要时间的,所以还没有初始化完成就拿来使用,肯定会空指针异常(不稳定)


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值