Java并发之线程八锁

一、引入

先了解什么是synchronized
不加 synchronzied 的方法就好比不遵守规则的人,不去老实排队(好比翻窗户进去的),记住一点,synchronized不是锁住了方法,而是锁住了使用了该方法的类对象或者对象实例,如:
1. 锁住对象实例:

class Test{
    public synchronized void test() {
    }
}
//等价于
class Test{
    public void test() {
        synchronized(this) {
        }
    }
}

2. 锁住类对象:

class Test{
	public synchronized static void test() {
	}
}
//等价于
class Test{
    public static void test() {
        synchronized(Test.class) {
        }
    }
}

线程八锁的重点:

  • 非静态方法的默认锁是this,静态方法的默认锁是class
  • 某一时刻内,只能有一个线程有锁,无论几个方法
  • 同一个类对象中的静态方法和非静态方法上锁,即对象实例上锁和类对象上锁,之间不会冲突
  • 不同对象实例间非静态方法执行不会冲突,非静态方法属于实例
  • 不同对象实例间静态方法执行会冲突,静态方法属于类

二、“八锁”

1. 对象实例:

情况1:12 或 21

@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();
}

解释:
线程数:2个线程
锁:对象实例
两个线程是对同一个实例对象上锁,a()方法或b()方法谁都可能先执行,先执行的会上锁,后面线程得等前一个线程结束才可以执行,通准确来说是同步,通俗讲是串行

情况2:1s后12,或 2 1s后 1

@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();
}

解释:
线程数:2个线程
锁:对象实例
两个线程是对同一个实例对象上锁,a()方法的sleep并不是线程阻塞,是这个线程在运行中模拟有这么一段处理时间,a()方法或b()方法谁都可能先执行,先执行的会上锁,后面线程得等前一个线程结束才可以执行

情况3:3 1s 12 或 23 1s 1 或 32 1s 1

@Slf4j(topic = "c.Number")
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个线程
锁:对象实例
a()方法和b()方法是对同一个实例对象上锁,两者不能并行运行,只能串行执行,c()方法没有锁,a()方法或b()运行时不影响c()方法并行运行,反过来c()方法运行a()b()方法依旧可以运行
通过并行运行可以得出,最先输出的只能是2或3,因为a()方法可以先执行,但不会先输出1,如果对哪个方法先执行有疑虑,我可以告诉你,哪个方法都可以先执行,只是只能通过输出来看

情况4:2 1s 后 1

@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个线程
锁:对象实例
两个线程是对不同实例对象上锁,线程之间不会影响,可以并行执行,但b()方法比a()更早输出

2. 对象实例和类对象混合:

情况1: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个线程
锁:对象实例和类对象
引入那里我已经给大家补了,a()上的锁是类对象,b()方法上的锁是实例对象,即重点第三点,两者是独立执行,谁都不会阻塞,因此,b()方法输出比a()更快

情况2:1s 后12, 或 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();
    new Thread(()->{ n1.a(); }).start();
    new Thread(()->{ n1.b(); }).start();
}

解释:
线程数:2个线程
锁:类对象
两个线程是对同一个类对象上锁,先执行的上锁,后面的阻塞,因此先执行第一个线程,第二个才可以执行,结果会出现两种

情况3: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();
    Number n2 = new Number();
    new Thread(()->{ n1.a(); }).start();
    new Thread(()->{ n2.b(); }).start();
}

解释:
线程数:2个线程
锁:对象实例和类对象
两个线程执行的是不同对象的方法,非静态方法和静态方法(类和实例)、非静态方法和非静态方法(实例与实例,两个不同实例)之间不会冲突,可以并行运行,并行是相对于多核的,单核那叫时间片轮询执行,看起来也像是并行,不过是内核在做上下文切换,影响的是性能

情况4:1s 后12, 或 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个线程
锁:类对象
与情况二类似,上锁的根本在于同一个类,对不同实例是没有毛钱关系,所以这里也会有上锁,会发生阻塞

三、总结

线程八锁,实际是总结出来也就是,上锁【类、实例】和线程执行方法【是否同实例】的划分,而增加sleep是为了能够更好的展示效果,能够更好去理解线程。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嗝屁小孩纸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值