论坛首页 Java企业应用论坛

ThreadLocal与synchronized

浏览 107342 次
精华帖 (8) :: 良好帖 (15) :: 新手帖 (9) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-05-31  
klyuan 写道
ThreadLocal与synchronized
这个类有一个Student的私有变量,在run方法中,它随机产生一个整数。然后设置到student变量中,从student中读取设置后的值。然后睡眠5秒钟,最后再次读student的age值。

public class ThreadDemo implements Runnable{
  [color=red]Student student = new Student();[/color]
  public static void main(String[] agrs) {
	 ThreadDemo td = new ThreadDemo();
	 Thread t1 = new Thread(td,"a");
	 Thread t2 = new Thread(td,"b");
    t1.start();
    t2.start();

  }
/* (non-Javadoc)
 * @see java.lang.Runnable#run()
 */
 public void run() {
	 accessStudent();
 }
 
 public void accessStudent() {
	    String currentThreadName = Thread.currentThread().getName();
	    System.out.println(currentThreadName+" is running!");
	   // System.out.println("first  read age is:"+this.student.getAge());
	    Random random = new Random();
	    int age = random.nextInt(100);
	    System.out.println("thread "+currentThreadName +" set age to:"+age);
	   
	   this.student.setAge(age);
	    System.out.println("thread "+currentThreadName+" first  read age is:"+this.student.getAge());
	    try {
	    Thread.sleep(5000);
	    }
	    catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
	    System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
	     
 } 
}

注意:这里的this.student.setAge(age);

我们把第一个例子用ThreadLocal来实现,但是我们需要些许改变。

Student并不是一个私有变量了,而是需要封装在一个ThreadLocal对象中去。调用ThreadLocal的set方法,ThreadLocal会为每一个线程都保持一份Student变量的副本。所以对student的读取操作都是通过ThreadLocal来进行的。
	protected Student getStudent() {
		Student student = (Student)studentLocal.get();
		if(student == null) {
			student = new Student();
			studentLocal.set(student);
		}
		return student;
	}
	
	protected void setStudent(Student student) {
		studentLocal.set(student);
	}

accessStudent()方法需要做一些改变。通过调用getStudent()方法来获得当前线程的Student变量,如果当前线程不存在一个Student变量,getStudent方法会创建一个新的Student变量,并设置在当前线程中。
    Student student = getStudent();
    student.setAge(age);
accessStudent()方法中无需要任何同步代码。

完整的代码清单如下:
TreadLocalDemo.java
public class TreadLocalDemo implements Runnable {
   private final static  ThreadLocal studentLocal = new ThreadLocal();
   
   public static void main(String[] agrs) {
	   TreadLocalDemo td = new TreadLocalDemo();
		 Thread t1 = new Thread(td,"a");
		 Thread t2 = new Thread(td,"b");
		
	    t1.start();
	    t2.start();
	   
	   


	  }
   
	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	public void run() {
		 accessStudent();
	}

	public  void  accessStudent() {
		
	    String currentThreadName = Thread.currentThread().getName();
	    System.out.println(currentThreadName+" is running!");
	    Random random = new Random();
	    int age = random.nextInt(100);
	    System.out.println("thread "+currentThreadName +" set age to:"+age);
	    Student student = getStudent();
	    student.setAge(age);
	    System.out.println("thread "+currentThreadName+" first  read age is:"+student.getAge());
	    try {
	    Thread.sleep(5000);
	    }
	    catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
	    System.out.println("thread "+currentThreadName +" second read age is:"+student.getAge());
	    
	}
	
	protected Student getStudent() {
		Student student = (Student)studentLocal.get();
		if(student == null) {
			student = new Student();
			studentLocal.set(student);
		}
		return student;
	}
	
	protected void setStudent(Student student) {
		studentLocal.set(student);
	}
}

Student student = getStudent();
student.setAge(age);


可见,使用ThreadLocal后,我们不需要任何同步代码,却能够保证我们线程间数据的安全。
而且,ThreadLocal的使用也非常的简单。
我们仅仅需要使用它提供的两个方法
void set(Object obj) 设置当前线程的变量的副本的值。
Object get() 返回当前线程的变量副本

由此可见,ThreadLocal通过一个Map来为每个线程都持有一个变量副本。这个map以当前线程为key。与synchronized相比,ThreadLocal是以空间换时间的策略来实现多线程程序。

Synchronized还是ThreadLocal?
ThreadLocal以空间换取时间,提供了一种非常简便的多线程实现方式。因为多个线程并发访问无需进行等……………………


注意引用中标记了红色的地方,也就是使用ThreadLocal改造前后Student student的作用范围的变化,如果使用这中方式来解决并发的问题,我想也完全没有必要引入ThreadLocal了,直接在accessStudent方法中这样处理就OK了 Student student =  new Student();:
    	public void  accessStudent(){
		//synchronized(this){	
		String currentThreadName = Thread.currentThread().getName();   
        System.out.println(currentThreadName+" is running!");   
       // System.out.println("first  read age is:"+this.student.getAge());   
        Random random = new Random();   
        int age = random.nextInt(100);   
        System.out.println("thread "+currentThreadName +" set age to:"+age);   
        Student student =  new Student();
        student.setAge(age);


相信这样每个线程,读出来的数据也不可能会有交叉的问题.
0 请登录后投票
   发表时间:2007-05-31  
因为引用的东西比较长,同时又不能在代码块中“着色”来强调我需要突出的问题,从我上面的回复不知道这里的各位是否明白了我的意图:其实LZ在解释synchornized和ThreadLocal的过程中改变了
accessStudent()中的student,正是这一点的改变,才使得楼主感觉到了ThreadLocal可以做到同步…………,如果是这种情况下也能同步的话,我想楼主是对的,但根据楼主的逻辑,下面的处理方法恐怕测试通不过:
public class ThreadDemo implements Runnable {
	private ThreadLocal local = new ThreadLocal();
	private Student student = getStudent();
           …………………………
            …………………………………

    public void  accessStudent(){
		//synchronized(this){	
        String currentThreadName = Thread.currentThread().getName();   
        System.out.println(currentThreadName+" is running!");   
       // System.out.println("first  read age is:"+this.student.getAge());   
        Random random = new Random();   
        int age = random.nextInt(100);   
        System.out.println("thread "+currentThreadName +" set age to:"+age);   
        //Student student =  new Student();
        student.setAge(age);   
        System.out.println("thread "+currentThreadName+
        		" first  read age is:"+student.getAge());   
        try {   
        Thread.sleep(5000);   
        } 

上面的代码中accessStudent使用的实例student来自ThreadDemo,这也是和楼主使用synchronized的时候是相同“地位”的,
0 请登录后投票
   发表时间:2007-05-31  
嗯。。ThreadLocal根本不能解决共享资源访问的问题,我想有很多人误用了synchronized,原本不需要用到同步,用ThreadLocal就能解决问题了.却用同步这种极其降低性能的方法。。所以能够ThreadLocal来代替Synchronized的地方,最好用ThreadLocal,真正只能用Synchronized的地方,ThreadLocal也只能干蹬眼
0 请登录后投票
   发表时间:2007-06-28  
俺觉得.楼主的主要意思就是如何避免线程之间的冲突,说明了两种情况而已
0 请登录后投票
   发表时间:2007-07-06  
我认为ThreadLocal是在多线程环境下,为各线程的资源(存放在threadLocals中)提供了一个管理方法。
目的并不是为了解决多线程环境中的并发访问控制。
0 请登录后投票
   发表时间:2007-07-06  
常人都喜欢纵向的钻研问题!
而不善于横向的分析,比较问题!
所以就被有人投诉了!
当然,本文的某些地方的确不够严谨!
0 请登录后投票
   发表时间:2007-07-06  
我想现在处理并发的方式已经简单很多了.
JDK5提供了一组concurrent的对象给我们访问,这可是大师Doug Lea的作品
jdk6更加完善了这一package.
大家可以看下java.util.concurrent
0 请登录后投票
   发表时间:2007-07-29  
貌似threadloacl跟线程安全没什么关系,理解保证得到的对象是单例
网上的一个简单实现public class ThreadLocal {
    private Map values = Collections.synchronizedMap(new HashMap());
    public Object get() {
        Thread curThread = Thread.currentThread();
        Object o = values.get(curThread);
        if (o == null && !values.containsKey(curThread)) {
            o = initialValue();
            values.put(curThread, o);
        }
        return o;
    }
    public void set(Object newValue) {
        values.put(Thread.currentThread(), newValue);
    }
    public Object initialValue() {
        return null;
    }
}
0 请登录后投票
   发表时间:2007-08-06  
看了一天的ThreadLocal,看到lz这篇才感觉对ThreadLocal有了一个深入的理解,通过两个比较,能更深入的了解ThreadLocal。看这篇文章之前也看其他的介绍ThreadLocal的文章,基本看不懂,一头雾水。谢谢LZ!
0 请登录后投票
   发表时间:2007-10-15  
有2个小宝宝,要抢喝奶,于是“同步”是这样来实现的,给宝宝一个先后顺序,一个一个的喝,ThreadLocal是另外一种方式,就是有几个宝宝就几个奶妈,于是一个宝宝配一个奶妈,特别是杏仁茶都磨得出的那种奶妈,这两种方式都能让宝宝不闹,
1 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics