明仔2017

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

目标:如何保证各自线程上的数据是独立的,即A线程上数据只能被A线程操作

1:示例线程共享变量

我们先来看一个反例

package com.prepare.study;

import java.util.Random;

/**
 * @author: yinlm
 * @Date: Created in 2018/4/18
 * @Description:多个线程数据混乱的示例
 */
public class ThreadTest2 {
    // static 修饰 表示这是个全局变量
    private static int data = 0;
    public static void main(String[] args){
        // 模拟有3个线程
        for(int i=1;i<=3;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    data = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName()+ " put data "+data);
                    // 同一个线程范围内去操作共享数据
                    new OtherA().getData();
                    new OtherB().getData();
                }
            }).start();
        }
    }
static class OtherA{ public void getData(){ System.out.println("A from "+ Thread.currentThread().getName()+" get " +data); } } static class OtherB{ public void getData(){ System.out.println("B from "+ Thread.currentThread().getName()+" get " +data); } } } 结果:数据乱了,Thread-0 put的数据,从线程0拿的数据是错误的 Thread-0 put data 830670045 Thread-1 put data -2139375952 Thread-2 put data -458533612 A from Thread-2 get -458533612 A from Thread-0 get -458533612 A from Thread-1 get -458533612 B from Thread-2 get -458533612 B from Thread-1 get -458533612 B from Thread-0 get -458533612

使用ThreadLocal类线程绑定来实现数据的线程独立。

package com.prepare.study;

import java.util.Random;

/**
 * @author: yinlm
 * @Date: Created in 2018/4/18
 * @Description:
 */
public class ThreadTest2 {
    // static 修饰 表示这是个全局变量
    private static ThreadLocal<Integer> threadData = new ThreadLocal<>();

    public static void main(String[] args){
        // 模拟有3个线程
        for(int i=1;i<=3;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    int data = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName()+ " put data "+data);
                    // 数据进行了线程绑定
                    threadData.set(data);  
                    new OtherA().getData();
                    new OtherB().getData();
                }
            }).start();
        }

    }
    
    static class OtherA{
        public void getData(){
            // 获取的是该线程绑定的数据
            System.out.println("A from "+ Thread.currentThread().getName()+" get " +threadData.get());
        }
    }

    static class OtherB{
        public void getData(){
            System.out.println("B from "+ Thread.currentThread().getName()+" get " +threadData.get());
        }
    }
}

2:ThreadLocal的使用

ThreadLocal用于实现线程内的数据共享,即对于相同的程序代码,对各模块在同一个线程中运行时要共享一份数据,而在另外线程中运行时又共享另外一份数据

(1) 每个线程调用全局ThreadLocal对象的set方法,就相当于往其内部的map中增加一条记录,key分别是各自的线程,value是各自的set方法传进去的值。在线程结束时候可以调用

ThreadLocal.clear()方法,这样会更快释放内存,不调用也可以,因为线程结束后也可以自动释放相关的ThreadLocal变量

总结:一个ThradLocal代表一个变量,故其中只能放一个数据,你有多个变量都要线程范围内共享则要定义多个ThreadLocal对象;或这把这多个变量封装成一个对象。

技巧一:  当然有一种更优雅的方法,把该对象的构造方法写成线程绑定的,(即让某个类针对不同线程创建一个独立的实例对象),如下代码。

          实现对ThreadLocal变量的封装,让外界不要直接操作ThreadLocal 变量。

          该类的设计模仿了单利模式的思想,单利是所有线程拿到的是同一个实例;而这个是每一个线程拿到的是各自独立的对象。

        

package com.prepare.study;

import java.util.Random;

/**
 * @author: yinlm
 * @Date: Created in 2018/4/18
 * @Description:
 */
public class ThreadTest2 {

    public static void main(String[] args){
        // 模拟有3个线程
        for(int i=1;i<=3;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    int data = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName()+ " put data "+data);
                    // 该对象的实例本身就是线程绑定的
                    ThreadScopeUser.getThreadInstance().setName("name - "+data);
                    ThreadScopeUser.getThreadInstance().setAge(data);
// 模拟去操作数据 new OtherA().getData(); new OtherB().getData(); } }).start(); } } static class OtherA{ public void getData(){ // 获取该线程绑定的实例 ThreadScopeUser scopeUser = ThreadScopeUser.getThreadInstance(); System.out.println("A from "+ Thread.currentThread().getName()+" get " + scopeUser.getName()+" "+scopeUser.getAge()); } } static class OtherB{ public void getData(){ ThreadScopeUser scopeUser = ThreadScopeUser.getThreadInstance(); System.out.println("A from "+ Thread.currentThread().getName()+" get " + scopeUser.getName()+" "+scopeUser.getAge()); } } } // 该对象的创建就是为了线程独立的,那么线程独立就由此类来管理吧 class ThreadScopeUser{ private String name; private Integer age; // 构造方法私有,仿照单例写法 private ThreadScopeUser(){} // 这样获取该实体的实例对象本身就是线程绑定的 public static ThreadScopeUser getThreadInstance(){ ThreadScopeUser userInstance = threadLocalUser.get(); if(userInstance == null){ userInstance = new ThreadScopeUser(); threadLocalUser.set(userInstance); } return userInstance; } private static ThreadLocal<ThreadScopeUser> threadLocalUser = new ThreadLocal<>(); 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; } }

  

 

posted on 2018-04-18 22:16  明仔2017  阅读(340)  评论(0编辑  收藏  举报