线程安全函数的概念比较直观,众所周知,同一进程的不一样线程会共享同一主内存,线程的私有栈中只包括PC栈,操做数栈,局部变量数组和动态连接。对共享内存进行读写时,若要保证线程安全,则必须经过加锁的方式。数组
若一个程序或子程序能够“在任意时刻被中断而后操做系统调度执行另一段代码,这段代码又调用了该子程序不会出错”,则称其为可重入(reentrant或re-entrant)的。即当该子程序正在运行时,执行线程能够再次进入并执行它,仍然得到符合设计时预期的结果。与多线程并发执行的线程安全不一样,可重入强调对单个线程执行时从新进入同一个子程序仍然是安全的。安全
通常而言,可重入的函数必定是线程安全的,反之则不必定成立。在不加锁的前提下,若是一个函数用到了全局或静态变量,那么它不是线程安全的,也不是可重入的。若是咱们加以改进,对全局变量的访问加锁,此时它是线程安全的但不是可重入的,由于一般的加锁方式是针对不一样线程的访问(如Java的synchronized),当同一个线程屡次访问就会出现问题。只有当函数知足可重入的四条条件时,才是可重入的。多线程
回到引言里的问题,若是一个获取锁的线程调用其它的synchronized修饰的方法,会发生什么?并发
从设计上讲,当一个线程请求一个由其余线程持有的对象锁时,该线程会阻塞。当线程请求本身持有的对象锁时,若是该线程是重入锁,请求就会成功,不然阻塞。函数
咱们回来看synchronized,synchronized拥有强制原子性的内部锁机制,是一个可重入锁。所以,在一个线程使用synchronized方法时调用该对象另外一个synchronized方法,即一个线程获得一个对象锁后再次请求该对象锁,是永远能够拿到锁的。操作系统
在Java内部,同一个线程调用本身类中其余synchronized方法/块时不会阻碍该线程的执行,同一个线程对同一个对象锁是可重入的,同一个线程能够获取同一把锁屡次,也就是能够屡次重入。缘由是Java中线程得到对象锁的操做是以线程为单位的,而不是以调用为单位的。线程
以前谈到过,每一个锁关联一个线程持有者和一个计数器。当计数器为0时表示该锁没有被任何线程持有,那么任何线程都均可能得到该锁而调用相应方法。当一个线程请求成功后,JVM会记下持有锁的线程,并将计数器计为1。此时其余线程请求该锁,则必须等待。而该持有锁的线程若是再次请求这个锁,就能够再次拿到这个锁,同时计数器会递增。当线程退出一个synchronized方法/块时,计数器会递减,若是计数器为0则释放该锁。设计