0
点赞
收藏
分享

微信扫一扫

Java并发之线程八锁

@toc

一、引入

先了解什么是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是为了能够更好的展示效果,能够更好去理解线程。

四、结束语

与CSDN同步,没有找到所要相关可转至​ 嗝屁小孩纸 或者可以转告我,不理解的可以互相探讨!
 
评论区可留言,可私信,可互相交流学习,共同进步,欢迎各位给出意见或评价,本人致力于做到优质文章,感谢各位!

举报

相关推荐

0 条评论