线程“八锁“ synchronized到底是对哪个对象加锁?

本文通过一系列习题详细分析了Java中synchronized关键字的使用,探讨了其对对象加锁的机制。在不同场景下,synchronized作用于实例方法、静态方法以及未加锁方法时,产生的互斥效果和并发执行的情况。总结了判断线程互斥的关键在于锁的对象是否相同,并强调了任务调度器的影响。
摘要由CSDN通过智能技术生成

线程"八锁" synchronized到底是对哪个对象加锁?

习题一

class Number{
    public synchronized void a(){
        System.out.println("1");
    }

    public synchronized void b(){
        System.out.println("2");
    }
}
public class TestBlock {
    public static void main(String[] args) throws InterruptedException {
        Number n = new Number();
        Thread t1 = new Thread(()->{
            System.out.println("t1->begin!!!");
            n.a();
        });

        Thread t2 = new Thread(()->{
            System.out.println("t2->begin!!!");
            n.b();
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

对于Number类里面的a方法和b方法都是对当前对象this加锁,也就是对Number对象加锁.

当两个线程通过对象调用两个方法(a和b)的时候,会产生互斥效果-->锁的都是同一个对象(Number对象)

对于打印结果取决于任务调度器先调度谁就先打印谁,两者都有机会被优先调度,所以打印顺序可能是 1 2 也可能是2 1.

习题二

class Number1{
    public synchronized void a(){
        System.out.println("1");
    }

    public synchronized void b(){
        System.out.println("2");
    }
}
public class TestBlock1 {
    public static void main(String[] args) throws InterruptedException {
        Number1 n = new Number1();
        Thread t1 = new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t1->begin!!!");
            n.a();
        });

        Thread t2 = new Thread(()->{
            System.out.println("t2->begin!!!");
            n.b();
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

对比习题1就相当于在t1线程里面多加了个sleep,让其t1线程睡眠一会.

但是,t1线程和t2线程锁的还是同一个对象Number具有互斥效果.

打印结果还是取决于任务调度器的调度,如果先调度的是t1(t1先拿到锁),那么就等待1s后,执行a方法打印1,然后线程2在执行b方法,打印2.

还有一种情况是任务调度器先调度t2线程,就会先打印2,然后睡眠1s后打印1.

习题3

class Number3{
    public synchronized void a(){
        System.out.println("1");
    }
    public synchronized void b(){
        System.out.println("2");
    }
    public void c(){//没有加锁
        System.out.println("3");
    }
}
public class TestBlock3 {
    public static void main(String[] args) throws InterruptedException {
        Number3 n = new Number3();
        Thread t1 = new Thread(()->{
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t1->begin!!!");
            n.a();
        });
        Thread t2 = new Thread(()->{
            System.out.println("t2->begin!!!");
            n.b();
        });
        Thread t3 = new Thread(()->{
            System.out.println("t3->begin!!!");
            n.c();
        });
        t1.start();
        t2.start();
        t3.start();
        t1.join();
        t2.join(); 
        t3.join();
    }
}

习题3是多加了一个方法c,但是方法c没有加synchronized.

对于这个题,方法a,b都加了synchronized可以其互斥效果,但是反方c没有加synchronized,不会被加锁.也就是线程1或者线程2执行,线程3不会阻塞,依然会继续往后执行.

对于打印结果还是取决于任务调度器的调度.

如果先调度的是线程1,那么会等待1s后打印1,但是方法c没有加锁,所以不会阻塞,线程3执行方法c打印3,然后1s后打印1 ,线程1释放锁之后唤醒线程2,线程2执行b方法打印2.====> 3 一秒后 1 2

由于线程2和线程3是并发执行的取决于任务调度器

如果先调度的是线程2,那么会先打印 2 ,然后线程3并发/并行的执行方法c打印3,然后一秒后打印1

===> 2 3 一秒后 1

如果先调度的是线程3,那么会先打印 3 ,然后线程2并发/并行的执行方法b打印2,然后一秒后打印1

===> 3 2 一秒后 1

习题4

class Number4{
    public synchronized void a(){
        System.out.println("1");
    }
    public synchronized void b(){
        System.out.println("2");
    }
}
public class TestBlock4 {
    public static void main(String[] args) throws InterruptedException {
        Number4 n1 = new Number4();
        Number4 n2 = new Number4();
        Thread t1 = new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t1->begin!!!");
            n1.a();
        });
        Thread t2 = new Thread(()->{
            System.out.println("t2->begin!!!");
            n2.b();
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

这个题有两个对象n1和n2.

有两个对象,n1.a(),n2.b(),既然是不同的对象,也就是线程1和线程2对不同的对象加锁=>锁的不是同一个对象不产生互斥效果,并发/并行的执行,但由于t1线程睡眠1s钟,所以总是先打印2 一秒后 在打印1

习题5

class Number5{
    public synchronized static void a(){
        System.out.println("1");
    }
    public synchronized void b(){
        System.out.println("2");
    }
}
public class TestBlock5 {
    public static void main(String[] args) throws InterruptedException {
        Number5 n = new Number5();
        Thread t1 = new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t1->begin!!!");
            n.a();
        });
        Thread t2 = new Thread(()->{
            System.out.println("t2->begin!!!");
            n.b();
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

线程1调用a方法,a是静态方法,对静态方法加锁相当于是对类对象加锁-->锁的是Number类对象

线程2调用b方法,b是非静态方法,相当于对this对象加锁->也就是n对象

所以锁的是不同的对象==>对不同的对象进行加锁--->不会产生互斥效果

所以两线程是并发执行的,由于睡眠,先打印2,一秒后打印1

习题6

class Number6 {
    public synchronized static void a(){
        System.out.println("1");
    }
    public synchronized static void b(){
        System.out.println("2");
    }
}
public class TestBlock6 {
    public static void main(String[] args) throws InterruptedException {
        Number6 n = new Number6();
        Thread t1 = new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t1->begin!!!");
            n.a();
        });
        Thread t2 = new Thread(()->{
            System.out.println("t2->begin!!!");
            n.b();
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

两个线程调用的方法都是对静态方法进行加锁==>都是对类对象加锁-->锁的是相同对象-->产生互斥效果.

取决于任务调度器. 先调度线程1就是 1s之后打印1 ,然后打印2,否则的话先调度线程2就是 打印2 ,1s之后然后打印1

习题7

class Number7 {
    public synchronized static void a(){
        System.out.println("1");
    }
    public synchronized  void b(){
        System.out.println("2");
    }
}
public class TestBlock7 {
    public static void main(String[] args) {
        Number7 n1 = new Number7();
        Number7 n2 = new Number7();
        Thread t1 = new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            n1.a();
        });
        Thread t2 = new Thread(()->{
            n2.b();
        });
        t1.start();
        t2.start();
    }
}

加锁的对象不一样,线程1调用的方法对类对象加锁,线程2里面利用n2对象调用b方法,对n2对象加锁,所以两个线程是对不同的对象进行加锁->不产生互斥效果.

所以线程1和线程2是并发执行的,所以是先打印2,一秒后打印1

习题8

class Number8{
    public static synchronized void a() {
        System.out.println("1");
    }
    public static synchronized void b() {
        System.out.println("2");
    }
}
public class TestBlock8 {

    public static void main(String[] args) {
        Number8 n1 = new Number8();
        Number8 n2 = new Number8();
        Thread t1 = new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            n1.a();
        });
        Thread t2 = new Thread(()->{
            n2.b();
        });
        t1.start();
        t2.start();
    }
}

虽然是不同的对象调用方法

但是调用的方法都是对静态方法进行加锁==>都是对类对象加锁-->锁的是相同对象-->产生互斥效果.

取决于任务调度器. 先调度线程1就是 1s之后打印1 ,然后打印2,否则的话先调度线程2就是 打印2 ,1s之后然后打印1

总结

分析这种问题,首先往最根头看起,到底锁的对象是谁,是this还是类对象,还是没有加锁,然后在确定多个对象是否是锁的同一个对象

  • 如果锁的是同一个对象,那么会产生互斥效果,一个线程没有执行完,另外一个线程阻塞等待.
  • 锁的不是同一个对象,那么就不会产生互斥效果,并行的执行
  • 如果没有加锁,那么也是会与其他线程并行的执行

然后具体情况具体分析,可能分为多种情况-->这种情况就要考虑任务调度器的调度执行了.--->分多种情况去考虑即可

参考 :

黑马程序员JUC视频-->哔哩哔哩

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值