Hi_Amos
坚持每天都在进步!!

线程内的数据共享与对象独立,举例:张三给李四转钱,开启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;
        }
    }
    
}
View Code

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

运行效果:

 

posted on 2014-04-21 01:06  Hi_Amos  阅读(4075)  评论(0编辑  收藏  举报