I want to fly higher
programming Explorer
posts - 114,comments - 263,trackbacks - 0
1.代码示例
package com.landon.mavs.example.concurrent;

import java.util.concurrent.ThreadLocalRandom;

/**
 * 
 * ThreadLocal例子
 * 
 * <pre>
 *     1.线程局部变量.其完全不提供锁.以空间换时间的手段,为每个线程提供变量的独立副本,以保证线程的安全.
 *     2.接口:
 *         public T get() 返回此线程局部变量的当前线程副本中的值
 *         public void set(T value) 将此线程局部变量的当前线程副本中的值设置为指定值
 *         public void remove() 移除此线程局部变量当前线程的值
 *         protected T initialValue() 返回此线程局部变量的当前线程的“初始值”
 *  3.源码:
 *       // 1.取得当前调用线程t 2.根据t获得ThreadLocalMap【Entry(ThreadLocal k, Object v)】(即每个线程都有一个threadLocals)
 *       // 3. 如果map为不null,则将this和value设置到map;否则创建map
 *       public void set(T value) {
 *         Thread t = Thread.currentThread();
 *         ThreadLocalMap map = getMap(t);
 *         if (map != null)
 *             map.set(this, value);
 *         else
 *             createMap(t, value);
 *     }
 *     
 *     // 1.取得当前调用线程t 2.取得t线程关联的ThreadLocalMap
 *     // 3.如果map不为null则将this作为key得到Entry.然后得到值.
 *     // 4.如果map为null则返回initialValue(默认为null,同时会将initialValue set至当前线程)
 *     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();
 *     }.
 *  4.注意:
 *      1.不同线程间的对象副本并不是由ThreadLocal创建的.因为set方法未生成任何value的副本.所以将一个对象的实例设置到不同线程的ThreadLocal中,同样无法保证线程安全.
 *  所以需要每个线程内创建副本,然后设置到ThreadLocal中(即ThreadLocal只是一个key而已.).
 *      2.从get方法的最后一句看出,如果没有set,则第一次调用get方法时会调用initialValue;可使用匿名内部类覆写此方法,如果不希望初始值为null.另外如果调用了remove方法,即从
 *  ThreadLocalMap移除ThreadLocal key,则再次调用get方法时依然会调用initialValue.
 *      3.ThreadLocal实例通常是类中的 private static final字段(1.其只是用来做key,和当前线程绑定,所以用static方便 2.通常用静态方法直接获取局部变量实例,
 *  如ThreadLocalRandom),它们希望将状态与某一个线程相关联.
 *  
 *  5.总结:
 *      简单来说,ThreadLocal就是一个当前线程局部变量表的一个key,该key对应的V是new出来的一个新值,每个线程的都不一样.这样
 *  当前线程拿到ThreadLocal.get->则直接获得线程当前局部变量表对应的V.(第一次调用get时会将会将initialValue set,
 *  所以初始化ThreadLocal的时候可覆写initialValue).
 * </pre>
 * 
 * @author landon
 * 
 
*/

public class ThreadLocalExample {
    
// 一个线程局部变量
    private static final ThreadLocal<ThreadLocalVar> localVar = new ThreadLocal<>();

    
// 线程局部变量内部的V
    private static class ThreadLocalVar {
        
public int var;
    }


    
// 初始化的时候指定了localVar2的变量值
    private static final ThreadLocal<ThreadLocalVar> localVar2 = new ThreadLocal<ThreadLocalVar>() {
        
protected ThreadLocalVar initialValue() {
            
return new ThreadLocalVar();
        }

    }
;

    
// 一个任务
    private static class ThreadLocalTask implements Runnable {
        @Override
        
public void run() {
            
// 注意这里必须要new一个值,然后set.->即set了当前调用线程中局部变量的值.
            ThreadLocalVar var = new ThreadLocalVar();
            localVar.set(var);

            
// 随机一个局部变量的值.这里用到了ThreadLocalRandom,一个活生生的例子

            
// public static ThreadLocalRandom current() {
            
// return localRandom.get();
            
// }

            
// 这里初始化了一个线程局部变量值,是new了一个ThreadLocalRandom.这个很关键.
            
// private static final ThreadLocal<ThreadLocalRandom> localRandom =
            
// new ThreadLocal<ThreadLocalRandom>() {
            
// protected ThreadLocalRandom initialValue() {
            
// return new ThreadLocalRandom();
            
// }
            
// };
            var.var = ThreadLocalRandom.current().nextInt();

            
// 打印当前调用线程,即当前调用线程的线程局部变量的值.
            System.out.println("curThread:" + Thread.currentThread().getName()
                    
+ " localVar:" + localVar.get().var);

            
// 操作localVar2,将当前线程的id复制给localvar2
            localVar2.get().var = (int) Thread.currentThread().getId();

            System.out.println("curThread:" + Thread.currentThread().getName()
                    
+ " localVar2:" + localVar2.get().var);
        }

    }


    
// 从输出看,ThreadLocalExample#localVar如果不是线程局部变量则多线程操作时,一定要加锁的.但是如果是线程局部变量,则其只用来做一个key而已,局部变量的值是new出来
    
// 并设置,所以每个线程的ThreadLocal的get值都是互相没有任何关系的。
    
// 多线程之间可随意操作localVar.
    
// 同理localvar2
    public static void main(String[] args) {
        
for (int i = 0; i < 5; i++{
            
new Thread(new ThreadLocalTask(), "Thread-" + i).start();
        }


        
// 主线程操作
        localVar.set(new ThreadLocalVar());
        localVar.get().var = 100;

        System.out.println("curThread:" + Thread.currentThread().getName()
                
+ " localVar:" + localVar.get().var);

        
// 主线程操作localvar2
        localVar2.get().var = 201;

        System.out.println(
"curThread:" + Thread.currentThread().getName()
                
+ " localVar:" + localVar2.get().var);
    }

}



2.本篇主要介绍了ThreadLocal的用法,作为解决线程安全的一种方法.ThreadLocalRandom就是一个典型例子.

posted on 2014-03-05 15:17 landon 阅读(1676) 评论(0)  编辑  收藏 所属分类: Program

只有注册用户登录后才能发表评论。


网站导航: