多线程间的数据共享和对象独立,ThreadLocal详解
来源:互联网 发布:四川广电网络官网 编辑:程序博客网 时间:2024/05/22 05:28
线程内的数据共享与对象独立,举例:张三给李四转钱,开启A线程去执行转钱这个动作,刚好同时王五给赵六转钱,开启B线程去执行转钱,因为是调用的同样一个动作或者说对象,所以如果不能保证线程间的对象独立,那么很有可能发生,张三给李四转钱时把王五转给赵六的转钱一块提交了,而王五转钱整个动作还未完成,那么就造成了转钱错误, 所以线程间一方面要保证数据的共享,另一方面要保证对象的对立.
1.用Map封装对象以数据实现共享
package com.amos.concurrent;import java.util.HashMap;import java.util.Map;import java.util.Random;/** * @ClassName: ThreadScopeShareData * @Description: 下面的例子用的是Map对象将数据实现共享 * @author: amosli * @email:hi_amos@outlook.com * @date Apr 20, 2014 6:19:02 PM */public class ThreadScopeShareData {public static Map<Object, Integer> map = new HashMap<Object, Integer>(); public static void main(String[] args) { for (int i = 0; i < 3; i++) { new Thread(new Runnable() { public void run() { int data = new Random().nextInt();//给data设值, System.out.println(Thread.currentThread().getName() + " set data:" + data); map.put(Thread.currentThread(), data);//将值按照Thread去设值,取的时候也按Thread去取,以保证数据的共享,但又保证了对象的独立. new A().get(); new B().get(); } }).start(); } } static class A {//这里A和B的方法虽然是一样的,这里是想表示有可能调用不同的对象去执行数据操作 public int get() { data = map.get(Thread.currentThread()); System.out.println("a from thread:" + Thread.currentThread().getName() + " is " + data); return data; } } static class B { public int get() { int data = map.get(Thread.currentThread()); System.out.println("b from thread:" + Thread.currentThread().getName() + " is " + data); return data; } }}
运行效果:
2.使用ThreadLocal实现数据共享
创建ThreadLocal,可以直接new出来,其设值支技泛型,new ThreadLocal<T>,如下将上面代码改写:
public class ThreadLocalShareData { private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(); public static void main(String[] args) { for (int i = 0; i < 3; i++) { new Thread(new Runnable() { public void run() { int data = new Random().nextInt();//给data设值, System.out.println(Thread.currentThread().getName() + " set data:" + data); threadLocal.set(data);//使用ThreadLocal来设值 new A().get(); new B().get(); } }).start(); } } static class A {//这里A和B的方法虽然是一样的,这里是想表示有可能调用不同的对象去执行数据操作 public int get() { int data = threadLocal.get(); System.out.println("a from thread:" + Thread.currentThread().getName() + " is " + data); return data; } }class B.......}
下面是ThreadLocal set(T value)方法的源码:
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
这里同样是用Map方式的设值,只不过又封装了一层ThreadLocalMap.
查看其ThreadLocal get()方法的源码:
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); }
同样是通过与线程绑定,取值的.
3.实例测试
package com.amos.concurrent;class Account { /* * 定义一个ThreadLocal类型的变量,该变量是一个线程局部变量 */ private ThreadLocal<String> name = new ThreadLocal<String>(); // 定义一个初始化name属性的构造器 public Account(String str) { this.name.set(str); // 下面的代码用于访问当前线程的name副本的值 System.out.println("------" + this.name.get()); } // name的getter,setter方法 public String getName() { return name.get(); } public void setName(String str) { this.name.set(str); }}class MyTest extends Thread { // 定义一个Account属性 private Account account; public MyTest(Account account, String name) { super(name);// 设置thread的名称 this.account = account; } @Override public void run() { // 循环 for (int i = 0; i < 10; i++) { if (i == 6) {// 当i=6时,将name名称更改为当前的线程名 account.setName(getName()); } System.out.println(account.getName() + " 账户i的值:" + i); } }}public class ThreadLocalTest { public static void main(String[] args) { Account account = new Account("初始名称"); // 启动两个线程,两人个线程共享同一个账户,即只有一个账户名. /* * 虽然丙个线程共享同一个账户,即只有一个账户名.但由于账户名是ThradLocal类型的,所以每个线程都完全拥有各自的账户名副本, * 因此在i=6以后,将看到两人个线程访问同一个账户时出现不同的账户名 */ new MyTest(account, "张三").start(); new MyTest(account, "李四").start(); }}
效果如下:
4.关于ThreadLocal的几点说明
1).ThreadLoca原理:
Thread Local Variable(线程局部变量)的意思,其功能其实非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立地改变自己的副本,而不会和D他线程的副本冲突,从线程的角度来看,就好像每个线程都完全拥有该变量一样.
2).常用的方法:
>>T get():返回此线程局部变量中当前线程的值.
>>void remove():删除此线程局部变量中当前线程的值.
>>void set(T value):设置此线程局部变量中当前线程副本中的值.
3).ThradLocal和线程同步机制的区别:
实现机制不同:和线程同步机制一样,都是为了解决多线程中,对同一变量的访问冲突,在普通的同步机制中,是通过对象加锁来实现多个线程对同一个变量的安全访问的.而ThreadLocal是将需要并发访问的资源复制多分,每个线程拥有一份资源,每个线程拥有自己的资源副本,从而也变没有必要对该变量进行同步了.
面向问题的领域不同: ThreadLocal 并不能替代同步机制,同步机制是为了同步多个线程对相同资源的并发访问,是多个线程之间进行通信的有效方式;而ThradLocal是为了隔离多个线程的数据共享,从根本上避免了多个线程之间对共享资源(变量)的竞争,也就不需要对多个线程进行同步了.
4)何时使用?
如果多个线程之间需要共享资源,以达到线程之间的通信功能,就使用同步机制.
如果仅仅需要隔离多个线程之间的共享冲突,则可以使用ThreadLocal
5.扩展---封装复杂数据对象
package com.amos.concurrent;import java.util.Random;/** * @ClassName: ThreadLocalShareData * @Description: 下面的例子用的是ThreadLocal对象将数据实现共享,封装复杂数据对象 * @author: amosli * @email:hi_amos@outlook.com * @date Apr 20, 2014 6:19:02 PM */public class ThreadLocalShareDataTest { public static void main(String[] args) { for (int i = 0; i < 3; i++) { new Thread(new Runnable() { public void run() { int data = new Random().nextInt();//给data设值, System.out.println(Thread.currentThread().getName() + " set data:" + data); MyThreadData.getMyThreadData().setName("name"+data); MyThreadData.getMyThreadData().setAge(data); new A().get(); new B().get(); } }).start(); } } static class A {//这里A和B中的方法是一样的,可以只看一个 public void get() { MyThreadData myThreadData = MyThreadData.getMyThreadData(); int data =myThreadData.getAge(); System.out.println("a from thread:" + Thread.currentThread().getName() + " age: " + data+" name:"+myThreadData.getName()); } } static class B{ public void get() { MyThreadData myThreadData = MyThreadData.getMyThreadData(); int data =myThreadData.getAge(); System.out.println("b from thread:" + Thread.currentThread().getName() + " age: " + data+" name:"+myThreadData.getName()); } } //自定义对象 static class MyThreadData { private static ThreadLocal<MyThreadData> mapLocal = new ThreadLocal<MyThreadData>(); private MyThreadData(){} //单例模式,获取数值 public static MyThreadData getMyThreadData(){ MyThreadData instance = mapLocal.get(); if(instance==null){ instance = new MyThreadData(); mapLocal.set(instance); } return instance; } //name,age setter/getter private String name ; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } } }
上面例子都是封装基本类型的数据,这里是封装String name,Integer age,封装了复杂数据对象.
运行效果:
- 多线程间的数据共享和对象独立,ThreadLocal详解
- 多线程(三) 实现线程范围内模块之间共享数据及线程间数据独立(ThreadLocal)
- 【多线程】共享对象和数据的解决方案
- 多线程间共享对象和数据方式
- Java多线程/并发15、保持线程间的数据独立:ThreadLocal应用
- java多线程学习(四)---ThreadLocal和单例实现线程内对象的共享
- 多线程共享数据 synchronized和使用ThreadLocal区别
- 多线程之线程范围内的数据共享ThreadLocal
- Java基础:多线程之线程范围内的数据共享ThreadLocal
- 多线程访问共享对象和数据的方式
- 多线程访问共享对象和数据的方式-总结
- 多线程访问共享对象和数据的方式
- 多线程访问共享对象和数据的方式
- ThreadLocal实现线程范围内模块之间共享数据及线程间数据独立
- ThreadLocal实现:java线程范围内的共享数据,线程外独立
- 多线程访问共享对象和数据方法
- 同一个线程不同模块间的数据共享 ThreadLocal
- Java多线程与并发应用-(6)-多个线程之间共享对象和数据的方式
- TI C66x DSP 系统events及其应用 - 3
- JOptionPane类提示框的一些常用的方法
- Auto-increment 会在新记录插入表中时生成一个唯一的数字。
- cocos2d-x安装和配置
- 苹果打包问题汇总
- 多线程间的数据共享和对象独立,ThreadLocal详解
- JEECG开源团队招募新成员 2014年
- 单链表的基本操作
- Pro Android学习笔记(八八):了解Handler(2):什么是Handler
- hdu_2526_浪漫手机
- Flex4绘制扇形
- 关于大数据
- jQuery校验---jquery validate 详解
- 联想电脑快捷键的使用和通用设置