线程内的数据共享与对象独立,举例:张三给李四转钱,开启A线程去执行转钱这个动作,刚好同时王五给赵六转钱,开启B线程去执行转钱,因为是调用的同样一个动作或者说对象,所以如果不能保证线程间的对象独立,那么很有可能发生,张三给李四转钱时把王五转给赵六的转钱一块提交了,而王五转钱整个动作还未完成,那么就造成了转钱错误, 所以线程间一方面要保证数据的共享,另一方面要保证对象的对立.

1.用Map封装对象以数据实现共享

  1. package com.amos.concurrent;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. import java.util.Random;
  5. /**
  6. * @ClassName: ThreadScopeShareData
  7. * @Description: 下面的例子用的是Map对象将数据实现共享
  8. * @author: amosli
  9. * @email:hi_amos@outlook.com
  10. * @date Apr 20, 2014 6:19:02 PM
  11. */
  12. public class ThreadScopeShareData {
  13. public static Map<Object, Integer> map = new HashMap<Object, Integer>();
  14. public static void main(String[] args) {
  15. for (int i = 0; i < 3; i++) {
  16. new Thread(new Runnable() {
  17. public void run() {
  18. int data = new Random().nextInt();//给data设值,
  19. System.out.println(Thread.currentThread().getName() + " set data:" + data);
  20. map.put(Thread.currentThread(), data);//将值按照Thread去设值,取的时候也按Thread去取,以保证数据的共享,但又保证了对象的独立.
  21. new A().get();
  22. new B().get();
  23. }
  24. }).start();
  25. }
  26. }
  27.  
  28. static class A {//这里A和B的方法虽然是一样的,这里是想表示有可能调用不同的对象去执行数据操作
  29. public int get() {
  30. data = map.get(Thread.currentThread());
  31. System.out.println("a from thread:" + Thread.currentThread().getName() + " is " + data);
  32. return data;
  33. }
  34. }
  35.  
  36. static class B {
  37. public int get() {
  38. int data = map.get(Thread.currentThread());
  39. System.out.println("b from thread:" + Thread.currentThread().getName() + " is " + data);
  40. return data;
  41. }
  42. }
  43.  
  44. }

 

运行效果:

 2.使用ThreadLocal实现数据共享

创建ThreadLocal,可以直接new出来,其设值支技泛型,new ThreadLocal<T>,如下将上面代码改写:

  1. public class ThreadLocalShareData {
  2. private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
  3. public static void main(String[] args) {
  4. for (int i = 0; i < 3; i++) {
  5. new Thread(new Runnable() {
  6. public void run() {
  7. int data = new Random().nextInt();//给data设值,
  8. System.out.println(Thread.currentThread().getName() + " set data:" + data);
  9. threadLocal.set(data);//使用ThreadLocal来设值
  10. new A().get();
  11. new B().get();
  12. }
  13. }).start();
  14. }
  15. }
  16. static class A {//这里A和B的方法虽然是一样的,这里是想表示有可能调用不同的对象去执行数据操作
  17. public int get() {
  18. int data = threadLocal.get();
  19. System.out.println("a from thread:" + Thread.currentThread().getName() + " is " + data);
  20. return data;
  21. }
  22. }
  23. class B....
  24. ...
  25. }

下面是ThreadLocal set(T value)方法的源码:

  1. public void set(T value) {
  2. Thread t = Thread.currentThread();
  3. ThreadLocalMap map = getMap(t);
  4. if (map != null)
  5. map.set(this, value);
  6. else
  7. createMap(t, value);
  8. }

这里同样是用Map方式的设值,只不过又封装了一层ThreadLocalMap.

查看其ThreadLocal  get()方法的源码:

  1. public T get() {
  2. Thread t = Thread.currentThread();
  3. ThreadLocalMap map = getMap(t);
  4. if (map != null) {
  5. ThreadLocalMap.Entry e = map.getEntry(this);
  6. if (e != null)
  7. return (T)e.value;
  8. }
  9. return setInitialValue();
  10. }

同样是通过与线程绑定,取值的.

3.实例测试

  1. package com.amos.concurrent;
  2. class Account {
  3. /*
  4. * 定义一个ThreadLocal类型的变量,该变量是一个线程局部变量
  5. */
  6. private ThreadLocal<String> name = new ThreadLocal<String>();
  7.  
  8. // 定义一个初始化name属性的构造器
  9. public Account(String str) {
  10. this.name.set(str);
  11. // 下面的代码用于访问当前线程的name副本的值
  12. System.out.println("------" + this.name.get());
  13. }
  14. // name的getter,setter方法
  15. public String getName() {
  16. return name.get();
  17. }
  18. public void setName(String str) {
  19. this.name.set(str);
  20. }
  21. }
  22.  
  23. class MyTest extends Thread {
  24. // 定义一个Account属性
  25. private Account account;
  26.  
  27. public MyTest(Account account, String name) {
  28. super(name);// 设置thread的名称
  29. this.account = account;
  30. }
  31.  
  32. @Override
  33. public void run() {
  34. // 循环
  35. for (int i = 0; i < 10; i++) {
  36. if (i == 6) {// 当i=6时,将name名称更改为当前的线程名
  37. account.setName(getName());
  38. }
  39. System.out.println(account.getName() + " 账户i的值:" + i);
  40. }
  41. }
  42. }
  43. public class ThreadLocalTest {
  44. public static void main(String[] args) {
  45. Account account = new Account("初始名称");
  46. // 启动两个线程,两人个线程共享同一个账户,即只有一个账户名.
  47. /*
  48. * 虽然丙个线程共享同一个账户,即只有一个账户名.但由于账户名是ThradLocal类型的,所以每个线程都完全拥有各自的账户名副本,
  49. * 因此在i=6以后,将看到两人个线程访问同一个账户时出现不同的账户名
  50. */
  51. new MyTest(account, "张三").start();
  52. new MyTest(account, "李四").start();
  53. }
  54. }

效果如下:

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.扩展---封装复杂数据对象

  1. package com.amos.concurrent;
  2. import java.util.Random;
  3. /**
  4. * @ClassName: ThreadLocalShareData
  5. * @Description: 下面的例子用的是ThreadLocal对象将数据实现共享,封装复杂数据对象
  6. * @author: amosli
  7. * @email:hi_amos@outlook.com
  8. * @date Apr 20, 2014 6:19:02 PM
  9. */
  10. public class ThreadLocalShareDataTest {
  11. public static void main(String[] args) {
  12. for (int i = 0; i < 3; i++) {
  13. new Thread(new Runnable() {
  14. public void run() {
  15. int data = new Random().nextInt();//给data设值,
  16. System.out.println(Thread.currentThread().getName() + " set data:" + data);
  17. MyThreadData.getMyThreadData().setName("name"+data);
  18. MyThreadData.getMyThreadData().setAge(data);
  19. new A().get();
  20. new B().get();
  21. }
  22. }).start();
  23. }
  24. }
  25. static class A {//这里A和B中的方法是一样的,可以只看一个
  26. public void get() {
  27. MyThreadData myThreadData = MyThreadData.getMyThreadData();
  28. int data =myThreadData.getAge();
  29. System.out.println("a from thread:" + Thread.currentThread().getName() + " age: " + data+" name:"+myThreadData.getName());
  30. }
  31. }
  32. static class B{
  33. public void get() {
  34. MyThreadData myThreadData = MyThreadData.getMyThreadData();
  35. int data =myThreadData.getAge();
  36. System.out.println("b from thread:" + Thread.currentThread().getName() + " age: " + data+" name:"+myThreadData.getName());
  37. }
  38. }
  39. //自定义对象
  40. static class MyThreadData {
  41. private static ThreadLocal<MyThreadData> mapLocal = new ThreadLocal<MyThreadData>();
  42. private MyThreadData(){}
  43. //单例模式,获取数值
  44. public static MyThreadData getMyThreadData(){
  45. MyThreadData instance = mapLocal.get();
  46. if(instance==null){
  47. instance = new MyThreadData();
  48. mapLocal.set(instance);
  49. }
  50. return instance;
  51. }
  52. //name,age setter/getter
  53. private String name ;
  54. private Integer age;
  55. public String getName() {
  56. return name;
  57. }
  58. public void setName(String name) {
  59. this.name = name;
  60. }
  61. public Integer getAge() {
  62. return age;
  63. }
  64. public void setAge(Integer age) {
  65. this.age = age;
  66. }
  67. }
  68.  
  69. }

上面例子都是封装基本类型的数据,这里是封装String name,Integer age,封装了复杂数据对象.

运行效果:

java核心知识点学习----多线程间的数据共享和对象独立,ThreadLocal详解的更多相关文章

  1. java核心知识点学习----多线程间的数据共享的几种实现方式比较

    需求:设计4个线程,其中两个线程每次对j增加1,另外两个线程对j减少1. 实现数据共享的几种方式比较: 1.使用同一个runnable对象 如果每个线程执行的代码相同,那么可以使用同一个runnabl ...

  2. Java核心知识点学习----多线程中的阻塞队列,ArrayBlockingQueue介绍

    1.什么是阻塞队列? 所谓队列,遵循的是先进先出原则(FIFO),阻塞队列,即是数据共享时,A在写数据时,B想读同一数据,那么就将发生阻塞了. 看一下线程的四种状态,首先是新创建一个线程,然后,通过s ...

  3. Java核心知识点学习----多线程并发之线程间的通信,notify,wait

    1.需求: 子线程循环10次,主线程循环100次,这样间隔循环50次. 2.实现: package com.amos.concurrent; /** * @ClassName: ThreadSynch ...

  4. Java核心知识点学习----多线程 倒计时记数器CountDownLatch和数据交换的Exchanger

    本文将要介绍的内容都是Java5中的新特性,一个是倒计时记数器---CountDownLatch,另一个是用于线程间数据交换的Exchanger. 一.CountDownLatch 1.什么是Coun ...

  5. java核心知识点学习----多线程并发之线程同步

    1.什么是线程同步? 多线程编程是很有趣的事情,它很容易出现"错误情况",这种情况不是由编码造成的,它是由系统的线程调度造成的,当使用多个线程来访问同一个数据时,很容易出现&quo ...

  6. Java核心知识点学习----使用Condition控制线程通信

    一.需求 实现线程间的通信,主线程循环3次后,子线程2循环2次,子线程3循环3次,然后主线程接着循环3次,如此循环3次. 即:A->B->C---A->B->C---A-> ...

  7. Java核心知识点学习----线程中如何创建锁和使用锁 Lock,设计一个缓存系统

    理论知识很枯燥,但这些都是基本功,学完可能会忘,但等用的时候,会发觉之前的学习是非常有意义的,学习线程就是这样子的. 1.如何创建锁? Lock lock = new ReentrantLock(); ...

  8. java核心知识点学习----创建线程的第三种方式Callable和Future CompletionService

    前面已经指出通过实现Runnable时,Thread类的作用就是将run()方法包装成线程执行体,那么是否可以直接把任意方法都包装成线程执行体呢?Java目前不行,但其模仿者C#中是可以的. Call ...

  9. java核心知识点学习----重点学习线程池ThreadPool

    线程池是多线程学习中需要重点掌握的. 系统启动一个新线程的成本是比较高的,因为它涉及与操作系统交互.在这种情形下,使用线程池可以很好的提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考 ...

随机推荐

  1. Webmin

    1.webmin介绍 Webmin 让您能够在远程使用支持 HTTPS (SSL 上的 HTTP)协议的 Web 浏览器通过 Web 界面管理您的主机.这在保证了安全性的前提下提供了简单深入的远程管理 ...

  2. [转]配置mysql允许远程连接的方法

    配置mysql允许远程连接的方法 vim /etc/my.cnf注释这一行:bind-address=127.0.0.1 ==> #bind-address=127.0.0.1保存退出.mysq ...

  3. android数据存储之Sqlite(二)

    SQLite学习笔记 前言:上一章我们介绍了sqlite的一些基本知识以及在dos命令下对sqlite进行的增删改查的操作,这一章我们将在android项目中实际来操作sqlite. 1. SQLit ...

  4. JSR 303 - Bean Validation 介绍及最佳实践

    JSR 303 - Bean Validation 介绍及最佳实践 JSR 303 – Bean Validation 是一个数据验证的规范,2009 年 11 月确定最终方案.2009 年 12 月 ...

  5. flash cs6导入某些mp3不能的解决办法

    安装最新的quicktime 另外还有一个很恶心的办法,可以不用装quicktime. 1.用adobe audio打开一个没问题的mp3, 2.再打开有问题的MP3,全选,复制: 3.切换到没问题的 ...

  6. json_encode和json_decode

    <?php $json = '{"a":1,"b":2,"c":3,"d":4,"e":5}' ...

  7. javascript事件类型之界面拖拽交互

    一.在线DEMO 界面拖拽交互

  8. [Tex学习笔记]数学公式再次测试

    \begin{align*}\sum_{n=0}^{\infty}\frac{(n!)^{2}2^{n+1}}{(2n+1)!}&=\sum_{n=0}^{\infty}\int_{0}^{1 ...

  9. Java操作SFTP

    import java.util.Properties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.jcr ...

  10. 19. Palindrome Partitioning && Palindrome Partitioning II (回文分割)

    Palindrome Partitioning Given a string s, partition s such that every substring of the partition is ...