`
m635674608
  • 浏览: 4947034 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

ThreadLocal 与 Synchronized 总结

    博客分类:
  • java
阅读更多

1.对于同步方法和对象:
无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的不同对象访问。
解释一下“取得的锁都是对象”的意思:如果一个对象有多个synchronized方法,只要一个线程访问了这个对象中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法,但是对于不是synchronized的方法可以访问。而对于其他线程里的另一个对象可以访问synchronized方法。

2.对于同步块:
synchronized(要锁定的对象) {……} 
当要锁定的对象为this时表示的是调用这个方法的对象。

3.对于同步static 方法:
不管调用方法的是类还是对象,锁定的都是类。即类被锁定了,只要一个线程中的对象或类访问了一个synchronized static方法,其他线程里的不管是类还是对象都不能再访问synchronized static方法了。

注:以上的对象即指一个类的实例。

再通过例子说明一下以上几点:
假设P1、P2是同一个类的不同对象,这个类中定义了以下几种情况的同步块或同步方法,P1、P2就都能够调用他们。
1.  把synchronized当作函数修饰符时,示例代码如下:

Java代码 复制代码 收藏代码
  1. 1.  Public synchronized void method(){      
  2. 2.  //….      
  3. 3.  }    


这也就是同步方法,那这时synchronized锁定的是哪个对象呢?他锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中 执行这个同步方法时,他们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却能够任意调用这个被加了 synchronized关键字的方法。
上边的示例代码等同于如下代码:

Java代码 复制代码 收藏代码
  1. 1.  public void method()      
  2. 2.  {      
  3. 3.  synchronized (this)      //  (1)      
  4. 4.  {      
  5. 5.         //…..      
  6. 6.  }      
  7. 7.  }     


(1)处的this指的是什么呢?他指的就是调用这个方法的对象,如P1。可见同步方法实质是将synchronized作用于object reference。――那个拿到了P1对象锁的线程,才能够调用P1的同步方法,而对P2而言,P1这个锁和他毫不相干,程式也可能在这种情形下摆脱同 步机制的控制,造成数据混乱。
2.同步块,示例代码如下:

Java代码 复制代码 收藏代码
  1. 1.  public void method(SomeObject so) {      
  2. 2.  synchronized(so)      
  3. 3.  {      
  4. 4.         //…..      
  5. 5.  }      
  6. 6.  }     

这时,锁就是so这个对象,谁拿到这个锁谁就能够运行他所控制的那段代码。当有一个明确的对象作为锁时,就能够这样写程式,但当没有明确的对象作为锁,只是想让一段代码同步时,能够创建一个特别的instance变量(他得是个对象)来充当锁:

Java代码 复制代码 收藏代码
  1. 1.  class Foo implements Runnable      
  2. 2.  {      
  3. 3.         private byte[] lock = new byte[0];  // 特别的instance变量      
  4. 4.      Public void method()      
  5. 5.  {      
  6. 6.         synchronized(lock) { //… }      
  7. 7.  }      
  8. 8.  //…..      
  9. 9.  }   

 
注:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
3.将synchronized作用于static 函数,示例代码如下:

Java代码 复制代码 收藏代码
  1. 1.  Class Foo      
  2. 2.  {      
  3. 3.  public synchronized static void method1()   // 同步的static 函数      
  4. 4.  {      
  5. 5.  //….      
  6. 6.  }      
  7. 7.  public void method2()      
  8. 8.  {      
  9. 9.         synchronized(Foo.class)   //  class literal(类名称字面常量)      
  10. 10. }      
  11. 11.        }     

 



代码中的method2()方法是把class literal作为锁的情况,他和同步的static函数产生的效果是相同的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。
记得在《Effective Java》一书中看到过将 Foo.class和 P1.getClass()用于作同步锁还不相同,不能用P1.getClass()来达到锁这个Class的目的。P1指的是由Foo类产生的对象。
能 够推断:假如一个类中定义了一个synchronized的static函数A,也定义了一个synchronized 的instance函数B,那么这个类的同一对象Obj在多线程中分别访问A和B两个方法时,不会构成同步,因为他们的锁都不相同。A方法的锁是Obj所 属的那个Class,而B的锁是Obj所属的这个对象。
搞清楚synchronized锁定的是哪个对象,就能帮助我们设计更安全的多线程程序。

1、synchronized实例方法

Java代码 复制代码 收藏代码
  1. synchronized void method(){   
  2.   ...   
  3. }  



在功能上,它相当于

Java代码 复制代码 收藏代码
  1. void method() {   
  2.      synchronized (this) {   
  3.     ...   
  4.     }   
  5. }  



2、synchronized类方法

Java代码 复制代码 收藏代码
  1. class Something {   
  2.    static synchronized void method(){   
  3.      ...   
  4.    }   
  5. }  

 

 



在功能上,它相当于

Java代码 复制代码 收藏代码
  1. class Something {   
  2.     static void method() {   
  3.       synchronized (Something.class){   
  4.        ...   
  5.     }   
  6.   }   
  7. }  



3、如果我们也可以自定义方法来实现相当于synchronized类似的功能,代码如下:

Java代码 复制代码 收藏代码
  1. void method() {   
  2.     lock();   
  3.     try{   
  4.        ...   
  5.     }finally{   
  6.        unlock();   
  7.     }   
  8. }  


以上的代码就是一个Before/After Pattern的一种实现方式
举例:
下面中Mutex类这种用来进行共享互斥的机制,一般称为mutex。

Java代码 复制代码 收藏代码
  1. public class Gate {   
  2.     private int counter = 0;   
  3.     private String name = "Nobody";   
  4.     private String address = "Nowhere";   
  5.     private final Mutex mutex = new Mutex();   
  6.     public void pass(String name, String address) { // 并非synchronized   
  7.         mutex.lock();   
  8.         try {   
  9.             this.counter++;   
  10.             this.name = name;   
  11.             this.address = address;   
  12.             check();   
  13.         } finally {   
  14.             mutex.unlock();   
  15.         }   
  16.     }   
  17.     public String toString() { // 并非synchronized   
  18.         String s = null;   
  19.         mutex.lock();   
  20.         try {   
  21.             s = "No." + counter + ": " + name + ", " + address;   
  22.         } finally {   
  23.             mutex.unlock();   
  24.         }   
  25.         return s;   
  26.     }   
  27.     private void check() {   
  28.         if (name.charAt(0) != address.charAt(0)) {   
  29.             System.out.println("***** BROKEN ***** " + toString());   
  30.         }   
  31.     }   
  32. }  



设计简单的Mutex类1:

Java代码 复制代码 收藏代码
  1. public final class Mutex {   
  2.     private boolean busy = false;   
  3.     public synchronized void lock() {   
  4.         while (busy) {   
  5.             try {   
  6.                 wait();   
  7.             } catch (InterruptedException e) {   
  8.             }   
  9.         }   
  10.         busy = true;   
  11.     }   
  12.     public synchronized void unlock() {   
  13.         busy = false;   
  14.         notifyAll();   
  15.     }   
  16. }  



设计完善的Mutex类2:

Java代码 复制代码 收藏代码
  1. public final class Mutex {   
  2.     private long locks = 0;   
  3.     private Thread owner = null;   
  4.     public synchronized void lock() {   
  5.         Thread me = Thread.currentThread();   
  6.         while (locks > 0 && owner != me) {   
  7.             try {   
  8.                 wait();   
  9.             } catch (InterruptedException e) {   
  10.             }   
  11.         }   
  12.         // locks == 0 || owner == me   
  13.         owner = me;   
  14.         locks++;   
  15.     }   
  16.     public synchronized void unlock() {   
  17.         Thread me = Thread.currentThread();   
  18.         if (locks == 0 || owner != me) {   
  19.             return;   
  20.         }   
  21.         // locks > 0 && owner == me   
  22.         locks--;   
  23.         if (locks == 0) {   
  24.             owner = null;   
  25.             notifyAll();   
  26.         }   
  27.     }   
  28. }  




4、我们看到synchronized块的时候,要先思考"synchronized是在保护什么",然后进一步思考"获取谁的锁定来保护的呢"
   要调用synchronized实例方法的纯种,一定会获取thid的锁定。一个实例的锁定,同一个时间内,只能有个线程可以得到。
   如果实例不同,那锁定也不同了。使用synchronized块的时候,特别需要考虑"获取谁的锁定来保护的呢"这种情况。因为
   synchronized需要明确地指明要获取的是哪个对象的锁定。例如:
 

Java代码 复制代码 收藏代码
  1. synchronized (obj) {   
  2.     ...   
  3.   }  
 


  这样的程序代码中,obj就是我们所要获取锁定的对象。请小心这个对象不可心写错,获取错误对象的锁定,就好想要保护自己
  自己的家,却把别人家的门给锁定了。

 

1.区别ThreadLocal 与 synchronized

  • ThreadLocal是一个线程隔离(或者说是线程安全)的变量存储的管理实体(注意:不是存储用的),它以Java类方式表现;
  • synchronized是Java的一个保留字,只是一个代码标识符,它依靠JVM的锁机制来实现临界区的函数、变量在CPU运行访问中的原子性。

两者的性质、表现及设计初衷不同,因此没有可比较性。

2.理解ThreadLocal中提到的变量副本
    事实上,我们向ThreadLocal中set的变量不是由ThreadLocal来存储的,而是Thread线程对象自身保存。当用户调用ThreadLocal对象的set(Object o)时,该方法则通过Thread.currentThread()获取当前线程,将变量存入Thread中的一个Map内,而Map的Key就是当前的ThreadLocal实例。请看源码,这是最主要的两个函数,能看出ThreadLocal与Thread的调用关系:

Java代码 复制代码 收藏代码
  1. public void set(T value) {   
  2.         Thread t = Thread.currentThread();   
  3.         ThreadLocalMap map = getMap(t);   
  4.         if (map != null)   
  5.             map.set(this, value);   
  6.         else  
  7.             createMap(t, value);   
  8. }   
  9.   
  10. ThreadLocalMap getMap(Thread t) {   
  11.         return t.threadLocals;   
  12. }  
public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
}

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
}


(有兴趣的朋友可以阅读Java的ThreadLocal源码)因此,我们可以知道,所谓的变量副本,即是对Object Reference(对象引用)的拷贝。

3.理解Thread和 ThreadLocal对变量的引用关系
    实际上Thread和ThreadLocal对变量引用关系就像是坐标系中的X轴和Y轴,是从两个维度上来组织对变量的引用的。

  • 首先说Thread。
  •     我们知道一个ThreadOne的执行会贯穿多个方法MethodA、MethodB、MethodC这些方法可能分布于不同的类实例。假设,这些方法分别使用了ThreadLocalA、ThreadLocalB、ThreadLocalC来保存线程本地变量,那么这些变量都存于ThreadOne的Map中,并使用各自的ThreadLocal实例作为key。 因此,可以认为,借助ThreanLocal的set方法,在X轴上,Thread横向关联同一线程上下文中来自多个Method的变量引用副本。



  • 接着说ThreadLocal。
  •     一个MethodA中的X变量将被多个线程ThreadOne、ThreadTwo、ThreadThree所访问。假设MethodA使用ThreadLocal存储X,通过set方法,以ThreadLocal作为key值,将不同线程来访时的不同的变量值引用保存于ThreadOne、ThreadTwo、ThreadThree的各自线程上下文中,确保每个线程有自己的一个变量值。因此,可以认为,ThreadLocal是以Method为Y轴,纵向关联了处于同一方法中的不同线程上的变量。


 

 

1、对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。


2、ThreadLocal使用场合主要解决多线程中数据因并发产生不一致问题。ThreadLocal为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存,单大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。

 

3、ThreadLocal不能使用基本数据类型,只能使用Object类型。ThreadLocal的使用比synchronized要简单得多。

ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。
synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。
而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。

 

4、Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。

 

示例1:

Java代码 复制代码 收藏代码
  1. //线程局部变量   
  2.     protected static ThreadLocal<Connection>  threadLocalCon = new ThreadLocal<Connection>();   
  3.        
  4.     /*  
  5.      * 获取数据库连接  
  6.      */  
  7.     public static  Connection getCon() {   
  8.   
  9.         Connection con = threadLocalCon.get();   
  10.         System.out.println("当前线程名称="+Thread.currentThread().getName()+",con="+con);   
  11.         try {   
  12.             if (con == null || con.isClosed()) {   
  13.                 Class.forName(driver);   
  14.                 con = DriverManager.getConnection(url, username, password);   
  15.                 threadLocalCon.set(con);   
  16.                    
  17.                 System.out.println("当前线程="+Thread.currentThread().getName()+",新建连接"+con);   
  18.                 System.out.println("当前线程="+Thread.currentThread().getName()+",threadLocalCon.get()="+threadLocalCon.get());   
  19.                 try {   
  20.                     System.out.println("当前线程="+Thread.currentThread().getName()+",开始休眠");   
  21.                     Thread.currentThread().sleep(10000);   
  22.                     System.out.println("当前线程="+Thread.currentThread().getName()+",结束休眠");   
  23.                 } catch (InterruptedException e) {   
  24.                     // TODO Auto-generated catch block   
  25.                     e.printStackTrace();   
  26.                 }   
  27.             }   
  28.         } catch (ClassNotFoundException e) {   
  29.             e.printStackTrace();   
  30.         } catch (SQLException e) {   
  31.             e.printStackTrace();   
  32.         }   
  33.         return con;   
  34.     }  
//线程局部变量
	protected static ThreadLocal<Connection>  threadLocalCon = new ThreadLocal<Connection>();
	
	/*
	 * 获取数据库连接
	 */
	public static  Connection getCon() {

		Connection con = threadLocalCon.get();
		System.out.println("当前线程名称="+Thread.currentThread().getName()+",con="+con);
		try {
			if (con == null || con.isClosed()) {
				Class.forName(driver);
				con = DriverManager.getConnection(url, username, password);
				threadLocalCon.set(con);
				
				System.out.println("当前线程="+Thread.currentThread().getName()+",新建连接"+con);
				System.out.println("当前线程="+Thread.currentThread().getName()+",threadLocalCon.get()="+threadLocalCon.get());
				try {
					System.out.println("当前线程="+Thread.currentThread().getName()+",开始休眠");
					Thread.currentThread().sleep(10000);
					System.out.println("当前线程="+Thread.currentThread().getName()+",结束休眠");
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return con;
	}

 

 

启动2个线程先后发出请求:



 

小结:

1、线程局部变量threadLocalCon为每一个线程分别提供一个该变量的副本,所以,每个线程第一次访问该变量时,通过get()取得的值都是null。

2、客户端的每一次请求,服务端都会新建一个线程为其服务,一次请求结束,相对应的线程也随之随之结束。

 

示例2、

在getCon()方法中加上synchronized关键字,再次执行(两次请求之间发起时间间隔短于10秒),看效果:

 

 

小结:
 1、synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问,第一个线程在getCon()执行完成之前,第二个线程只能等待。

 

文章来自于:

http://www.iteye.com/topic/179040

http://huangqiqing123.iteye.com/blog/1428071

http://javabeezer.iteye.com/blog/644561

http://lighter.iteye.com/blog/133136

 

分享到:
评论

相关推荐

    Synchronized与ThreadLocal

    Synchronized与ThreadLocal

    谈谈Java中的ThreadLocal

     ThreadLocal一般称为线程本地变量,它是一种特殊的线程绑定机制,将变量与线程绑定在一起,为每一个线程维护一个独立的变量副本。通过ThreadLocal可以将对象的可见范围限制在同一个线程内。  跳出误区  需要...

    线程安全你还在用synchronized?

    你还在用synchronized?线程安全相关知识深入剖析

    multiThread.jpg

    Java多线程技术思维导图,覆盖全面,可用作学习指导和查缺补漏,十分高效。...涵盖线程中断、线程状态、线程间通信,并发容器、ThreadLocal、Synchronized、CountDownLatch、CyclicBarrier等内容。

    Java多线程 之 临界区、ThreadLocal.docx

    synchronized关键字不属于方法特征签名的一部分,所以可以在覆盖方法的时候加上去。也就是说,在父类的方法声明上可以没有synchronized关键字,而在子类覆盖该方法时加上synchronized关键字。 注意:使用...

    Java中的线程同步与ThreadLocal无锁化线程封闭实现

    主要介绍了Java中的线程同步与ThreadLocal无锁化线程封闭实现,Synchronized关键字与ThreadLocal变量的使用是Java中线程控制的基础,需要的朋友可以参考下

    Java并发编程学习笔记

    2、可重入锁与 Synchronized 的其他特性 3、ThreadLocal 的底层实现与使用 4、ReentrantLock底层实现和如何使用 5、Condition源码分析 6、ReentrantReadWriteLock底层实现原理 7、并发工具类CountDownLatch 、...

    Java多线程编程中易混淆的3个关键字总结

    主要介绍了Java多线程编程中易混淆的3个关键字总结,本文总结了、volatile、ThreadLocal、synchronized等3个关键字,对这几个容易混淆概念的关键字分别做了讲解,需要的朋友可以参考下

    多线程相关代码(V3)

    多线程相关的(具体包括Lock synchronized Join ThreadLocal Executors CountDownLatch等)一些demo。

    【2018最新最详细】并发多线程教程

    4.彻底理解synchronized 5.彻底理解volatile 6.你以为你真的了解final吗? 7.三大性质总结:原子性、可见性以及有序性 8.初识Lock与AbstractQueuedSynchronizer(AQS) 9.深入理解AbstractQueuedSynchronizer(AQS) 10....

    java线程学习笔记

    2.3 线程本地存储(Java.lang.ThreadLocal) 15 2.4 线程阻塞 17 2.4.1 调用sleep(millisecond)使任务进入休眠状态 17 2.4.2 等待输出与输入 17 2.4.3 对象锁不可用 17 2.4.4 通过wait()使线程挂起。 17 2.5 线程...

    java学习整理文档.docx

    java 学习整理文档 Spring框架并...使用ThreadLocal ,在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在 ThreadLocal 中(以空间换时间”),为每一个线程都提供了一份变量,因此可以同时访问而互不影响

    java并发编程面试题

    java并发编程 基础知识,守护线程与线程, 并行和并发有什么区别? 什么是上下文切换? 线程和进程区别 什么是线程和进程? 创建线程有哪几种方式?,如何避免线程死锁 ...ThreadLocal内存泄漏分析与

    Java并发编程原理与实战

    线程安全性问题简单总结.mp4 线程之间的通信之wait notify.mp4 通过生产者消费者模型理解等待唤醒机制.mp4 Condition的使用及原理解析.mp4 使用Condition重写waitnotify案例并实现一个有界队列.mp4 深入解析...

    Java并发编程相关技术使用案例

    线程创建、Synchronized和Reentrantlock锁的使用、线程安全问题演示、Condition的应用、CountDownLatch的应用、Cyclicbarrier的应用、Semaphore的应用、线程池的应用、Completablefuture的应用、手写阻塞队列、fork...

    java多线程安全性基础介绍.pptx

    threadLocal 为每个线程保存一个副本 类似于一个以线程id为key的map 不可变对象状态 final 关键共享资源上互斥,变并发为串行即同步 锁 分类 显示锁 Lock Lock是个接口 实现类 ReentrantLock 可重入锁...

    Java进阶教程,面试大全,包罗万象

    synchronized在静态方法和普通方法的区别。 怎么实现所有线程在等待某个事件的发生才会去执行。 CAS。 Hashtable是怎么加锁的。 HashMap的并发问题。 ConcurrenHashMap 介绍。 AQS。 如何检测死锁,怎么预防死锁。 ...

    Java进阶教程,面试大全

    synchronized在静态方法和普通方法的区别。 怎么实现所有线程在等待某个事件的发生才会去执行。 CAS。 Hashtable是怎么加锁的。 HashMap的并发问题。 ConcurrenHashMap 介绍。 AQS。 如何检测死锁,怎么预防死锁。 ...

    java线程安图分析(含测试代码)

    目录: 基础概念 造成线程不安全的条件 变量种类与线程安全 如何避免线程不安全 synchronized关键字使用和原理 jdk多线程并发包 THREADLOCAL 测试工程

    百度地图毕业设计源码-interview-guide:面试指南

    synchronized与ReentractLock区别 volatile 线程池原理和参数配置 内存的多级缓存机 CAS 容器 HashMap 1.7和1.8的区别,resize过程,多线程的问题 ConcurrentHashMap 1.7和1.8的区别 redis 保证缓存与数据库中数据...

Global site tag (gtag.js) - Google Analytics