深入浅出Java并发多线程(13)- 线程8大核心基础 -多线程导致的问题

本文探讨了Java多线程环境中的安全问题,包括a++操作的并发问题、死锁示例以及对象发布和初始化时的安全隐患。通过案例分析了构造函数未完成初始化、注册监听事件时的潜在风险以及在构造函数中创建线程的问题。同时,提出了相应的解决方案,如使用工厂模式确保对象完全初始化后再提供给外界。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 多线程安全问题

1.1 a++问题

见上篇

1.2 活跃性问题-死锁

  • 死锁案例
public class MultiThreadError implements Runnable {

    int flag = 1;

    static Object o1 = new Object();
    static Object o2 = new Object();

    @Override
    public void run() {
        System.out.println("falg= " + flag);
        if (flag == 1) {
            synchronized (o1) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (o2) {
                    System.out.println("1");
                }
            }
        }

        if (flag == 0) {
            synchronized (o2) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (o1) {
                    System.out.println("0");
                }
            }
        }
    }

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

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

    }
}

在这里插入图片描述

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

在这里插入图片描述


1.3.1 案例-构造函数没有初始化完毕


/**
 * @Description 初始化没有完毕,就this赋值
 * @Author tzb
 * @Date 2021/3/8 21:38
 * @Version 1.0
 **/
public class MultiThreadError4 {

    static Point point;

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

}

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 +
                '}';
    }

}

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

在这里插入图片描述

  • 修改
    在这里插入图片描述
    在这里插入图片描述

1.3.2 案例-注册监听事件

package background;

import javax.xml.transform.Source;
import java.util.EventListener;
import java.util.function.Predicate;


/**
 * @Description 观察者模式
 * @Author tzb
 * @Date 2021/3/8 21:53
 * @Version 1.0
 **/
public class MultiThreadError5 {

    int count;

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

        for (int i = 0; i < 10000; i++) {
            System.out.print(i);
        }

        count = 100;
    }

    public static void main(String[] args) {
        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);
    }


    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 {
    }

}

在这里插入图片描述
在这里插入图片描述
注册了监听器后,在 onEvent 方法中就已经隐含地暴露了外部类的对象,EvenListener是匿名内部类,它持有外部类的引用,可以对count进行操作。如果构造函数对count赋值没有完成,即便是注册监听器,也有安全隐患。

1.3.3 案例-构造函数新建线程

错误做法

  • 在构造函数拿到线程池的引用或者数据库的连接池,而创建连接池的时候都是在后台新开线程,

2 如何解决逸出

  • 返回副本,针对返回private对象的情况
    在这里插入图片描述

  • 工厂模式,针对未完成初始化就把对象提供给外界

/**
 * @Description 工厂模式修复初始化问题
 * @Author tzb
 * @Date 2021/3/9 22:42
 * @Version 1.0
 **/
public class MultiThreadError7 {

    int count;

    private EventListener listener;

    private MultiThreadError7(MySource source) {
        listener = new EventListener() {
            @Override
            public void onEvent(Event e) {
                System.out.println("我得到的数字是:" + count);
            }
        };

        for (int i = 0; i < 10000; i++) {
            System.out.print(i);
        }

        count = 100;
    }

    public static MultiThreadError7 getInstance(MySource source) {
        MultiThreadError7 safeListener = new MultiThreadError7(source);
        source.registerListener(safeListener.listener);
        return safeListener;
    }

    public static void main(String[] args) {
        MySource mySource = new MySource();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace