【多线程核心技术】---synchronized

线程安全

1:多个线程共同访问一个对象上的方法内部的私有变量(局部变量),方法中的变量不存在 非线程安全问题,永远是线程安全的。这是因为方法内部的变量是私有的特性造成的。

实例变量非线程安全:

2:多个线程共同访问一个对象的实例变量(成员变量),

多个线程共同访问一个对象的中的同步方法时一定是线程安全的。 方法前加synchronized关键字

3:多个线程访问多个锁:

 *两个线程分别访问同一个类(同一个对象的相同名称的同步方法)的两个不同实例的(创建两个)相同名称的同步方法,效果是以异步的方式运行的

两个线程分别访问两个对象的同一个的同步方法进行访问。

 * --》两个业务对象在系统产生了两个锁,所以是异步的
 *关键字synchronized取得的锁是对象锁,而不是一段代码或者方法当作锁,那个线程先执行带synchronized关键字的方法,
 *那个线程就持有该方法所属对象的锁Lock,那么其他线程只能持等待状态,前提是多个线程访问的是同一个对象。
 *但是如果多个线程访问多个对象,则JVM会创建多个锁。

4:synchronized方法与锁对象

调用用关键字synchronized声明的方法一定是排队执行的。只有共享资源的读写访问才需要同步化。

两个线程分别访问同一个对象的的两个方法(一个同步,一个不同步),

线程A先持有Object对象的锁,线程B完全可以异步调用非synchronized类型的方法。

两个线程分别访问同一个对象的的两个的同步方法,

线程A先持有Object对象的Lock锁,线程B如果在这时调用Object对象中的synchronized类型的方法则需等待,也就是同步。

一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,
在Java里边就是拿到某个同步对象的锁(一个对象只有一把锁);
如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池等待队列中)。
取到锁后,他就开始执行同步代码(被synchronized修饰的代码);
线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中等待的某个线程就可以拿到锁执行同步代码了。

事实上,synchronized修饰非静态方法、同步代码块的synchronized (this)用法和synchronized (非this对象)的用法锁的是对象,线程想要执行对应同步代码,需要获得对象锁。

synchronized修饰静态方法以及同步代码块的synchronized (类.class)用法锁的是类,线程想要执行对应同步代码,需要获得类锁。

私有锁:在类内部声明一个私有属性如private Object lock,在需要加锁的代码段synchronized(lock)。

回顾基础知识,Java中的每一个对象都可以作为锁,而不同的场景锁是不一样的。

  1. 对于实例同步方法,锁是当前实例对象。
  2. 对于静态同步方法,锁是当前对象的Class对象。
  3. 对于同步方法块,锁是Synchonized括号里配置的对象

5:脏读:

多个线程在读取实例变量时,赋值时进行了同步,但在取值时其值已经被其他线程更改过了。

        解决方案:getValue()和setValue()方法上加synchronized关键字。


6: synchronized锁重入       

关键字synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的。这也证明在一个synchronized方法/块的内部调用本类的其他synchronized方法/块时,是永远可以得到锁的。

可重入就意味着:线程可以进入任何一个它已经拥有的锁所同步着的代码块。

以下来自博客 https://www.cnblogs.com/cielosun/p/6684775.html

1.2. 可重入

1.2.1. 定义

关于可重入这一概念,我们需要参考维基百科。

若一个程序或子程序可以“在任意时刻被中断然后操作系统调度执行另外一段代码,这段代码又调用了该子程序不会出错”,则称其为可重入(reentrant或re-entrant)的。即当该子程序正在运行时,执行线程可以再次进入并执行它,仍然获得符合设计时预期的结果。与多线程并发执行的线程安全不同,可重入强调对单个线程执行时重新进入同一个子程序仍然是安全的。

1.2.2. 可重入的条件

  • 不在函数内使用静态或全局数据。
  • 不返回静态或全局数据,所有数据都由函数的调用者提供。
  • 使用本地数据(工作内存),或者通过制作全局数据的本地拷贝来保护全局数据。
  • 不调用不可重入函数。

1.3. 可重入与线程安全

一般而言,可重入的函数一定是线程安全的,反之则不一定成立。在不加锁的前提下,如果一个函数用到了全局或静态变量,那么它不是线程安全的,也不是可重入的。如果我们加以改进,对全局变量的访问加锁,此时它是线程安全的但不是可重入的,因为通常的枷锁方式是针对不同线程的访问(如Java的synchronized),当同一个线程多次访问就会出现问题。只有当函数满足可重入的四条条件时,才是可重入的。

2. synchronized的可重入性

2.1. synchronized是可重入锁

回到引言里的问题,如果一个获取锁的线程调用其它的synchronized修饰的方法,会发生什么?

从设计上讲,当一个线程请求一个由其他线程持有的对象锁时,该线程会阻塞。当线程请求自己持有的对象锁时,如果该线程是重入锁,请求就会成功,否则阻塞。

我们回来看synchronized,synchronized拥有强制原子性的内部锁机制,是一个可重入锁。因此,在一个线程使用synchronized方法时调用该对象另一个synchronized方法,即一个线程得到一个对象锁后再次请求该对象锁,是永远可以拿到锁的

在Java内部,同一个线程调用自己类中其他synchronized方法/块时不会阻碍该线程的执行,同一个线程对同一个对象锁是可重入的,同一个线程可以获取同一把锁多次,也就是可以多次重入。原因是Java中线程获得对象锁的操作是以线程为单位的,而不是以调用为单位的。

2.2. synchronized可重入锁的实现

之前谈到过,每个锁关联一个线程持有者和一个计数器。当计数器为0时表示该锁没有被任何线程持有,那么任何线程都都可能获得该锁而调用相应方法。当一个线程请求成功后,JVM会记下持有锁的线程,并将计数器计为1。此时其他线程请求该锁,则必须等待。而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增。当线程退出一个synchronized方法/块时,计数器会递减,如果计数器为0则释放该锁。

关于父类和子类的锁的重入:

   子类完全可以父类的synchonized方法,然后调用父类中的方法,

   子类完全可以通过"可重入锁"调用父类的同步方法,


以下解释来自博客 http://blog.csdn.net/aitangyong/article/details/22695399

 *使用A a = new A()这种方式创建对象的时候,JVM会在后台给我们分配内存空间,然后调用构造函数执行初始化操作,
 *最后返回内存空间的引用。
 *即构造函数只是进行初始化,并不负责分配内存空间(创建对象)。
 *所以呢其实创建子类对象的时候,JVM会为子类对象分配内存空间,
 *并调用父类的构造函数。
 *我们可以这样理解:创建了一个子类对象的时候,在子类对象内存中,
 *有两份数据,一份继承自父类,一份来自子类,但是他们属于同一个对象(子类对象),
 *只不过是java语法提供了this和super关键字来让我们能够按照需要访问这2份数据而已。
 *这样就产生了子类和父类的概念,但实际上只有子类对象,没有父类对象。

可重入锁最大的作用是避免死锁

7:锁的异常

    当一个线程执行的代码出现异常,其所持有的锁会自动释放。

8:同步不具有基础性

    父类同步,子类不能继承父类的同步,子类需在方法中添加synchronized关键字

synchronized和lock的区别:

      Lock 的锁定是通过代码实现的,而 synchronized 是在 JVM 层面上实现的。

      synchronized 在锁定时如果方法块抛出异常,JVM 会自动将锁释放掉,不会因为出了异常没有释放锁造成线程死锁。但是 Lock 的话就享受不到 JVM 带来自动的功能,出现异常时必须在 finally 将锁释放掉,否则将会引起死锁。

      在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronize,另外可读性非常好。


推荐博客:
              http://blog.csdn.net/u013142781/article/details/51697672
             http://www.open-open.com/lib/view/open1433234095994.html
             http://www.open-open.com/lib/view/open1448188558083.html
             http://www.open-open.com/lib/view/open1395238621087.html
     

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值