【JUC剖析】八锁现象 探析

Youzg LOGO

欢迎来到 (Java版)“八年抗战” 现场!

经历了本篇博文,相信同学们对于 锁 的理解就很彻底了!
别骗我

那么,现在就开始内容的讲解吧:

两个synchronized方法:

synchronized 锁的对象 是 方法调用者
两个方法共用一把锁,谁先拿到锁,谁先执行

例如:

package edu.youzg.core;

import java.util.concurrent.TimeUnit;

/**
 * 8锁现象
 * @Author: Youzg
 * @CreateTime: 2020-07-30 08:20
 * @Description: 带你深究Java的本质!
 */
public class EightLock {
    private static Desktop desktop = new Desktop();

    public static void main(String[] args) {
        new Thread(() -> {
            desktop.chat();
        }, "线程1").start();

        new Thread(() -> {
            desktop.game();
        }, "未加锁方法").start();
    }
}

class Desktop {

    // synchronized 锁的对象 是 方法调用者
    // 两个方法共用一把锁,谁先拿到锁,谁先执行
    // 锁资源 就是 主线程中new的 desktop对象
    public synchronized void chat() {
        System.out.println("聊天功能");
    }

    public synchronized void game() {
        System.out.println("游戏功能");
    }

}

如上面的代码,锁资源 就是 主线程中new的desktop对象

运行结果1

这样的结果,相信很多同学都能预料到
那么接下来,就慢慢加大难度了


synchronized同步方法 + 前一个方法内部延迟:

synchronized 锁的对象 是 方法调用者
两个方法共用一把锁,谁先拿到锁,谁先执行

例如:

package edu.youzg.core;

import java.util.concurrent.TimeUnit;

/**
 * 8锁现象
 * @Author: Youzg
 * @CreateTime: 2020-07-30 08:20
 * @Description: 带你深究Java的本质!
 */
public class EightLock {
    private static Desktop desktop = new Desktop();

    public static void main(String[] args) {
        new Thread(() -> {
            desktop.chat();
        }, "线程1").start();

        new Thread(() -> {
            desktop.game();
        }, "未加锁方法").start();
    }
}

class Desktop {

    // synchronized 锁的对象 是 方法调用者
    // 两个方法共用一把锁,涉嫌拿到锁,谁先执行
    public synchronized void chat() {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("聊天功能");
    }

    public synchronized void game() {
        System.out.println("游戏功能");
    }

}

运行结果2
可以看到:和我们的分析一致!


普通方法 + synchronized同步方法:

synchronized 锁方法 与 普通方法
不竞争锁资源
两个线程竞争cpu时间片段 执行

没劲

例如:

package edu.youzg.core;

import java.util.concurrent.TimeUnit;

/**
 * 8锁现象
 * @Author: Youzg
 * @CreateTime: 2020-07-30 08:20
 * @Description: 带你深究Java的本质!
 */
public class EightLock {
    private static Desktop desktop = new Desktop();

    public static void main(String[] args) {
        new Thread(() -> {
            desktop.chat();
        }, "线程1").start();
        
        new Thread(() -> {
            desktop.video();
        }, "未加锁方法").start();
    }
}

class Desktop {

    // synchronized 锁方法 与 普通方法
    // 不竞争锁资源,谁先执行完谁先输出
    public synchronized void chat() {
        System.out.println("聊天功能");
    }

    public void video() {
        System.out.println("视频功能");
    }

}

运行结果3
可以看到:和我们的分析一致!


两个锁对象:

两个锁对象 => 两个调用对象 => 两把锁
两个线程竞争cpu时间片段 执行

打十个

例如:

package edu.youzg.core;

import java.util.concurrent.TimeUnit;

/**
 * 8锁现象
 * @Author: Youzg
 * @CreateTime: 2020-07-30 08:20
 * @Description: 带你深究Java的本质!
 */
public class EightLock {
	// 两个对象,两把锁
    private static Desktop desktop1 = new Desktop();
    private static Desktop desktop2 = new Desktop();

    public static void main(String[] args) {
        new Thread(() -> {
            desktop1.chat();
        }, "线程1").start();

        new Thread(() -> {
            desktop2.game();
        }, "线程2").start();

    }
}

class Desktop {

    public synchronized void chat() {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("聊天功能");
    }

    public synchronized void game() {
        System.out.println("游戏功能");
    }

}

运行结果4
可以看到:
由于我们设置的第一个线程所执行的方法延时太大
因此在临界资源的竞争中,第二个线程竞争到了资源


两个 静态+同步 方法:

static静态方法,在类一旦加载,就初始化了此方法
Desktop 只存在唯一一个 Class对象
因此 两个静态方法 锁 都是 此类的Class对象
因此,按照调用顺序执行

分析

例如:

package edu.youzg.core;

import java.util.concurrent.TimeUnit;

/**
 * 8锁现象
 * @Author: Youzg
 * @CreateTime: 2020-07-30 08:20
 * @Description: 带你深究Java的本质!
 */
public class EightLock {

    public static void main(String[] args) {
        new Thread(() -> {
            Desktop.chat();
        }, "线程1").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            Desktop.game();
        }, "线程2").start();

    }
}

// Desktop 只存在唯一一个 Class对象
// 因此 静态方法 锁的是此类的Class对象
class Desktop {

    // static 方法
    // 类一旦加载,就初始化了此方法
    public static synchronized void chat() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("聊天功能");
    }

    // static 方法
    // 类一旦加载,就初始化了此方法
    public static synchronized void game() {
        System.out.println("游戏功能");
    }

}

运行结果5
可以看到:
和我们预料的是一样的!


两个对象 + 两个 静态+同步 方法:

static静态方法,在类一旦加载,就初始化了此方法
Desktop 只存在唯一一个 Class对象
因此 静态方法 锁 都是 此类的Class对象
因此,按照调用顺序执行

分析2

例如:

package edu.youzg.core;

import java.util.concurrent.TimeUnit;

/**
 * 8锁现象
 * @Author: Youzg
 * @CreateTime: 2020-07-30 08:20
 * @Description: 带你深究Java的本质!
 */
public class EightLock {
	// 两个对象的Class对象只有一个
    private static Desktop desktop1 = new Desktop();
    private static Desktop desktop2 = new Desktop();

    public static void main(String[] args) {
        new Thread(() -> {
            desktop1.chat();
        }, "线程1").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            desktop2.game();
        }, "线程2").start();

    }
}

// Desktop 只存在唯一一个 Class对象
// 因此 静态方法 锁的是此类的Class对象
class Desktop {

    // static 方法
    // 类一旦加载,就初始化了此方法
    public static synchronized void chat() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("聊天功能");
    }

    // static 方法
    // 类一旦加载,就初始化了此方法
    public static synchronized void game() {
        System.out.println("游戏功能");
    }

}

运行结果6


普通同步方法 + 静态同步方法:

静态方法 的 锁 是 此类的Class对象
普通同步方法 的 锁 不是本类Class对象
两个方法的锁不一样,互不影响

分析3

package edu.youzg.core;

import java.util.concurrent.TimeUnit;

/**
 * 8锁现象
 * @Author: Youzg
 * @CreateTime: 2020-07-30 08:20
 * @Description: 带你深究Java的本质!
 */
public class EightLock {
    private static Desktop desktop = new Desktop();

    public static void main(String[] args) {
        new Thread(() -> {
            desktop.chat();
        }, "线程1").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            desktop.game();
        }, "线程2").start();

    }
}

// Desktop 只存在唯一一个 Class对象
// 因此 静态方法 锁的是此类的Class对象
class Desktop {

    // static 方法
    // 类一旦加载,就初始化了此方法
    public static synchronized void chat() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("聊天功能");
    }

    // 普通同步方法
    public synchronized void game() {
        System.out.println("游戏功能");
    }
    
}

运行结果7


两个对象 + 静态同步方法 + 普通同步方法:

两个对象的Class对象只有一个
但是 锁不相同(一个是Class对象,一个是this)
两个方法的锁不一样,互不影响

分析4

package edu.youzg.core;

import java.util.concurrent.TimeUnit;

/**
 * 8锁现象
 * @Author: Youzg
 * @CreateTime: 2020-07-30 08:20
 * @Description: 带你深究Java的本质!
 */
public class EightLock {
    // 两个对象的Class对象只有一个
    // 但是 锁不相同
    private static Desktop desktop1 = new Desktop();
    private static Desktop desktop2 = new Desktop();

    public static void main(String[] args) {
        new Thread(() -> {
            desktop1.chat();
        }, "线程1").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            desktop2.game();
        }, "线程2").start();

    }
}

// Desktop 只存在唯一一个 Class对象
// 因此 静态方法 锁的是此类的Class对象
class Desktop {

    // static 方法
    // 类一旦加载,就初始化了此方法
    public static synchronized void chat() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("聊天功能");
    }

    // 普通同步方法
    public synchronized void game() {
        System.out.println("游戏功能");
    }

}

运行结果8


技术总结:

  1. 当一个线程 试图 访问同步代码块时,
    它首先必须得到锁退出抛出异常时必须释放锁
  2. Java 中的每一个对象都可以作为锁;
    普通同步方法this
    静态同步方法Class
    同步方法块括号
  3. 锁的对象不是同一个,就直接按照线程执行的快慢来决定;
    锁的对象是同一个,就按照线程进入的先后顺序决定

到此为止,八锁问题全部解决!
厉害

posted @ 2020-08-06 10:13  在下右转,有何贵干  阅读(112)  评论(0编辑  收藏  举报