Java 多线程共享数据 Java 多线程之间共享数据
冬日毛毛雨 人气:01、线程范围的共享变量
多个业务模块针对同一个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)例如Strut2
的ActionContext
,同一段代码被不同的线程调用运行时,该代码操作的数据是每个线程各自的状态和数据,对于不同的线程来说,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
加载全部内容