易混淆的锁知识

前言

跟着狂神学JUC,结合狂神提出的关于锁的几点易混淆的知识方面,结合自己的理解,写一些自己的心得体会。

狂神说的八锁

八锁现象

狂神代码和理解分析

1、synchronized锁方法的调用者(单资源)

一个资源调用两个加锁的方法,其打印信息看下列代码案例:

package demo3;

import java.util.concurrent.TimeUnit;

public class Test01 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
    	// 同一个资源
        Computer computer = new Computer();
        new Thread(()->{
    
    computer.watch();},"A").start();

        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
    
    computer.study();},"B").start();
    }
}

class Computer{
    
    
    public synchronized void watch(){
    
    
        System.out.println("看电影。。。。");
    }
    public synchronized void study(){
    
    
        System.out.println("学习。。。");
    }
}

在这里插入图片描述
如果说是因为调用顺序问题,接下来看下列代码:

package demo3;

import java.util.concurrent.TimeUnit;

public class Test02 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Computer2 computer = new Computer2();
        new Thread(()->{
    
    computer.watch();},"A").start();

        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
    
    computer.study();},"B").start();
    }
}

class Computer2{
    
    
	// synchronized 锁的对象是方法的调用者
    // 两个方法,由于被调用,是同一个资源,导致谁先获取到锁,谁先执行!
    public synchronized void watch(){
    
    
        // 延迟 4秒
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
        System.out.println("看电影。。。。");
    }
    public synchronized void study(){
    
    
        System.out.println("学习。。。");
    }
}

在这里插入图片描述
[问:]即使在watch()中加了延迟,针对两个不同的线程,打印出的信息为什么依旧存在顺序?

这里的操作者 computer 是同一资源!
1、由于线程A随系统启动,立刻就能调用watch();
2、由于线程A调用的watch()方法存在延迟4s等待。此时虽然是主线程1s等待结束,但线程B依旧还在等A释放锁!

[总结:]

synchronized 锁的对象是方法的调用者!

2、同时存在锁和不锁的打印情况(单资源)

一个资源中,存在加锁操作方法和不加锁的普通方法,其代码逻辑如下所示:

package demo3;

import java.util.concurrent.TimeUnit;

public class Test03 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Computer3 computer = new Computer3();
        new Thread(()->{
    
    computer.watch();},"A").start();

        TimeUnit.SECONDS.sleep(1);
        
        // 调用了其中无锁的方法
        new Thread(()->{
    
    computer.talk();},"B").start();
    }
}

class Computer3{
    
    
    // synchronized 锁的对象是方法的调用者
    // 两个方法,由于被调用,是同一个资源,导致谁先获取到锁,谁先执行!
    public synchronized void watch(){
    
    
        // 延迟 4秒
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
        System.out.println("看电影。。。。");
    }
    public synchronized void study(){
    
    
        System.out.println("学习。。。");
    }
    
    // 没有锁,不是同步方法,不受锁的影响!
    public void talk(){
    
    
        System.out.println("聊天。。。。");
    }
}

在这里插入图片描述
[结论:]

由于存在无锁的普通方法,其不受锁的影响!

3、多资源调用不同加锁方法

多个资源对象调用不同的加锁方法,逻辑如下所示:

package demo3;

import java.util.concurrent.TimeUnit;

public class Test04 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        // 两个资源
        Computer4 computer1 = new Computer4();
        Computer4 computer2 = new Computer4();
        
        new Thread(()->{
    
    computer1.watch();},"A").start();

        TimeUnit.SECONDS.sleep(1);

        // 调用了其中无锁的方法
        new Thread(()->{
    
    computer2.study();},"B").start();
    }
}

class Computer4{
    
    
    // synchronized 锁的对象是方法的调用者
    // 两个方法,由于被调用,是同一个资源,导致谁先获取到锁,谁先执行!
    public synchronized void watch(){
    
    
        // 延迟 4秒
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
        System.out.println("看电影。。。。");
    }
    public synchronized void study(){
    
    
        System.out.println("学习。。。");
    }

}

在这里插入图片描述
[总结:]

1、两个不同的资源,就是两个不同的调用者!
2、synchronized 锁的是同一个调用者,即针对相同资源的加锁!

4、static修饰带synchronized的方法(单资源)

采取一个实例化对象资源,分别开启不同的线程,调用不同的static synchronized方法。

package demo3;

import java.util.concurrent.TimeUnit;

/**
 * 一个对象,调用两个不同的static 同步方法
 */
public class Test05 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Computer5 computer = new Computer5();
        new Thread(()->{
    
    computer.watch();},"A").start();

        TimeUnit.SECONDS.sleep(1);

        // 调用了其中无锁的方法
        new Thread(()->{
    
    computer.study();},"B").start();
    }
}

/**
 * 两个静态同步方法
 */
class Computer5{
    
    
    // synchronized 锁的对象是方法的调用者
    // 两个方法,由于被调用,是同一个资源,导致谁先获取到锁,谁先执行!
    public static synchronized void watch(){
    
    
        // 延迟 4秒
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
        System.out.println("看电影。。。。");
    }
    public static synchronized void study(){
    
    
        System.out.println("学习。。。");
    }
}

在这里插入图片描述

5、多资源调用static修饰的synchronized方法

4中,是采取的单资源调用,其中的锁是针对一个调用者而言,所以和1中的结果是一致的。

但是如果是两个资源调用static synchronized修饰的方法呢?

package demo3;

import java.util.concurrent.TimeUnit;

/**
 * 两个对象,调用两个不同的static 同步方法
 */
public class Test06 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Computer6 computer1 = new Computer6();
        Computer6 computer2 = new Computer6();

		// 1、两个资源
        // 2、但是同步方法为 static synchronized
        // 3、static 是静态资源,程序一加载,就保存至jvm中
        // 4、static synchronized 加锁的是class文件,并非是锁的调用者
        // 5、两个不同的资源类,但是class唯一,不同的对象其编译后的文件是同一个!!
        // 6、导致不同对象  static synchronized 依旧保证顺序!
        
        new Thread(()->{
    
    computer1.watch();},"A").start();

        TimeUnit.SECONDS.sleep(1);

        // 调用了其中无锁的方法
        new Thread(()->{
    
    computer2.study();},"B").start();
    }
}

/**
 * 两个静态同步方法
 */
class Computer6{
    
    
    // synchronized 锁的对象是方法的调用者
    // 两个方法,由于被调用,是同一个资源,导致谁先获取到锁,谁先执行!

    // static 表示静态资源,在类初次加载的时候,就会产生放入至jvm内存中
    // static synchronized 这个静态类型的锁,锁的是class!
    public static synchronized void watch(){
    
    
        // 延迟 4秒
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
        System.out.println("看电影。。。。");
    }
    public static synchronized void study(){
    
    
        System.out.println("学习。。。");
    }
}

在这里插入图片描述
此时的运行结果,和3的结果却不相同!!

[问题分析:]

1、类中,static 修饰的方法、变量等属于静态资源类。在程序一加载的时候,就会存在JVM的内存中。
2、此时的 static synchronized 锁的是class对象,而并非是针对资源加锁!
3、class 全局唯一!两个对象编译后的class却都是相同的一个!

6、同时存在static锁和正常锁(单资源)

同一个资源调用携带static synchronized和普通的synchronized方法。

package demo3;

import java.util.concurrent.TimeUnit;

/**
 * 两个对象,调用两个不同的static 同步方法
 */
public class Test07 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Computer7 computer = new Computer7();

        // 1、两个资源
        // 2、但是同步方法为 static synchronized
        // 3、static 是静态资源,程序一加载,就保存至jvm中
        // 4、static synchronized 加锁的是class文件,并非是锁的调用者
        // 5、两个不同的资源类,但是class唯一,不同的对象其编译后的文件是同一个!!

        new Thread(()->{
    
    computer.watch();},"A").start();

        TimeUnit.SECONDS.sleep(1);

        // 调用了其中无锁的方法
        new Thread(()->{
    
    computer.study();},"B").start();
    }
}

/**
 * 一个静态同步方法
 */
class Computer7{
    
    
    // synchronized 锁的对象是方法的调用者
    // 两个方法,由于被调用,是同一个资源,导致谁先获取到锁,谁先执行!

    // static 表示静态资源,在类初次加载的时候,就会产生放入至jvm内存中
    // static synchronized 这个静态类型的锁,锁的是class!
    public static synchronized void watch(){
    
    
        // 延迟 4秒
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
        System.out.println("看电影。。。。");
    }
    
    // 这个只是一个普通的锁
    public synchronized void study(){
    
    
        System.out.println("学习。。。");
    }
}

其执行后的日志打印为:
在这里插入图片描述
这个却和预期的相反,就好像这是两个锁?
[问题分析:]

1、static 表示静态,类一加载,就会存在!
2、static synchronized 表示针对class文件加锁。
3、不携带 static 修饰的 synchronized,加锁的是调用者!
4、static synchronized 是class 加锁,单一的 synchronized 只是调用方加锁,这两个是不同的锁

7、多资源消费同时存在static锁和正常锁

单资源中,由于static synchronized (class锁)synchronized (调用者锁)是两个不同的锁,所以开辟两个线程,不会影响其执行顺序!

但是此时如果是两个线程分别调用两个不同的带锁方法呢?

package demo3;

import java.util.concurrent.TimeUnit;

/**
 * 两个对象,调用两个不同的static 同步方法
 */
public class Test08 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Computer8 computer1 = new Computer8();
        Computer8 computer2 = new Computer8();

        // 1、两个资源
        // 2、但是同步方法为 static synchronized
        // 3、static 是静态资源,程序一加载,就保存至jvm中
        // 4、static synchronized 加锁的是class文件,并非是锁的调用者
        // 5、两个不同的资源类,但是class唯一,不同的对象其编译后的文件是同一个!!

        new Thread(()->{
    
    computer1.watch();},"A").start();

        TimeUnit.SECONDS.sleep(1);

        // 调用了其中无锁的方法
        new Thread(()->{
    
    computer2.study();},"B").start();
    }
}

/**
 * 一个静态同步方法
 */
class Computer8{
    
    
    // synchronized 锁的对象是方法的调用者
    // 两个方法,由于被调用,是同一个资源,导致谁先获取到锁,谁先执行!

    // static 表示静态资源,在类初次加载的时候,就会产生放入至jvm内存中
    // static synchronized 这个静态类型的锁,锁的是class!
    public static synchronized void watch(){
    
    
        // 延迟 4秒
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
        System.out.println("看电影。。。。");
    }

    // 这个只是一个普通的锁
    public synchronized void study(){
    
    
        System.out.println("学习。。。");
    }
}

在这里插入图片描述

两个对象互不相关。
带static的synchronized锁的是class文件,任何.java类都只会有一个.class文件
不携带static的synchronized,只是一个调用者的锁!

猜你喜欢

转载自blog.csdn.net/qq_38322527/article/details/114695795