java核心知识点学习----多线程间的数据共享和对象独立,ThreadLocal详解
线程内的数据共享与对象独立,举例:张三给李四转钱,开启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,封装了复杂数据对象.
运行效果:
java核心知识点学习----多线程间的数据共享和对象独立,ThreadLocal详解的更多相关文章
- java核心知识点学习----多线程间的数据共享的几种实现方式比较
需求:设计4个线程,其中两个线程每次对j增加1,另外两个线程对j减少1. 实现数据共享的几种方式比较: 1.使用同一个runnable对象 如果每个线程执行的代码相同,那么可以使用同一个runnabl ...
- Java核心知识点学习----多线程中的阻塞队列,ArrayBlockingQueue介绍
1.什么是阻塞队列? 所谓队列,遵循的是先进先出原则(FIFO),阻塞队列,即是数据共享时,A在写数据时,B想读同一数据,那么就将发生阻塞了. 看一下线程的四种状态,首先是新创建一个线程,然后,通过s ...
- Java核心知识点学习----多线程并发之线程间的通信,notify,wait
1.需求: 子线程循环10次,主线程循环100次,这样间隔循环50次. 2.实现: package com.amos.concurrent; /** * @ClassName: ThreadSynch ...
- Java核心知识点学习----多线程 倒计时记数器CountDownLatch和数据交换的Exchanger
本文将要介绍的内容都是Java5中的新特性,一个是倒计时记数器---CountDownLatch,另一个是用于线程间数据交换的Exchanger. 一.CountDownLatch 1.什么是Coun ...
- java核心知识点学习----多线程并发之线程同步
1.什么是线程同步? 多线程编程是很有趣的事情,它很容易出现"错误情况",这种情况不是由编码造成的,它是由系统的线程调度造成的,当使用多个线程来访问同一个数据时,很容易出现&quo ...
- Java核心知识点学习----使用Condition控制线程通信
一.需求 实现线程间的通信,主线程循环3次后,子线程2循环2次,子线程3循环3次,然后主线程接着循环3次,如此循环3次. 即:A->B->C---A->B->C---A-> ...
- Java核心知识点学习----线程中如何创建锁和使用锁 Lock,设计一个缓存系统
理论知识很枯燥,但这些都是基本功,学完可能会忘,但等用的时候,会发觉之前的学习是非常有意义的,学习线程就是这样子的. 1.如何创建锁? Lock lock = new ReentrantLock(); ...
- java核心知识点学习----创建线程的第三种方式Callable和Future CompletionService
前面已经指出通过实现Runnable时,Thread类的作用就是将run()方法包装成线程执行体,那么是否可以直接把任意方法都包装成线程执行体呢?Java目前不行,但其模仿者C#中是可以的. Call ...
- java核心知识点学习----重点学习线程池ThreadPool
线程池是多线程学习中需要重点掌握的. 系统启动一个新线程的成本是比较高的,因为它涉及与操作系统交互.在这种情形下,使用线程池可以很好的提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考 ...
随机推荐
- c++读写二进制文件
要读取文件必须包含<fstream>头文件,这里包含了C++读写文件的方法,可以使用fstream 类,这个类可以对文件进行读写操作. 1.打开文件. 打开文件可以有两种方式,第一 ...
- 如何让AutoCAD自动加载Arx,比如ArxDbg.arx
1.在AutoCAD的安装根目录下,用记事本创建一个acad.rx文件,如下 ------acad.rx----- ArxDbg.arx -------------------- 2.将ArxDbg. ...
- Win7打补丁以后vs2012突然出现的程序版本不兼容问题
装这个: Update for Microsoft Visual Studio 2012 (KB2781514) http://www.microsoft.com/en-us/download/det ...
- Spark ML 文本的分类
最近一直在研究Spark的分类算法,因为我们是做日志文本分类,在官网和各大网站一直没找到相应的Demo,经过1个多月的研究,终于有点成效. val sparkConf = new SparkConf( ...
- bootstrap-列表组
<div class="container"> <!-- list-group 列表组 给ul添加 list-group-item 列表项 给li添加 --> ...
- haproxy+keepalived实现高可用负载均衡
软件负载均衡一般通过两种方式来实现:基于操作系统的软负载实现和基于第三方应用的软负载实现.LVS就是基于Linux操作系统实现的一种软负载,HAProxy就是开源的并且基于第三应用实现的软负载. HA ...
- 大众点评试题分析(C/C++)
1.main函数执行完毕,从栈中弹出操作函数 void fn1(void), fn2(void), fn3(void); int main() { atexit(fn3); atexit(fn1); ...
- [转] 解决HttpServletResponse输出的中文乱码问题
首先,response返回有两种,一种是字节流outputstream,一种是字符流printwrite. 申明:这里为了方便起见,所有输出都统一用UTF-8编码. 先说字节流,要输出“中国" ...
- PHP For Windows/php-5.6.11-Win32-VC11-x64启动脚本
启动php-cgi.bat @echo off E: cd E:\php-5.6.11-Win32-VC11-x64 php-cgi.exe -b 127.0.0.1:9000 -c php.ini- ...
- 转:WIN7上搭建Windows Phone 8 开发环境——VMware Workstation下Win8 “无法安装Hyper-V, 某个虚拟机监控程序正在运行”问题解决的办法
转自:http://www.cnblogs.com/shaddock2013/p/3155024.html 最近在试着在Windows 7上搭建Windows Phone 8的开发调试环境,使用的是V ...