一、运行结果错误
例子: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")); } }
结果:
因为子线程运行是需要时间的,所以还没有初始化完成就拿来使用,肯定会空指针异常(不稳定)