Java多线程编程之ThreadLocal线程范围内的共享变量

模拟ThreadLocal类实现:线程范围内的共享变量,每个线程只能访问他自己的,不能访问别的线程。

package com.ljq.test.thread;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * 线程范围内的共享变量
 *
 * 三个模块共享数据,主线程模块和AB模块
 *
 * @author Administrator
 *
 */
public class ThreadScopeShareData {
  // 准备共享的数据
  private static int data = 0;
  // 存放各个线程对应的数据
  private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>();

  public static void main(String[] args) {
    // 启动两个线程
    for (int i = 0; i < 2; i++) {
      new Thread(new Runnable() {
        @Override
        public void run() {
          // 现在当前线程中修改一下数据,给出修改信息
          int data = new Random().nextInt();
          // 将线程信息和对应数据存储起来
          threadData.put(Thread.currentThread(), data);
          System.out.println(Thread.currentThread().getName() + " has put data :" + data);
          new A().get();
          new B().get();
        }
      }).start();
    }
  }

  static class A {
    public void get() {
      int data = threadData.get(Thread.currentThread());
      System.out.println("A from " + Thread.currentThread().getName()
          + " get data :" + data);
    }
  }

  static class B {
    public void get() {
      int data = threadData.get(Thread.currentThread());
      System.out.println("B from " + Thread.currentThread().getName()
          + " get data :" + data);
    }
  }
}

运行结果:

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

每个线程调用全局ThreadLocal对象的set方法,就相当于往其内部的map中增加一条记录,key分别是各自的线程,value是各自的set方法传进去的值。在线程结束时可以调用ThreadLocal.clear()方法,这样会更快释放内存,不调用也可以,因为线程结束后也可以自动释放相关的ThreadLocal变量。

ThreadLocal的应用场景:
订单处理包含一系列操作:减少库存量、增加一条流水台账、修改总账,这几个操作要在同一个事务中完成,通常也即同一个线程中进行处理,如果累加公司应收款的操作失败了,则应该把前面的操作回滚,否则,提交所有操作,这要求这些操作使用相同的数据库连接对象,而这些操作的代码分别位于不同的模块类中。

银行转账包含一系列操作: 把转出帐户的余额减少,把转入帐户的余额增加,这两个操作要在同一个事务中完成,它们必须使用相同的数据库连接对象,转入和转出操作的代码分别是两个不同的帐户对象的方法。

例如Strut2的ActionContext,同一段代码被不同的线程调用运行时,该代码操作的数据是每个线程各自的状态和数据,对于不同的线程来说,getContext方法拿到的对象都不相同,对同一个线程来说,不管调用getContext方法多少次和在哪个模块中getContext方法,拿到的都是同一个。

实验案例:定义一个全局共享的ThreadLocal变量,然后启动多个线程向该ThreadLocal变量中存储一个随机值,接着各个线程调用另外其他多个类的方法,这多个类的方法中读取这个ThreadLocal变量的值,就可以看到多个类在同一个线程中共享同一份数据。

实现对ThreadLocal变量的封装,让外界不要直接操作ThreadLocal变量。
对基本类型的数据的封装,这种应用相对很少见。
对对象类型的数据的封装,比较常见,即让某个类针对不同线程分别创建一个独立的实例对象。

package com.ljq.test.thread;

import java.util.Random;

/**
 * ThreadLocal类及应用技巧
 *
 * 将线程范围内共享数据进行封装,封装到一个单独的数据类中,提供设置获取方法
 * 将该类单例化,提供获取实例对象的方法,获取到的实例对象是已经封装好的当前线程范围内的对象
 */
public class ThreadLocalTest {

  private static ThreadLocal<Integer> x = new ThreadLocal<Integer>();
  //private static ThreadLocal<MyThreadScopeData> myThreadScopeData = new ThreadLocal<MyThreadScopeData>();
  public static void main(String[] args) {
    for(int i=0;i<2;i++){
      new Thread(new Runnable(){
        @Override
        public void run() {
          int data = new Random().nextInt();
          System.out.println(Thread.currentThread().getName() + " has put data :" + data);
          x.set(data);

          /*
          MyThreadScopeData myData = new MyThreadScopeData();
          myData.setName("name" + data);
          myData.setAge(data);
          myThreadScopeData.set(myData);
          */
          MyThreadScopeData.getThreadInstance().setName("name" + data);
          MyThreadScopeData.getThreadInstance().setAge(data);
          new A().get();
          new B().get();
        }
      }).start();
    }
  }

  //使用获取到的线程范围内的对象实例调用相应方法
  static class A{
    public void get(){
      int data = x.get();
      System.out.println("A from " + Thread.currentThread().getName() + " get data :" + data);

      /*
      MyThreadScopeData myData = myThreadScopeData.get();
      System.out.println("A from " + Thread.currentThread().getName()
          + " getMyData: " + myData.getName() + "," + myData.getAge());
      */
      MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
      System.out.println("A from " + Thread.currentThread().getName()
          + " getMyData: " + myData.getName() + "," + myData.getAge());
    }
  }

  //使用获取到的线程范围内的对象实例调用相应方法
  static class B{
    public void get(){
      int data = x.get();
      System.out.println("B from " + Thread.currentThread().getName() + " get data :" + data);

      MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
      System.out.println("B from " + Thread.currentThread().getName()
          + " getMyData: " + myData.getName() + "," + myData.getAge());
    }
  }
}

class MyThreadScopeData {

  // 单例
  private MyThreadScopeData() {
  }

  // 提供获取实例方法,不加synchronized关键字表示线程各拿各自的数据,互不干扰
  public static/* synchronized */MyThreadScopeData getThreadInstance() {
    // 从当前线程范围内数据集中获取实例对象
    MyThreadScopeData instance = map.get();
    if (instance == null) {
      instance = new MyThreadScopeData();
      map.set(instance);
    }
    return instance;
  }

  // 将实例对象存入当前线程范围内数据集中
  private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();

  private String name;
  private int age;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }
}
时间: 2015-05-16

java多线程并发中使用Lockers类将多线程共享资源锁定

复制代码 代码如下: package com.yao; import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReadWriteLock;import java.util.c

java通过共享变量结束run停止线程的方法示例

stop()方法已经被弃用,原因是不太安全.API文档中给出了具体的详细解释.通过interrupted()方法打断线程.不推荐.通过共享变量结束run()方法,进而停止线程.如实例 复制代码 代码如下: public class ThreadInterrupt {    public static void main(String []args){        Runner run = new Runner();        run.start();        try {       

Java 存储模型和共享对象详解

Java 存储模型和共享对象详解 很多程序员对一个共享变量初始化要注意可见性和安全发布(安全地构建一个对象,并其他线程能正确访问)等问题不是很理解,认为Java是一个屏蔽内存细节的平台,连对象回收都不需要关心,因此谈到可见性和安全发布大多不知所云.其实关键在于对Java存储模型,可见性和安全发布的问题是起源于Java的存储结构. Java存储模型原理 有很多书和文章都讲解过Java存储模型,其中一个图很清晰地说明了其存储结构: 由上图可知, jvm系统中存在一个主内存(Main Memory或J

Java中tomcat memecached session 共享同步问题的解决办法

事件缘由:一个主项目"图说美物",另外一个子功能是品牌商的入驻功能,是跟主项目分开的项目,为了共享登录的用户信息,而实现session共享,俩个tomcat,一个tomcat6,一个tomcat7 web项目windows系统下实现session的共享 第一个步: 在俩个tomcat的context.xml这个文件中配置如下代码: <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManage

java实现屏幕共享功能实例分析

本文实例讲述了java实现屏幕共享功能的方法.分享给大家供大家参考.具体分析如下: 最近在做软件软件工程的课程设计,做一个用于实验室的屏幕监控系统,参考各种前人代码,最后领悟之后要转换自己的代码,初学者都是这样模仿过来的. 说到屏幕监控系统,有教师端和学生端,教师端就是Server端,学生端就做Client端.系统里比较有趣的一个地方应该算是屏幕广播与屏幕监控吧,其余什么点名签到,锁屏,定时关机的,就相对来说简单点. 屏幕广播,在功能实现上面,说白了,就是教师端的机器不断截取屏幕信息,以图片的形

Java读写Windows共享文件夹的方法实例

项目常常需要有访问共享文件夹的需求,例如共享文件夹存储照片.文件等.那么如何使用Java读写Windows共享文件夹呢? Java可以使用JCIFS框架对Windows共享文件夹进行读写,就这个框架可以让我们像访问本地文件夹一下访问远程文件夹. JCIFS的网址: http://jcifs.samba.org/ JCIFS是使用纯Java开发的一个开源框架,通过smb协议访问远程文件夹.该框架同时支持Windows共享文件夹和Linux共享文件夹,不过,Linux共享文件夹需要安装Samba服务

Java使用wait() notify()方法操作共享资源详解

Java多个线程共享资源: 1)wait().notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写. 2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁,或者叫管程) 3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程: 4)调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线

Java多线程编程之访问共享对象和数据的方法

多个线程访问共享对象和数据的方式有两种情况: 1.每个线程执行的代码相同,例如,卖票:多个窗口同时卖这100张票,这100张票需要多个线程共享. 2.每个线程执行的代码不同,例如:设计四个线程,其中两个线程每次对j增加1,另外两个线程每次对j减少1. a.如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个对象中有共享数据.卖票就可以这样做,每个窗口都在做卖票任务,卖的票都是同一个数据(点击查看具体案例). b.如果每个线程执行的代码不同,就需要使用不同的Runnable对象,有

Java设计模式之共享模式/享元模式(Flyweight模式)介绍

Flyweight定义:避免大量拥有相同内容的小类的开销(如耗费内存),使大家共享一个类(元类). 为什么使用共享模式/享元模式 面向对象语言的原则就是一切都是对象,但是如果真正使用起来,有时对象数可能显得很庞大,比如,字处理软件,如果以每个文字都作为一个对象,几千个字,对象数就是几千,无疑耗费内存,那么我们还是要"求同存异",找出这些对象群的共同点,设计一个元类,封装可以被共享的类,另外,还有一些特性是取决于应用(context),是不可共享的,这也Flyweight中两个重要概念内

Java线程重复执行以及操作共享变量的代码示例

1.题目:主线程执行10次,子线程执行10次,此过程重复50次 代码: package com.Thread.test; /* * function:主线程执行10次,子线程执行10次, * 此过程重复50次 */ public class ThreadProblem { public ThreadProblem() { final Business bus = new Business(); new Thread(new Runnable() { public void run() { for

Java线程之程安全与不安全代码示例

作为一个Java web开发人员,很少也不需要去处理线程,因为服务器已经帮我们处理好了.记得大一刚学Java的时候,老师带着我们做了一个局域网聊天室,用到了AWT.Socket.多线程.I/O,编写的客户端和服务器,当时做出来很兴奋,回学校给同学们演示,感觉自己好NB,呵呵,扯远了.上次在百度开发者大会上看到一个提示语,自己写的代码,6个月不看也是别人的代码,自己学的知识也同样如此,学完的知识如果不使用或者不常常回顾,那么还不是自己的知识.大学零零散散搞了不到四年的Java,我相信很多人都跟我一

java线程间通信的通俗解释及代码示例

线程间通信:由于多线程共享地址空间和数据空间,所以多个线程间的通信是一个线程的数据可以直接提供给其他线程使用,而不必通过操作系统(也就是内核的调度). 进程间的通信则不同,它的数据空间的独立性决定了它的通信相对比较复杂,需要通过操作系统.以前进程间的通信只能是单机版的,现在操作系统都继承了基于套接字(socket)的进程间的通信机制.这样进程间的通信就不局限于单台计算机了,实现了网络通信.线程通信主要分为以下几个部分,下面通过生活中图书馆借书的例子简单讲解以下: 通过共享对象通信 加入图书馆只有

Java设计模式之访问者模式使用场景及代码示例

Java设计模式访问者模式 模式概念 访问者模式表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作.访问者模式适用于数据结构相对稳定算法又易变化的系统,若系统数据结构对象易于变化,则不适合使用访问者模式.访问者模式的优点是增加操作很容易,因为增加操作意味着增加新的访问者. Visitor应用场景 一定会有的疑问:visitor和iterator的区别: visitor可以访问不同的对象(只需要在Element定义对应的accept),但是Ite

Java自带的加密类MessageDigest类代码示例

MessageDigest 类 MessageDigest 类为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法.信息摘要是安全的单向哈希函数,它接收任意大小的数据,并输出固定长度的哈希值. MessageDigest 对象开始被初始化.该对象通过使用 update()方法处理数据.任何时候都可以调用 reset()方法重置摘要.一旦所有需要更新的数据都已经被更新了,应该调用digest() 方法之一完成哈希计算. 对于给定数量的更新数据,digest 方法只能被调用一次.在调用 d

Java实现判断浏览器版本与类型简单代码示例

简单的Java获取浏览器版本和类型方法,不是很完美,但是可以用: 希望大家加以完善! public static void main(String[] args) { String agent=request.getHeader("User-Agent").toLowerCase(); System.out.println(agent); System.out.println("浏览器版本:"+getBrowserName(agent)); } public Str

Java并发之条件阻塞Condition的应用代码示例

本文研究的主要是Java并发之条件阻塞Condition的应用示例代码,具体如下. Condition将Object监视器方法(wait.notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合使用,为每个对象提供多个等待 set(wait-set).其中,Lock 替代了synchronized方法和语句的使用,Condition替代了Object监视器方法的使用. 1. Condition的基本使用 由于Condition可以用来替代wait.no

Java编程小实例—数字时钟的实现代码示例

本文的实例是Java编程实现一个数字时钟,代码测试可用,练练手吧.代码如下: package me.socketthread; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.util.Calendar; import java.util.GregorianCalenda

java数据结构之树基本概念解析及代码示例

Java中树的存储结构实现 一.树 树与线性表.栈.队列等线性结构不同,树是一...节点与节点之间的父子关系,可以为每个节点增加一个parent域,用以记录该节点的父点 树是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合.它是由n(n>0)个有限节点组成一个具有层次关系的集合.把 它叫做"树"是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的. 树定义和基本术语 定义 树(Tree)是n(n≥0)个结点的有限集T,并且当

Java语言Consistent Hash算法学习笔记(代码示例)

本文研究的主要是ConsistentHashing算法代码. 一致性哈希(Consistent Hash) 协议简介 一致性哈希算法在1997年由麻省理工学院提出(参见0),设计目标是为了解决因特网中的热点(Hot pot)问题,初衷和CARP十分类似.一致性哈希修正了CARP使用的简单哈希算法带来的问题,使得DHT可以在P2P环境中真正得到应用. 哈希算法 一致性哈希提出了在动态变化的Cache环境中,哈希算法应该满足的4个适应条件: 平衡性(Balance) 平衡性是指哈希的结果能够尽可能分