并发编程 synchronized (二) 两种锁

目录

对象锁

定义

对象锁例子

在普通方法上加锁(相同对象调用相同方法)

对当前对象加锁(相同对象调用相同方法)

不同对象调用相同方法 && 相同对象调用不同方法

类锁

定义

类锁例子

在静态方法上加锁(不同对象调用相同方法)

对当前类加锁(不同对象调用相同方法)

不同对象调用不同方法

线程八锁

 第一锁

第二锁

第三锁(难点)

第四锁

第五锁(难点)

第六锁

第七锁

第八锁


对象锁

定义

形如:

synchronized void method(){ // 在普通成员方法上加锁
    // 临界区
}

void method(){
    synchronized(this){ // 锁住当前对象
        // 临界区
    }
}

两种方式的临界区,对于同一个对象是等效的

对象锁例子

在普通方法上加锁(相同对象调用相同方法)

package com.juc.ppy.synchronize;

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.MethodSynchronized")
public class MethodSynchronized {
    public MethodSynchronized(){ // 每次创建新对象,都打印出该对象的 hash 码
        log.debug("创建了 MethodSynchronized 新对象 {}", this.hashCode());
    }

    public synchronized void getTime1(){ // 在普通成员方法上加锁
        try{
            log.debug("在普通方法加锁");
            Thread.sleep(2000); // sleep() 不会释放锁,用来证明锁起作用了
        }catch (Exception e){
            e.printStackTrace();
        }
        log.debug("结束");
    }
}

class Test3{
    public static void main(String[] args) throws InterruptedException {
        MethodSynchronized ms = new MethodSynchronized();

        Thread t1 = new Thread(() -> { // 线程 1 调用 getTime1()
            ms.getTime1();
        }, "t1");

        Thread t2 = new Thread(() -> { // 线程 2 调用 getTime1()
            ms.getTime1();
        }, "t2");

        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

① 第 2 秒创建出对象 ms

② 紧接着线程 t1 获取到锁,开始执行;第 4 秒结束,释放锁

③  线程 t2 获取到锁,开始执行;第 6 秒结束

综上:t1、t2 共用 ms 这一把锁

对当前对象加锁(相同对象调用相同方法)

@Slf4j(topic = "c.MethodSynchronized")
public class MethodSynchronized {
    public MethodSynchronized(){ // 每次创建新对象,都打印出该对象的 hash 码
        log.debug("创建了 MethodSynchronized 新对象 {}", this.hashCode());
    }

    public void getTime2(){
        synchronized (this){ // 在当前对象上加锁
            try{
                log.debug("synchronized (this)");
                Thread.sleep(2000);
            }catch (Exception e){
                e.printStackTrace();
            }
            log.debug("结束");
        }
    }
}

class Test4{
    public static void main(String[] args) throws InterruptedException {
        MethodSynchronized ms = new MethodSynchronized();

        Thread t1 = new Thread(() -> {
            ms.getTime2();
        }, "t1");

        Thread t2 = new Thread(() -> {
            ms.getTime2();
        }, "t2");

        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

情况与上面一样,说明这两种加锁方式是等效的 

不同对象调用相同方法 && 相同对象调用不同方法

@Slf4j(topic = "c.MethodSynchronized")
public class MethodSynchronized {
    public MethodSynchronized(){ // 每次创建新对象,都打印出该对象的 hash 码
        log.debug("创建了 MethodSynchronized 新对象 {}", this.hashCode());
    }

    public void method(){
        synchronized (this){ // 在当前对象上加锁
            try{
                log.debug("先芜湖~");
                Thread.sleep(2000);
            }catch (Exception e){
                e.printStackTrace();
            }
            log.debug("再起飞!");
        }
    }
}

class Test6{
    public static void main(String[] args) throws InterruptedException {
        MethodSynchronized ms1 = new MethodSynchronized();
        MethodSynchronized ms2 = new MethodSynchronized();

        Thread t1 = new Thread(() -> {
            ms1.method();
        }, "t1");

        Thread t2 = new Thread(() -> {
            ms2.method();
        }, "t2");

        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

① 创建了 ms1、ms2 两个对象

② 第 59 秒 t1、t2 几乎同时执行

③ 第 1 秒 t1、t3 几乎同时结束

综上:锁没起作用

@Slf4j(topic = "c.MethodSynchronized")
public class MethodSynchronized {
    public MethodSynchronized(){ // 每次创建新对象,都打印出该对象的 hash 码
        log.debug("创建了 MethodSynchronized 新对象 {}", this.hashCode());
    }

    public synchronized void getTime1(){ // 在普通成员方法上加锁
        try{
            log.debug("在普通方法加锁");
            Thread.sleep(2000); // sleep() 不会释放锁,用来证明锁起作用了
        }catch (Exception e){
            e.printStackTrace();
        }
        log.debug("结束");
    }

    public void getTime2(){
        synchronized (this){ // 在当前对象上加锁
            try{
                log.debug("synchronized (this)");
                Thread.sleep(2000);
            }catch (Exception e){
                e.printStackTrace();
            }
            log.debug("结束");
        }
    }
}

class Test7{
    public static void main(String[] args) throws InterruptedException {
        MethodSynchronized ms = new MethodSynchronized();

        Thread t1 = new Thread(() -> { // t1 调用方法 1
            ms.getTime1();
        }, "t1");

        Thread t2 = new Thread(() -> { // t2 调用方法 2
            ms.getTime2();
        }, "t2");

        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

总结:同一个对象属于同一把锁

① 不同对象调用相同方法,锁不起作用;是因为 ms1、ms2 相当于两把锁,分别被 t1、t2 获取;锁不同,临界区就不一样,它们相互之间是不影响的,各执行各的

② 相同对象调用不同方法,锁起作用;对象相同,锁就相同,临界区就相同

类锁

定义

形如:

static synchronized void method(){ // 在静态成员方法上加锁
    // 临界区
}

void method(){
    synchronized(类名.class){ // 对当前类加锁
        // 临界区
    }
}

两种方式的临界区,对于同一个类是等效的

类锁例子

在静态方法上加锁(不同对象调用相同方法)

@Slf4j(topic = "c.ClassSynchronized")
public class ClassSynchronized {
    public ClassSynchronized(){ // 每次创建新对象,都打印出该对象的 hash 码
        log.debug("创建了 ClassSynchronized 新对象 {}", this.hashCode());
    }

    private static synchronized void getTime0(){ // 在静态成员方法上加锁
        try{
            log.debug("在静态方法加锁");
            Thread.sleep(2000); // sleep() 不会释放锁,用来证明锁起作用了
        }catch (Exception e){
            e.printStackTrace();
        }
        log.debug("结束");
    }

    public void getTime(){ // 最终调用的方法;用来证明:即使是不同对象,锁也是一样的
        getTime0();
    }
}

class Test0{
    public static void main(String[] args) throws InterruptedException {
        ClassSynchronized cs1 = new ClassSynchronized(); // 创建对象 1
        ClassSynchronized cs2 = new ClassSynchronized(); // 创建对象 2

        Thread t1 = new Thread(() -> {
            cs1.getTime(); // 线程 1 中调用对象 1 的普通方法
        }, "t1");

        Thread t2 = new Thread(() -> {
            cs2.getTime();
        }, "t2"); // 线程 2 中调用对象 2 的普通方法

        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

① 第 36 秒创建出对象 cs1、cs2

② 紧接着线程 t1 获取到锁,开始执行;第 38 秒结束,释放锁

③  线程 t2 获取到锁,开始执行;第 40 秒结束

综上:虽然 cs1、cs2 是两个不同对象,但是属于同一个类,同一个临界区,t1、t2 它们共用一把类锁

对当前类加锁(不同对象调用相同方法)

@Slf4j(topic = "c.ClassSynchronized")
public class ClassSynchronized {
    public void getTime1(){
        synchronized (ClassSynchronized.class){ // 直接锁住当前类
            try{
                log.debug("synchronized (ClassSynchronized.class)");
                Thread.sleep(2000);
            }catch (Exception e){
                e.printStackTrace();
            }
            log.debug("结束");
        }
    }
}

class Test1{
    public static void main(String[] args) throws InterruptedException {
        ClassSynchronized cs1 = new ClassSynchronized();
        ClassSynchronized cs2 = new ClassSynchronized();

        Thread t1 = new Thread(() -> {
            cs1.getTime1();
        }, "t1");

        Thread t2 = new Thread(() -> {
            cs2.getTime1();
        }, "t2");

        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

情况与上面一样,说明这两种加锁方式是等效的 

不同对象调用不同方法

@Slf4j(topic = "c.ClassSynchronized")
public class ClassSynchronized {
    public ClassSynchronized(){ // 每次创建新对象,都打印出该对象的 hash 码
        log.debug("创建了 ClassSynchronized 新对象 {}", this.hashCode());
    }

    public void method1(){ // 方法 1 芜湖
        synchronized (ClassSynchronized.class){ // 直接锁住当前类
            try{
                log.debug("芜湖~");
                Thread.sleep(2000);
            }catch (Exception e){
                e.printStackTrace();
            }
            log.debug("芜湖~");
        }
    }

    public void method2(){ // 方法 2 起飞
        synchronized (ClassSynchronized.class){ // 直接锁住当前类
            try{
                log.debug("起飞!");
                Thread.sleep(2000);
            }catch (Exception e){
                e.printStackTrace();
            }
            log.debug("起飞!");
        }
    }
}

class Test2{
    public static void main(String[] args) throws InterruptedException {
        ClassSynchronized cs1 = new ClassSynchronized();
        ClassSynchronized cs2 = new ClassSynchronized();

        Thread t1 = new Thread(() -> {
            cs1.method1(); // cs1 调用 芜湖
        }, "t1");

        Thread t2 = new Thread(() -> {
            cs2.method2(); // cs2 调用 起飞
        }, "t2");

        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

不同对象,调用不同方法,锁依旧起作用;足以说明对于同一个类都属于同一把锁 

线程八锁

 第一锁

@Slf4j(topic = "c.Number")
class Number{
    public synchronized void a() {
        log.debug("1");
    }
    public synchronized void b() {
        log.debug("2");
    }
}
public static void main(String[] args) {
    Number n1 = new Number();
    new Thread(()->{ n1.a(); }).start();
    new Thread(()->{ n1.b(); }).start();
}

结果:12 21

第二锁

@Slf4j(topic = "c.Number")
class Number{
    public synchronized void a() {
        sleep(1);
        log.debug("1");
    }
    public synchronized void b() {
        log.debug("2");
    }
}
public static void main(String[] args) {
    Number n1 = new Number();
    new Thread(()->{ n1.a(); }).start();
    new Thread(()->{ n1.b(); }).start();
}

结果:1s 后 122 过 1s 后 1

第三锁(难点)

class Number{
    public synchronized void a() {
        sleep(1);
        log.debug("1");
    }
    public synchronized void b() {
        log.debug("2");
    }
    public void c() {
        log.debug("3");
    }
}
public static void main(String[] args) {
    Number n1 = new Number();
    new Thread(()->{ n1.a(); }).start();
    new Thread(()->{ n1.b(); }).start();
    new Thread(()->{ n1.c(); }).start();
}

结果:3 过 1s 后 1232 过 1s 后 123 过 1s 后 1

说明:方法 c 没有加锁sleep 会释放时间片,就算第二个线程获得了时间片,由于没有锁进入阻塞;所以不可能出现 "13" 这种结果

第四锁

@Slf4j(topic = "c.Number")
class Number{
    public synchronized void a() {
        sleep(1);
        log.debug("1");
    }
    public synchronized void b() {
        log.debug("2");
    }
}
public static void main(String[] args) {
    Number n1 = new Number();
    Number n2 = new Number();
    new Thread(()->{ n1.a(); }).start();
    new Thread(()->{ n2.b(); }).start();
}

结果:2 1s 后 1

第五锁(难点)

@Slf4j(topic = "c.Number")
class Number{
    public static synchronized void a() {
        sleep(1);
        log.debug("1");
    }
    public synchronized void b() {
        log.debug("2");
    }
}
public static void main(String[] args) {
    Number n1 = new Number();
    new Thread(()->{ n1.a(); }).start();
    new Thread(()->{ n1.b(); }).start();
}

结果:2 1s 后 1

说明:a 加的是类锁,b 加的是对象锁相当于不同锁,互不影响

第六锁

@Slf4j(topic = "c.Number")
class Number{
    public static synchronized void a() {
        sleep(1);
        log.debug("1");
    }
    public static synchronized void b() {
        log.debug("2");
    }
}
public static void main(String[] args) {
    Number n1 = new Number();
    new Thread(()->{ n1.a(); }).start();
    new Thread(()->{ n1.b(); }).start();
}

结果:2 1s 后 11s 后 12

第七锁

@Slf4j(topic = "c.Number")
class Number{
    public static synchronized void a() {
        sleep(1);
        log.debug("1");
    }
    public synchronized void b() {
        log.debug("2");
    }
}
public static void main(String[] args) {
    Number n1 = new Number();
    Number n2 = new Number();
    new Thread(()->{ n1.a(); }).start();
    new Thread(()->{ n2.b(); }).start();
}

结果:2 1s 后 1 

第八锁

@Slf4j(topic = "c.Number")
class Number{
    public static synchronized void a() {
        sleep(1);
        log.debug("1");
    }
    public static synchronized void b() {
        log.debug("2");
    }
}
public static void main(String[] args) {
    Number n1 = new Number();
    Number n2 = new Number();
    new Thread(()->{ n1.a(); }).start();
    new Thread(()->{ n2.b(); }).start();
}

结果:2 1s 后 11s 后 12

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值