ThreadLocal与synchronized

系统 1471 0

ThreadLocal 与synchronized
Java良好的支持多线程。使用java,我们可以很轻松的编程一个多线程程序。但是使用多线程可能会引起并发访问的问题。synchronized和 ThreadLocal 都是用来解决多线程并发访问的问题。大家可能对synchronized较为熟悉,而对 ThreadLocal 就要陌生得多了。
并发问题。当一个对象被两个线程同时访问时,可能有一个线程会得到不可预期的结果。

一个简单的java类Studnet

Java代码 复制代码
  1. public   class  Student {   
  2.    private   int  age= 0 ;   
  3.      
  4.    public   int  getAge() {   
  5.        return   this .age;   
  6.          
  7.   }   
  8.      
  9.    public   void  setAge( int  age) {   
  10.        this .age = age;   
  11.   }   
  12. }  
    public class Student {
  private int age=0;
  
  public int getAge() {
	  return this.age;
	  
  }
  
  public void setAge(int age) {
	  this.age = age;
  }
}
  


一个多线程类ThreadDemo.
这个类有一个Student的私有变量,在run方法中,它随机产生一个整数。然后设置到student变量中,从student中读取设置后的值。然后睡眠5秒钟,最后再次读student的age值。

Java代码 复制代码
  1. public   class  ThreadDemo  implements  Runnable{   
  2.   Student student =  new  Student();   
  3.    public   static   void  main(String[] agrs) {   
  4.      ThreadDemo td =  new  ThreadDemo();   
  5.      Thread t1 =  new  Thread(td, "a" );   
  6.      Thread t2 =  new  Thread(td, "b" );   
  7.     t1.start();   
  8.     t2.start();   
  9.   
  10.   }   
  11. /* (non-Javadoc)  
  12.  * @see java.lang.Runnable#run()  
  13.  */   
  14.   public   void  run() {   
  15.      accessStudent();   
  16.  }   
  17.     
  18.   public   void  accessStudent() {   
  19.         String currentThreadName = Thread.currentThread().getName();   
  20.         System.out.println(currentThreadName+ " is running!" );   
  21.         // System.out.println("first  read age is:"+this.student.getAge());   
  22.         Random random =  new  Random();   
  23.          int  age = random.nextInt( 100 );   
  24.         System.out.println( "thread " +currentThreadName + " set age to:" +age);   
  25.           
  26.          this .student.setAge(age);   
  27.         System.out.println( "thread " +currentThreadName+ " first  read age is:" + this .student.getAge());   
  28.          try  {   
  29.         Thread.sleep( 5000 );   
  30.         }   
  31.          catch (InterruptedException ex) {   
  32.             ex.printStackTrace();   
  33.         }   
  34.         System.out.println( "thread " +currentThreadName + " second read age is:" + this .student.getAge());   
  35.             
  36.  }   
  37.      
  38. }  
    public class ThreadDemo implements Runnable{
  Student student = new Student();
  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());
	     
 }
  
}
  

运行这个程序,屏幕输出如下:
a is running!
b is running!
thread b set age to:33
thread b first  read age is:33
thread a set age to:81
thread a first  read age is:81
thread b second read age is:81
thread a second read age is:81

需要注意的是,线程a在同一个方法中,第一次读取student的age值与第二次读取值不一致。这就是出现了并发问题。

synchronized
上面的例子,我们模似了一个并发问题。Java提供了同步机制来解决并发问题。synchonzied关键字可以用来同步变量,方法,甚至同步一个代码块。
使用了同步后,一个线程正在访问同步对象时,另外一个线程必须等待。
  Synchronized同步方法
现在我们可以对accessStudent方法实施同步。
public synchronized void  accessStudent()
再次运行程序,屏幕输出如下:
a is running!
thread a set age to:49
thread a first  read age is:49
thread a second read age is:49
b is running!
thread b set age to:17
thread b first  read age is:17
thread b second read age is:17

加上了同步后,线程b必须等待线程a执行完毕后,线程b才开始执行。

对方法进行同步的代价是非常昂贵的。特别是当被同步的方法执行一个冗长的操作。这个方法执行会花费很长的时间,对这样的方法进行同步可能会使系统性能成数量级的下降。

Synchronized同步块
  在accessStudent方法中,我们真实需要保护的是student变量,所以我们可以进行一个更细粒度的加锁。我们仅仅对student相关的代码块进行同步。

Java代码 复制代码
  1. synchronized ( this ) {   
  2. Random random =  new  Random();   
  3. int  age = random.nextInt( 100 );   
  4. System.out.println( "thread " +currentThreadName + " set age to:" +age);   
  5.   
  6. this .student.setAge(age);   
  7.   
  8. System.out.println( "thread " +currentThreadName+ " first  read age is:" + this .student.getAge());   
  9. try  {   
  10. Thread.sleep( 5000 );   
  11. }   
  12. catch (InterruptedException ex) {   
  13.     ex.printStackTrace();   
  14. }   
  15. }  
    	    synchronized(this) {
	    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();
	    }
	    }
  

运行方法后,屏幕输出:
a is running!
thread a set age to:18
thread a first  read age is:18
b is running!
thread a second read age is:18
thread b set age to:62
thread b first  read age is:62
thread b second read age is:62

需要特别注意这个输出结果。
这个执行过程比上面的方法同步要快得多了。
只有对student进行访问的代码是同步的,而其它与部份代码却是异步的了。而student的值并没有被错误的修改。如果是在一个真实的系统中,accessStudent方法的操作又比较耗时的情况下。使用同步的速度几乎与没有同步一样快。

使用同步锁
稍微把上面的例子改一下,在ThreadDemo中有一个私有变量count,。
   private int count=0;
在accessStudent()中, 线程每访问一次,count都自加一次, 用来记数线程访问的次数。

Java代码 复制代码
  1. try  {   
  2. this .count++;   
  3. Thread.sleep( 5000 );   
  4. } catch (InterruptedException ex) {   
  5.     ex.printStackTrace();   
  6. }  
    	    try {
	    this.count++;
	    Thread.sleep(5000);
	    }catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
  

  为了模拟线程,所以让它每次自加后都睡眠5秒。
accessStuden()方法的完整代码如下:

Java代码 复制代码
  1.    String currentThreadName = Thread.currentThread().getName();   
  2. System.out.println(currentThreadName+ " is running!" );   
  3.    try  {   
  4. this .count++;   
  5. Thread.sleep( 5000 );   
  6. } catch (InterruptedException ex) {   
  7.     ex.printStackTrace();   
  8. }   
  9.  System.out.println( "thread " +currentThreadName+ " read count:" + this .count);   
  10.   
  11.   
  12. synchronized ( this ) {   
  13. Random random =  new  Random();   
  14. int  age = random.nextInt( 100 );   
  15. System.out.println( "thread " +currentThreadName + " set age to:" +age);   
  16.   
  17. this .student.setAge(age);   
  18.   
  19. System.out.println( "thread " +currentThreadName+ " first  read age is:" + this .student.getAge());   
  20. try  {   
  21. Thread.sleep( 5000 );   
  22. }   
  23. catch (InterruptedException ex) {   
  24.     ex.printStackTrace();   
  25. }   
  26. }   
  27. System.out.println( "thread " +currentThreadName + " second read age is:" + this .student.getAge());  
       	    String currentThreadName = Thread.currentThread().getName();
	    System.out.println(currentThreadName+" is running!");
			    try {
	    this.count++;
	    Thread.sleep(5000);
	    }catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
		    System.out.println("thread "+currentThreadName+" read count:"+this.count);
	    
	   
	    synchronized(this) {
	    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());
  

    运行程序后,屏幕输出:
a is running!
b is running!
thread a read count:2
thread a set age to:49
thread a first  read age is:49
thread b read count:2
thread a second read age is:49
thread b set age to:7
thread b first  read age is:7
thread b second read age is:7

我们仍然对student对象以synchronized(this)操作进行同步。
我们需要在两个线程中共享count失败。

所以仍然需要对count的访问进行同步操作。

Java代码 复制代码
  1. synchronized ( this ) {   
  2.    try  {   
  3.    this .count++;   
  4.   Thread.sleep( 5000 );   
  5.   } catch (InterruptedException ex) {   
  6.     ex.printStackTrace();   
  7.   }   
  8.   }   
  9.   System.out.println( "thread " +currentThreadName+ " read count:" + this .count);   
  10.      
  11.     
  12.    synchronized ( this ) {   
  13.   Random random =  new  Random();   
  14.    int  age = random.nextInt( 100 );   
  15.   System.out.println( "thread " +currentThreadName + " set age to:" +age);   
  16.     
  17.    this .student.setAge(age);   
  18.     
  19.   System.out.println( "thread " +currentThreadName+ " first  read age is:" + this .student.getAge());   
  20.    try  {   
  21.   Thread.sleep( 5000 );   
  22.   }   
  23.    catch (InterruptedException ex) {   
  24.     ex.printStackTrace();   
  25.   }   
  26.   }   
  27.   System.out.println( "thread " +currentThreadName + " second read age is:" + this .student.getAge());   
  28.    long  endTime = System.currentTimeMillis();   
  29.    long  spendTime = endTime - startTime;   
  30.   System.out.println( "花费时间:" +spendTime + "毫秒" );  
    		 synchronized(this) {
	    try {
	    this.count++;
	    Thread.sleep(5000);
	    }catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
	    }
	    System.out.println("thread "+currentThreadName+" read count:"+this.count);
	    
	   
	    synchronized(this) {
	    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());
	    long endTime = System.currentTimeMillis();
	    long spendTime = endTime - startTime;
	    System.out.println("花费时间:"+spendTime +"毫秒");
  


程序运行后,屏幕输出
a is running!
b is running!
thread a read count:1
thread a set age to:97
thread a first  read age is:97
thread a second read age is:97
花费时间:10015毫秒
thread b read count:2
thread b set age to:47
thread b first  read age is:47
thread b second read age is:47
花费时间:20124毫秒

我们在同一个方法中,多次使用synchronized(this)进行加锁。有可能会导致太多额外的等待。
应该使用不同的对象锁进行同步。

设置两个锁对象,分别用于student和count的访问加锁。

Java代码 复制代码
  1.   private  Object studentLock =  new  Object();   
  2. private  Object countLock =  new  Object();   
  3.   
  4. accessStudent()方法如下:   
  5.       long  startTime = System.currentTimeMillis();   
  6.         String currentThreadName = Thread.currentThread().getName();   
  7.         System.out.println(currentThreadName+ " is running!" );   
  8.         // System.out.println("first  read age is:"+this.student.getAge());   
  9.   
  10.           synchronized (countLock) {   
  11.          try  {   
  12.          this .count++;   
  13.         Thread.sleep( 5000 );   
  14.         } catch (InterruptedException ex) {   
  15.             ex.printStackTrace();   
  16.         }   
  17.         }   
  18.         System.out.println( "thread " +currentThreadName+ " read count:" + this .count);   
  19.            
  20.           
  21.          synchronized (studentLock) {   
  22.         Random random =  new  Random();   
  23.          int  age = random.nextInt( 100 );   
  24.         System.out.println( "thread " +currentThreadName + " set age to:" +age);   
  25.           
  26.          this .student.setAge(age);   
  27.           
  28.         System.out.println( "thread " +currentThreadName+ " first  read age is:" + this .student.getAge());   
  29.          try  {   
  30.         Thread.sleep( 5000 );   
  31.         }   
  32.          catch (InterruptedException ex) {   
  33.             ex.printStackTrace();   
  34.         }   
  35.         }   
  36.         System.out.println( "thread " +currentThreadName + " second read age is:" + this .student.getAge());   
  37.          long  endTime = System.currentTimeMillis();   
  38.          long  spendTime = endTime - startTime;   
  39.         System.out.println( "花费时间:" +spendTime + "毫秒" );  
     private Object studentLock = new Object();
private Object countLock = new Object();

accessStudent()方法如下:
	 long startTime = System.currentTimeMillis();
	    String currentThreadName = Thread.currentThread().getName();
	    System.out.println(currentThreadName+" is running!");
	   // System.out.println("first  read age is:"+this.student.getAge());

		 synchronized(countLock) {
	    try {
	    this.count++;
	    Thread.sleep(5000);
	    }catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
	    }
	    System.out.println("thread "+currentThreadName+" read count:"+this.count);
	    
	   
	    synchronized(studentLock) {
	    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());
	    long endTime = System.currentTimeMillis();
	    long spendTime = endTime - startTime;
	    System.out.println("花费时间:"+spendTime +"毫秒");
  


这样对count和student加上了两把不同的锁。

运行程序后,屏幕输出:
a is running!
b is running!
thread a read count:1
thread a set age to:48
thread a first  read age is:48
thread a second read age is:48
花费时间:10016毫秒
thread b read count:2
thread b set age to:68
thread b first  read age is:68
thread b second read age is:68
花费时间:20046毫秒
与两次使用synchronized(this)相比,使用不同的对象锁,在性能上可以得到更大的提升。

由此可见synchronized是实现java的同步机制。同步机制是为了实现同步多线程对相同资源的并发访问控制。保证多线程之间的通信。
可见,同步的主要目的是保证多线程间的数据共享。同步会带来巨大的性能开销,所以同步操作应该是细粒度的。如果同步使用得当,带来的性能开销是微不足道的。使用同步真正的风险是复杂性和可能破坏资源安全,而不是性能。


ThreadLocal
由上面可以知道,使用同步是非常复杂的。并且同步会带来性能的降低。Java提供了另外的一种方式,通过 ThreadLocal 可以很容易的编写多线程程序。从字面上理解,很容易会把 ThreadLocal 误解为一个线程的本地变量。其它 ThreadLocal 并不是代表当前线程, ThreadLocal 其实是采用哈希表的方式来为每个线程都提供一个变量的副本。从而保证各个线程间数据安全。每个线程的数据不会被另外线程访问和破坏。

我们把第一个例子用 ThreadLocal 来实现,但是我们需要些许改变。
Student并不是一个私有变量了,而是需要封装在一个 ThreadLocal 对象中去。调用 ThreadLocal 的set方法, ThreadLocal 会为每一个线程都保持一份Student变量的副本。所以对student的读取操作都是通过 ThreadLocal 来进行的。

Java代码 复制代码
  1. protected  Student getStudent() {   
  2.     Student student = (Student)studentLocal.get();   
  3.      if (student ==  null ) {   
  4.         student =  new  Student();   
  5.         studentLocal.set(student);   
  6.     }   
  7.      return  student;   
  8. }   
  9.   
  10. protected   void  setStudent(Student student) {   
  11.     studentLocal.set(student);   
  12. }  
    	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

Java代码 复制代码
  1. public   class  TreadLocalDemo  implements  Runnable {   
  2.     private   final   static    ThreadLocal  studentLocal =  new   ThreadLocal ();   
  3.       
  4.     public   static   void  main(String[] agrs) {   
  5.        TreadLocalDemo td =  new  TreadLocalDemo();   
  6.          Thread t1 =  new  Thread(td, "a" );   
  7.          Thread t2 =  new  Thread(td, "b" );   
  8.            
  9.         t1.start();   
  10.         t2.start();   
  11.           
  12.           
  13.   
  14.   
  15.       }   
  16.       
  17.      /* (non-Javadoc)  
  18.      * @see java.lang.Runnable#run()  
  19.      */   
  20.      public   void  run() {   
  21.          accessStudent();   
  22.     }   
  23.   
  24.      public    void   accessStudent() {   
  25.            
  26.         String currentThreadName = Thread.currentThread().getName();   
  27.         System.out.println(currentThreadName+ " is running!" );   
  28.         Random random =  new  Random();   
  29.          int  age = random.nextInt( 100 );   
  30.         System.out.println( "thread " +currentThreadName + " set age to:" +age);   
  31.         Student student = getStudent();   
  32.         student.setAge(age);   
  33.         System.out.println( "thread " +currentThreadName+ " first  read age is:" +student.getAge());   
  34.          try  {   
  35.         Thread.sleep( 5000 );   
  36.         }   
  37.          catch (InterruptedException ex) {   
  38.             ex.printStackTrace();   
  39.         }   
  40.         System.out.println( "thread " +currentThreadName + " second read age is:" +student.getAge());   
  41.            
  42.     }   
  43.        
  44.      protected  Student getStudent() {   
  45.         Student student = (Student)studentLocal.get();   
  46.          if (student ==  null ) {   
  47.             student =  new  Student();   
  48.             studentLocal.set(student);   
  49.         }   
  50.          return  student;   
  51.     }   
  52.        
  53.      protected   void  setStudent(Student student) {   
  54.         studentLocal.set(student);   
  55.     }   
  56. }  
    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);
	}
}
  

运行程序后,屏幕输出:
b is running!
thread b set age to:0
thread b first  read age is:0
a is running!
thread a set age to:17
thread a first  read age is:17
thread b second read age is:0
thread a second read age is:17

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

另外 ThreadLocal 还有一个protected的initialValue()方法。返回变量副本在当前线程的初始值。默认为null

ThreadLocal 是怎么做到为每个线程都维护一个变量的副本的呢?
我们可以猜测到 ThreadLocal 的一个简单实现

Java代码 复制代码
  1. public   class   ThreadLocal   
  2. {   
  3. private  Map values = Collections.synchronizedMap( new  HashMap());   
  4. public  Object get()   
  5.  {   
  6.   Thread curThread = Thread.currentThread();    
  7.   Object o = values.get(curThread);    
  8. if  (o ==  null  && !values.containsKey(curThread))   
  9.   {   
  10.    o = initialValue();   
  11.    values.put(curThread, o);    
  12.   }   
  13. return  o;    
  14.  }   
  15.   
  16. public   void  set(Object newValue)   
  17.  {   
  18.   values.put(Thread.currentThread(), newValue);   
  19.  }   
  20.   
  21. public  Object initialValue()   
  22.  {   
  23. return   null ;    
  24.  }   
  25. }  
    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; 
 }
}
  


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

Synchronized还是 ThreadLocal ?
ThreadLocal 以空间换取时间,提供了一种非常简便的多线程实现方式。因为多个线程并发访问无需进行等待,所以使用 ThreadLocal 会获得更大的性能。虽然使用 ThreadLocal 会带来更多的内存开销,但这点开销是微不足道的。因为保存在 ThreadLocal 中的对象,通常都是比较小的对象。另外使用 ThreadLocal 不能使用原子类型,只能使用Object类型。 ThreadLocal 的使用比synchronized要简单得多。
ThreadLocal 和Synchonized都用于解决多线程并发访问。但是 ThreadLocal 与synchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而 ThreadLocal 为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
Synchronized用于线程间的数据共享,而 ThreadLocal 则用于线程间的数据隔离。
当然 ThreadLocal 并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比 ThreadLocal 更加复杂。

 

 

http://klyuan.iteye.com/blog/81936

ThreadLocal与synchronized


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论