Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 701|回复: 0

ThreadLocal浅解

[复制链接]

该用户从未签到

发表于 2011-9-12 23:36:29 | 显示全部楼层 |阅读模式
[blockquote]-
[/blockquote]
如果有看到spring的一些源码的同学都知道,ThreadLocal对象在spring几乎在每一个重要的类的都存在其身影。到底ThreadLocal是有什么用的呢,让我们来揭开其面纱吧。

   假如某一个对象是非线程安全的,在多线程的环境下,对对象的访问则须采用synchronized或lock/unlock进行线程同步,采用这样的方法限制了并发的访问,会带来比较大的性能损失.当然,我们可以在无须同步的的情况下,来化解线程安全的问题---就是采用ThreadLocal.

   其实可以把ThreadLocal看成一个线程的局部变量,我们可以通过ThreadLocal为每一个线程提供了单独的副本.

  ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突.因为每一个线程都拥胡自己的变量副本,从而也就没有必要对该变量进行同步了.

  ThreadLocal提供了线程安全的共享对象,在编写多线程代码的时候,可以把不安全的变量封装进ThreadLocal.

   其实从另一个角度上看,对于多线程的资源共享的问题,同步机制采用了"以时间换空间"的方式,而ThreadLocal则采用了"以空间换时间"的方式。[注:这一句话来自 向同事借看的<<精通spring2.x企业开发详解>>一书第九章]

   举一个例子,先说明一下各类的用途如下:
   Log类是用来将程序的log记录存放在log.txt里面的,进行分配线程的工作
   TSLog产生Log的类,实例由每个线程所拥有
   ClientThread调用Log的线程

首先来看看Log类的写法:

java代码

    [li]public class Log {     [li]    private static final ThreadLocal tsLogCollection = new ThreadLocal();     [li]   [li]    // 加入一个log     [li]    public static void println(String s) {     [li]        getTSLog().println(s);     [li]    }     [li]   [li]    // 关闭log     [li]    public static void close() {     [li]        getTSLog().close();     [li]    }     [li]   [li]    // 取得线程特有的log,注意每一个线程都有一个副本的log,所以无论怎样调用TSLog的内容,都不会出现数据出错的现象     [li]    private static TSLog getTSLog() {     [li]        TSLog tsLog = (TSLog)tsLogCollection.get();     [li]   [li]        //如果线程是第一次呼叫,就建立新挡案并登陆log     [li]        if (tsLog == null) {     [li]            tsLog = new TSLog(Thread.currentThread().getName() + "-log.txt");     [li]            tsLogCollection.set(tsLog);     [li]        }     [li]   [li]        return tsLog;     [li]    }     [li]}   [/li]



再来看看最普通不过的TSLog类

Java代码

    import java.io.PrintWriter;     import java.io.FileWriter;     import java.io.IOException;        public class TSLog {         private PrintWriter writer = null;            //  初始化writer字段   
        public TSLog(String filename) {             try {                 writer = new PrintWriter(new FileWriter(filename));             } catch (IOException e) {                 e.printStackTrace();             }         }            //  加入一笔log         public void println(String s) {             writer.println(s);         }            //  关闭log         public void close() {             writer.println("==== End of log ====");             writer.close();         }     }   

      [pre] [/pre]

      再写一个ClientThread类:

      Java代码

        [li]public class ClientThread extends Thread {     [li]    public ClientThread(String name) {     [li]        super(name);     [li]    }     [li]    public void run() {     [li]        System.out.println(getName() + " BEGIN");     [li]        for (int i = 0; i < 10; i++) {     [li]            Log.println("i = " + i);     [li]            try {     [li]                Thread.sleep(100);     [li]            } catch (InterruptedException e) {     [li]            }     [li]        }     [li]        Log.close();     [li]        System.out.println(getName() + " END");     [li]    }     [li]}   [/li]

      [pre] [/pre]
      简单的类的类如下:

      Java代码

        [li]public class Main {     [li]    public static void main(String[] args) {     [li]        new ClientThread("A").start();     [li]        new ClientThread("B").start();     [li]        new ClientThread("C").start();     [li]    }     [li]}   [/li]

      [pre] [/pre]
      会产生A_log.txt,B_log.txt,以及c_log.txt三个文件,每一个文件的内容都是为
      main: i=1
      main: i=2
      ....
      main: i=10



      其实ThreadLocal的方法很简单的,只有一个set,get,remove,还有一个initialValue()等四个方法而已。

      再来看一个例子:

      Java代码

        [li]public class SequenceNumber {     [li]    private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){     [li]        public Integer initialValue(){     [li]            return 0;     [li]        }     [li]    };     [li]    public int getNextNum(){     [li]        seqNum.set(seqNum.get()+1);     [li]        return seqNum.get();     [li]    }     [li]         [li]    public static void main(String[] args)     [li]    {     [li]         SequenceNumber sn = new SequenceNumber();       [li]         TestClient t1 = new TestClient(sn);     [li]         TestClient t2 = new TestClient(sn);     [li]         TestClient t3 = new TestClient(sn);     [li]         t1.start();     [li]         t2.start();     [li]         t3.start();     [li]    }       [li]    private static class TestClient extends Thread     [li]    {     [li]        private SequenceNumber sn;     [li]        public TestClient(SequenceNumber sn) {     [li]            this.sn = sn;     [li]        }     [li]        public void run()     [li]        {     [li]            for (int i = 0; i < 3; i++) {     [li]                System.out.println("thread["+Thread.currentThread().getName()+"] sn["+sn.getNextNum()+"]");                 [li]            }     [li]        }     [li]    }     [li]}   [/li]

      运行结果如下:
      thread[Thread-2]sn[1]
      thread[Thread-0]sn[1]
      thread[Thread-1]sn[1]
      thread[Thread-2]sn[2]
      thread[Thread-0]sn[2]
      thread[Thread-1]sn[2]
      thread[Thread-2]sn[3]
      thread[Thread-0]sn[3]
      thread[Thread-1]sn[3]


      从结果可以看出,ThreadLocal为每一个线程都提供了一个对象的副本
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-2 09:44 , Processed in 0.386912 second(s), 46 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表