Java 多线程之间共享数据

 更新时间:2021年10月27日 15:33:18   作者:冬日毛毛雨  
这篇文章主要介绍了Java 多线程之间共享数据,围绕Java 多线程之间共享数据展开文章内容线程范围的共享变量、使用Map实现线程范围内数据的共享、ThreadLocal实现线程范围内数据的共享,需要的朋友可以参考一下

1、线程范围的共享变量

多个业务模块针对同一个static变量的操作 要保证在不同线程中 各模块操作的是自身对应的变量对象

public class ThreadScopeSharaData {

    private static  int data = 0 ;

    public static void main(String[] args) {
        for(int i = 0 ;i<2 ;i++){
            new Thread(new Runnable(){

                @Override
                public void run() {
                    data = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName()+ " put random data:"+data);
                    new A().get() ;
                    new B().get() ;
                }

            }).start() ;
        }

    }

    static class A {
        public int get(){
            System.out.println("A from " + Thread.currentThread().getName()
                    + " get data :" + data);
            return data ;
        }
    }

    static class B{
        public int get(){
            System.out.println("B from " + Thread.currentThread().getName()
                    + " get data :" + data);
            return data ;
        }
    }
}

模块A ,B都需要访问static的变量data 在线程0中会随机生成一个data值 假设为10 那么此时模块A和模块B在线程0中得到的data的值为10 ;在线程1中 假设会为data赋值为20 那么在当前线程下

模块A和模块B得到data的值应该为20

看程序执行的结果:

Thread-0 put random data:-2009009251
Thread-1 put random data:-2009009251
A from Thread-0 get data :-2009009251
A from Thread-1 get data :-2009009251
B from Thread-0 get data :-2009009251
B from Thread-1 get data :-2009009251


Thread-0 put random data:-2045829602
Thread-1 put random data:-1842611697
A from Thread-0 get data :-1842611697
A from Thread-1 get data :-1842611697
B from Thread-0 get data :-1842611697
B from Thread-1 get data :-1842611697

会出现两种情况:

  • 1.由于线程执行速度,新的随机值将就的随机值覆盖 data 值一样
  • 2.data 值不一样,但 A、B线程都

2、使用Map实现线程范围内数据的共享

可是将data数据和当前允许的线程绑定在一块,在模块A和模块B去获取数据data的时候 是通过当前所属的线程去取得data的结果就行了。
声明一个Map集合 集合的Key为Thread 存储当前所属线程 Value 保存data的值,

代码如下:

public class ThreadScopeSharaData {


    private static Map<Thread, Integer> threadData = new HashMap<>();

    public static void main(String[] args) {

        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    int data = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName() + " put random data:" + data);
                    threadData.put(Thread.currentThread(), data);
                    new A().get();
                    new B().get();

                }
            }).start();

        }

    }

    static class A {
        public void get() {
            int data = threadData.get(Thread.currentThread());

            System.out.println("A from " + Thread.currentThread().getName() + " get data:" + data);

        }
    }

    static class B {
        public void get() {
            int data = threadData.get(Thread.currentThread());
            System.out.println("B from " + Thread.currentThread().getName() + " get data:" + data);

        }
    }
}

Thread-0 put random data:-123490895
Thread-1 put random data:-1060992440
A from Thread-0 get data:-123490895
A from Thread-1 get data:-1060992440
B from Thread-0 get data:-123490895
B from Thread-1 get data:-1060992440

3、ThreadLocal实现线程范围内数据的共享

(1)订单处理包含一系列操作:减少库存量、增加一条流水台账、修改总账,这几个操作要在同一个事务中完成,通常也即同一个线程中进行处理,如果累加公司应收款的操作失败了,则应该把前面的操作回滚,否则,提交所有操作,这要求这些操作使用相同的数据库连接对象,而这些操作的代码分别位于不同的模块类中。

(2)银行转账包含一系列操作: 把转出帐户的余额减少,把转入帐户的余额增加,这两个操作要在同一个事务中完成,它们必须使用相同的数据库连接对象,转入和转出操作的代码分别是两个不同的帐户对象的方法。

(3)例如Strut2ActionContext,同一段代码被不同的线程调用运行时,该代码操作的数据是每个线程各自的状态和数据,对于不同的线程来说,getContext方法拿到的对象都不相同,对同一个线程来说,不管调用getContext方法多少次和在哪个模块中getContext方法,拿到的都是同一个。

(4)实验案例:定义一个全局共享的ThreadLocal变量,然后启动多个线程向该ThreadLocal变量中存储一个随机值,接着各个线程调用另外其他多个类的方法,这多个类的方法中读取这个ThreadLocal变量的值,就可以看到多个类在同一个线程中共享同一份数据。

(5)实现对ThreadLocal变量的封装,让外界不要直接操作ThreadLocal变量。

  • 对基本类型的数据的封装,这种应用相对很少见。
  • 对对象类型的数据的封装,比较常见,即让某个类针对不同线程分别创建一个独立的实例对象。
public class ThreadLocalTest {

    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {

        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    int data = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName() + " put random data:" + data);
                    threadLocal.set(data);
                    new A().get();
                    new B().get();

                }
            }).start();

        }

    }

    static class A {
        public void get() {
            int data = threadLocal.get();

            System.out.println("A from " + Thread.currentThread().getName() + " get data:" + data);

        }
    }

    static class B {
        public void get() {
            int data = threadLocal.get();
            System.out.println("B from " + Thread.currentThread().getName() + " get data:" + data);

        }
    }
}

Thread-0 put random data:-2015900409
Thread-1 put random data:-645411160
A from Thread-0 get data:-2015900409
A from Thread-1 get data:-645411160
B from Thread-0 get data:-2015900409
B from Thread-1 get data:-645411160

4、优化

public class ThreadLocalTest {

    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();

    //private static ThreadLocal<MyThreadScopeData> myThreadScopeDataThreadLocal = new ThreadLocal<>();


    public static void main(String[] args) {

        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    int data = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName() + " put random data:" + data);
                    threadLocal.set(data);

//                    MyThreadScopeData myThreadScopeData = new MyThreadScopeData();
//                    myThreadScopeData.setName("name" + data);
//                    myThreadScopeData.setAge(data);
//                    myThreadScopeDataThreadLocal.set(myThreadScopeData);

                    //获取与当前线程绑定的实例并设置值  
                    MyThreadScopeData.getThreadInstance().setName("name" + data);
                    MyThreadScopeData.getThreadInstance().setAge(data);
                    new A().get();
                    new B().get();

                }
            }).start();

        }

    }

    static class A {
        public void get() {
            int data = threadLocal.get();


//            MyThreadScopeData myData = myThreadScopeDataThreadLocal.get();
//
//
//            System.out.println("A from " + Thread.currentThread().getName()
//                    + " getMyData: " + myData.getName() + "," + myData.getAge());

            MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
            System.out.println("A from " + Thread.currentThread().getName()
                    + " getMyData: " + myData.getName() + "," + myData.getAge());
        }
    }

    static class B {
        public void get() {
            int data = threadLocal.get();
            //System.out.println("B from " + Thread.currentThread().getName() + " get data:" + data);

            MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
            System.out.println("B from " + Thread.currentThread().getName()
                    + " getMyData: " + myData.getName() + "," + myData.getAge());
        }
    }
}

//一个绑定当前线程的类
class MyThreadScopeData {

    private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<>();
    private String name;
    private int age;

    private MyThreadScopeData() {
    }

    //定义一个静态方法,返回各线程自己的实例
    //这里不必用同步,因为每个线程都要创建自己的实例,所以没有线程安全问题。
    public static MyThreadScopeData getThreadInstance() {
        //获取当前线程绑定的实例
        MyThreadScopeData instance = map.get();
        if (instance == null) {
            instance = new MyThreadScopeData();
            map.set(instance);
        }
        return instance;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }


}

Thread-1 put random data:-1041517189
Thread-0 put random data:-98835751
A from Thread-1 getMyData: name-1041517189,-1041517189
A from Thread-0 getMyData: name-98835751,-98835751
B from Thread-1 getMyData: name-1041517189,-1041517189
B from Thread-0 getMyData: name-98835751,-98835751

5、实例

设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1,写出程序。

(1)如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,卖票系统就可以这么做。

public class SellTicket {
    //卖票系统,多个窗口的处理逻辑是相同的
    public static void main(String[] args) {
        Ticket t = new Ticket();
        new Thread(t).start();
        new Thread(t).start();
    }
}

/**
 * 将属性和处理逻辑,封装在一个类中
 *
 * @author yang
 */
class Ticket implements Runnable {

    private int ticket = 10;

    public synchronized void run() {
        while (ticket > 0) {
            ticket--;
            System.out.println("当前票数为:" + ticket);
        }
    }
}

(2)如果每个线程执行的代码不同,这时候需要用不同的Runnable对象,例如,设计2个线程。一个线程对j增加1,另外一个线程对j减1,银行存取款系统。

public class MultiThreadShareData {
    private int j;
    public static void main(String[] args) {
        MultiThreadShareData multiThreadShareData = new MultiThreadShareData();
        for(int i=0;i<2;i++){
            new Thread(multiThreadShareData.new ShareData1()).start();//增加
            new Thread(multiThreadShareData.new ShareData2()).start();//减少
        }
    }
    //自增
    private synchronized void Inc(){
        j++;
        System.out.println(Thread.currentThread().getName()+" inc "+j);
    }
    //自减
    private synchronized void Dec(){
        j--;
        System.out.println(Thread.currentThread().getName()+" dec "+j);
    }

    class ShareData1 implements Runnable {
        public void run() {
            for(int i=0;i<5;i++){
                Inc();
            }
        }
    }
    class ShareData2 implements Runnable {
        public void run() {
            for(int i=0;i<5;i++){
                Dec();
            }
        }
    }
}

Thread-0 inc 1
Thread-0 inc 2
Thread-0 inc 3
Thread-0 inc 4
Thread-0 inc 5
Thread-1 dec 4
Thread-1 dec 3
Thread-2 inc 4
Thread-2 inc 5
Thread-2 inc 6
Thread-2 inc 7
Thread-2 inc 8
Thread-1 dec 7
Thread-1 dec 6
Thread-1 dec 5
Thread-3 dec 4
Thread-3 dec 3
Thread-3 dec 2
Thread-3 dec 1
Thread-3 dec 0

到此这篇关于Java 多线程之间共享数据的文章就介绍到这了,更多相关Java 多线程共享数据内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java中List add添加不同类型元素的讲解

    Java中List add添加不同类型元素的讲解

    今天小编就为大家分享一篇关于java的List add不同类型的对象,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-03-03
  • Java restTemplate发送get请求query参数传递问题解决

    Java restTemplate发送get请求query参数传递问题解决

    这篇文章主要为大家介绍了Java restTemplate发送get请求query参数传递问题解决,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • 详解如何有效地处理Java中的多线程

    详解如何有效地处理Java中的多线程

    在现代软件开发中,多线程编程已成为提高程序性能和响应速度的重要手段,Java提供了丰富的多线程支持,使得在Java中实现并发操作变得相对简单,本文将深入探讨Java多线程编程的基本概念、常见问题和最佳实践,需要的朋友可以参考下
    2024-06-06
  • 5种Java经典创建型模式详解

    5种Java经典创建型模式详解

    这篇文章主要为大家详细介绍了5种Java经典创建型模式,感兴趣的小伙伴们可以参考一下
    2016-03-03
  • 浅谈Java中的hashcode方法(推荐)

    浅谈Java中的hashcode方法(推荐)

    本篇文章主要介绍了Java中的hashcode方法,详细的介绍了hashCode方法的作用,具有一定的参考价值,有需要的可以了解一下。
    2016-12-12
  • java压缩文件和下载图片示例

    java压缩文件和下载图片示例

    这篇文章主要为大家详细介绍了java压缩文件和下载图片示例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • Spring自动注入失败的解决方法

    Spring自动注入失败的解决方法

    这篇文章主要介绍了Spring自动注入失败的解决方法,帮助大家更好的理解和学习使用Spring框架,感兴趣的朋友可以了解下
    2021-05-05
  • mybatis中resulthandler的用法

    mybatis中resulthandler的用法

    这篇文章主要介绍了mybatis中resulthandler的用法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • Java实现一致性Hash算法详情

    Java实现一致性Hash算法详情

    这篇文章主要介绍了Java实现一致性Hash算法详情,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-09-09
  • Java实现Redis延时消息队列

    Java实现Redis延时消息队列

    本文主要介绍了Java实现Redis延时消息队列,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08

最新评论