java中锁的概念

1、锁的种类

1.1、自旋锁(属于乐观锁)

为了不放弃cpu执行事件,循环的使用cas技术(在更改值时先再次获取值看值是否与刚才获取的相同,不相同说明被其他线程改变,则不进行操作,进行while循环,直到相同为止,再对值进行操作)对数据尝试进行更新,直到成功。

1.2、悲观锁

假定会发生并发冲突,同步所有对数据的相关操作,从读数据就开始上锁。

1.3、乐观锁

假定没有冲突,在修改数据时,如果发现数据和之前获取的不一样,则读最新数据,重试后修改

1.4、独享锁(写)

给资源加上写锁,线程可以修改资源,其他线程不能再加锁;(单写)

1.5、共享锁(读)

给资源加上读锁后只能读不能改,其他线程也只能加读锁,不能加写锁;(多读)

1.6、可重入锁

线程拿到一把锁之后,可以自由进入同一把锁所同步的其他代码,比如一个方法中的synchronized块中再次调用该方法,再次进入synchronized块,lock也是如此,同一个线程lock获取锁后,可以再次获取,并且lock提供有获取锁的次数的方法,同时释放时,也要释放相应的次数,多次调用unlock
可重入示例:

public class ObjectSyncDemo2 {

    public synchronized void test1(Object arg) {
        // 继续执行,保证能读取到之前的修改 JMM
        System.out.println(Thread.currentThread() + " 我开始执行 " + arg);
        if (arg == null) {
        	//这里再次调用这个带锁的方法,再次获取锁,表示可重入
            test1(new Object());
        }
        System.out.println(Thread.currentThread() + " 我执行结束" + arg);
    }

    public static void main(String[] args) throws InterruptedException {
        new ObjectSyncDemo2().test1(null);
    }
}

1.7、不可重入锁

与可重入锁相反

1.8、公平锁

争抢锁的顺序,如果按先来后到,则为公平

1.9、不公平锁

与公平锁相反

2、同步关键字synchronized

2.1、概念

java中锁的概念_第1张图片

2.1.1、锁粗化:

// 锁粗化(运行时 jit 编译优化)
// jit 编译后的汇编内容, jitwatch可视化工具进行查看
public class ObjectSyncDemo3 {
    int i;

    public void test1(Object arg) {
        synchronized (this) {
            i++;
        /*下边两行原本没有注释,优化后由于主方法里边循环次数太大,会把下边两行给优化掉,扩大锁范围,叫锁粗化
        为了减少加锁的消耗*/
        // }
        // synchronized (this) {
            i++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10000000; i++) {
            new ObjectSyncDemo3().test1("a");
        }
    }
}

2.1.2、锁消除:

public void test1(Object arg) {
        //StringBuffer线程安全,所以方法上加了锁
        // jit 优化, 消除了锁 (Buffer 实例 局部变量,因为时局部变量,所以会将append方法中的锁消除)
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("a");
        stringBuffer.append(arg);
        stringBuffer.append("c");
        // System.out.println(stringBuffer.toString());
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 1000000; i++) {
            new ObjectSyncDemo4().test1("123");
        }
    }

2.2、同步关键字加锁原理

下边mark word的这个表是不同的state(锁状态)中mark word中存的是什么数据(bitfileds),age字段为新生代回收次数,修改state只发生在加锁解锁时。
java中锁的概念_第2张图片
java中锁的概念_第3张图片
java中锁的概念_第4张图片

2.2.1、偏向锁到轻量级锁再到重量级锁

java中锁的概念_第5张图片
关于偏向锁:
如果开启了偏向锁(当然这个偏向锁默认也是开启的),那么第一个线程去访问时,会获取到对象中的thread id(这个thread id是偏向锁的核心,刚创建时默认为0),如果是0,该线程就会获取到锁,将对象中的thread id改为当前线程id,不会进行cas操作,本质就是无锁,开启偏向锁这种情况是因为有些代码虽然加锁了,但是实际场景只出现一个线程,出于优化所以只需要简单的比较thread id就行了,如果这个时候再有其他线程来获取锁时,发现thread id不是自己,这个时候就可以认为有多个线程在争抢锁,就会升级为轻量级锁,这时的thread id就只变成了一个摆设,不再起作用,此时会分两种情况,一种是第一个线程仍然占有锁,那么接下来竞争的线程就会进行CAS操作来等待获取锁,mark word中的数据就会变成轻量级锁状态的数据,另一种是第一个线程已经释放了锁,该对象没有被锁,那么该对象就是无锁状态,所有线程来进行CAS操作获取锁,如果CAS操作一定次数后还没有获取锁,就会升级到重量级锁。

2.2.2、重量级锁-监视器(monitor)

java中锁的概念_第6张图片
monitor源码(JVM底层代码):
java中锁的概念_第7张图片

2.2.3、同步关键字总结

同步关键字,优化内容:
偏向锁(减少在无竞争情况,JVM资源消耗)
出现两个及以上的线程争取->轻量级锁(CAS修改状态)
线程CAS自旋一定次数之后,为了避免过多自旋造成性能影响->重量级锁
对象的mark word 内部会保存一个监视器锁的一个地址,然后每个对象对应一个监视器对象,用到重量级锁的时候对象监视器就起作用了)

对象mark word里面 包含四种状态tag( 00 01 10 11 )
01 无锁
00 轻量锁
10 重量锁
11 GC废弃

java中锁的概念_第8张图片

你可能感兴趣的:(并发,java基础)