本文共 2020 字,大约阅读时间需要 6 分钟。
互斥锁是为了保证同一个方法同时间只有一个线程去执行,这个也是在多线程开发当中最基本的实现。在java体系当中有很多方法可以实现目的,如: synchronized ,lock ,redis分布式锁,zk分布式锁,基于数据库实现悲观锁等等。 本文将介绍synchronized这个java原生支持的互斥锁。
class X { // 修饰非静态方法 synchronized void foo() { // 临界区 } // 修饰静态方法 synchronized static void bar() { // 临界区 } // 修饰代码块 Object obj = new Object(); void baz() { synchronized(obj) { // 临界区 } }}
这段代码示例中没有出现加锁和解锁的方法,他们的实现是由java编译器在synchronized修饰的方法或者代码块前后自动加上的。
synchronized使用时候有以下几条规则:MyCalc这个类有两个方法:一个是get()方法,用来获取value的值;另一个是addOne()方法,用来给value加1,并且addOne()方法我们用Synchronized修饰。
class SafeCalc { long value = 0L; long get() { return value; } synchronized void addOne() { value += 1; }}
因为addOne()方法被synchronized修饰之后,这个方法就会同时只有一个线程去执行,所以一定能保证原子操作。因为我们的value方法是成员变化,会不会有线程可见性问题呢?
这里就会出来另一个概念: 管程与管程中锁的规则。1. 管程: 就是我们这里的synchronized。
2. 管程中锁的规则: 对一个锁的解锁 Happens-before 做用于后续这个锁的加锁。 第二句话的意思是指 前一个线程的解锁操作对后一个线程的加锁操作是可见的,综合Happens-before的传递性规则,我们就得出前一个线程在临界区修改的共享变更(该操作在解锁之前),对后续进入临界区(该操作在加锁之后)的线程是可见的。按照这个规则,如果多个线程同时操作addOne()这个方法, 可见性是可以保证的,也就是说如果有100 个线程去执行addOne()这个方法,最终的value的结果会是增加了100。
但是此时还是有一个问题,get()我方法的可见性是无法保证的。管程中锁的规则,是只保证后续对这个锁的加锁的可见性,而get()方法并没有加锁操作。所以可见性是无法保证的。我们可以通过给get() 方法加上synchronized修饰来保证可见性。
class SafeCalc { long value = 0L; synchronized long get() { return value; } synchronized void addOne() { value += 1; }}
更改后的模型:
package com.bdf.blog.thread.Synchronized;/** * @program: blog * @description: * @author: canghaihongxin * @create: 2019-04-21 12:24 **/public class SafeCacl { static int value ; public synchronized int getValue() { return value; } public synchronized static void addOne(){ value +=1; }}
这样就是一个错误的代码示例, 当我们在addOne()方法上添加了static 关键字的时候就Synchronized锁的就是我SafeCacl.class这个类,也就是类锁。synchronized锁的是this。
这个时候getValue()方法就无法保证可见性了。转载地址:http://gjkws.baihongyu.com/