synchronized关键字

引自B站楠哥:https://www.bilibili.com/video/BV18Z4y1P7N4

https://blog.csdn.net/weixin_44882846/article/details/114681855

结论

  1. 修饰静态方法时 锁类本身
  2. 修饰非静态方法时 谁使用锁谁
  3. 修饰代码块时 传谁锁谁

1、修饰方法时

静态方法

  1. 用类直接在两个线程中调用两个不同的同步方法
    1.产生互斥。
    2.因为对静态对象加锁实际上对类(.class)加锁,类对象只有一个,可以理解为任何时候都只有一个空间,里面有N个房间,一把锁,因此房间(同步方法)之间一定是互斥的。
    注:上述情况和用单例模式声明一个对象来调用非静态方法的情况是一样的,因为永远就只有这一个对象。所以访问同步方法之间一定是互斥的。
    2.用一个类的静态对象在两个线程中调用静态方法或非静态方法

    1. 产生互斥。
    

    2.因为是一个对象调用,同上。
    3.一个对象在两个线程中分别调用一个静态同步方法和一个非静态同步方法
    1.不会产生互斥。
    2.因为虽然是一个对象调用,但是两个方法的锁类型不同,调用的静态方法实际上是类对象在调用,即这两个方法产生的并不是同一个对象锁,因此不会互斥,会并发执行。

    静态方法

    修饰静态方法,锁的是这个类,所以肯定会排队抢锁。

非静态方法

public class Main1 {
    public static void main(String[] args) {
        for (int i = 0; i < 2; i++) {
            C1 c = new C1();
            new Thread(c::fun).start();
            new Thread(c::fun2).start();
        }
    }
}

class C1 {
    public synchronized void fun() {
        System.out.println("fun1: start...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("fun1: ends...");

    }

    public synchronized void fun2() {
        System.out.println("fun2: start...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("fun2: ends...");
    }
}
//结果:
fun1: start...
fun1: start...
fun1: ends...
fun1: ends...
fun2: start...
fun2: start...
fun2: ends...
fun2: ends...
public class Main2 {
    public static void main(String[] args) {
        C2 c = new C2();
        for (int i = 0; i < 2; i++) {
            new Thread(c::fun).start();
            new Thread(c::fun2).start();
        }
    }
}

class C2 {
    public synchronized void fun() {
        System.out.println("fun1: start...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("fun1: ends...");
    }

    public synchronized void fun2() {
        System.out.println("fun2: start...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("fun2: ends...");
    }
}
//结果:
fun1: start...
fun1: ends...
fun2: start...
fun2: ends...
fun1: start...
fun1: ends...
fun2: start...
fun2: ends...
  • 例一中每次循环都重新new一个对象 两次调用者时两个不同的对象c, 不是同一个实例对象,所以不会发生锁的争抢 。 但是每个c中的funfun2 争夺的确是同一个对象 即this

  • 例二中值new了一个对象, 每次执行方法时调用者都是c, 所以都要去抢夺对象c,锁的是同一个对象,所以排队抢锁。

2、修饰代码块时

2.1 this每次指向同一个对象时

public class Main3 {
    public static void main(String[] args) {
        C3 c = new C3();
        for (int i = 0; i < 2; i++) {
            new Thread(() -> {
                c.fun();
            }).start();
        }
    }
}

class C3 {
    public void fun() {
        synchronized (this) {
            System.out.println("start...");
            try {
                Thread.sleep(1000);
                System.out.println("active...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("ends...");
        }
    }
}
//结果:
start...
active...
ends...
start...
active...
ends...

2.2 this每次指向不同对象时

public class Main4 {
    public static void main(String[] args) {
        for (int i = 0; i < 2; i++) {
            C4 c = new C4();
            new Thread(() -> {
                c.fun();
            }).start();
        }
    }
}

class C4 {
    public void fun() {
        synchronized (this) {
            System.out.println("start...");
            try {
                Thread.sleep(1000);
                System.out.println("active...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("ends...");
        }
    }
}
//结果:
start...
start...
active...
ends...
active...
ends...

2.3 this指向class

public class Main5 {
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            C5 c = new C5();
            new Thread(() -> {
                c.fun();
            }).start();
        }
    }
}

class C5 {
    public void fun() {
        synchronized (Main5.class) {
            System.out.println("start...");
            try {
                Thread.sleep(1000);
                System.out.println("active...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("ends...");
        }
    }
}
//结果:
start...
active...
ends...
start...
active...
ends...
start...
active...
ends...

2.4 传数字 -128-127

public class Main6 {
    public static void main(String[] args) {
        for (int i = 0; i < 2; i++) {
            C6 c = new C6();
            new Thread(() -> {
                c.fun(127);
            }).start();
        }
    }
}

class C6 {
    public void fun(Integer num) {
        synchronized (num) {
            System.out.println("start...");
            try {
                Thread.sleep(1000);
                System.out.println("active...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("ends...");
        }
    }
}
//结果:
start...
active...
ends...
start...
active...
ends...

2.5 传不在-128-127之间的数字

public class Main7 {
    public static void main(String[] args) {
        for (int i = 0; i < 2; i++) {
            C7 c = new C7();
            new Thread(()->{
                c.fun(128);
            }).start();
        }
    }
}
class C7{
    public void fun(Integer num){
        synchronized(num){
            System.out.println("start...");
            try {
                Thread.sleep(1000);
                System.out.println("active...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("ends...");
        }
    }
}
//结果:
start...
start...
active...
ends...
active...
ends...

2.6 多个代码块锁定同一个类

public class Main8 {
    public static void main(String[] args) {
        C81 c = new C81();
        C82 c2 = new C82();
        for (int i = 0; i < 3; i++) {
            new Thread(c::fun).start();
            new Thread(c2::fun2).start();
        }
    }
}
class C81{
    public void fun(){
        synchronized(Main8.class){
            System.out.println("start...");
            try {
                Thread.sleep(1000);
                System.out.println("active...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("ends...");
        }
    }
}
class C82{
    public void fun2(){
        synchronized(Main8.class){
            System.out.println("fun2 start...");
            try {
                Thread.sleep(1000);
                System.out.println("fun2 active...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("fun2 ends...");
        }
    }
}
//结果:
start...
active...
ends...
fun2 start...
fun2 active...
fun2 ends...
start...
active...
ends...
fun2 start...
fun2 active...
fun2 ends...
start...
active...
ends...
fun2 start...
fun2 active...
fun2 ends...

2.7 总结

  • 1和2都是传入的this为啥执行的结果不一样呢? 因为2中的this都是新new出来的对象 而1中的只有一个对象

  • 4和5都是传入的数字为啥执行的结果不一样呢?因为java中有数字常量池范围在-128127之间。4传入的值在-128127之间,而5的num超出了这个范围所以5每次锁定的都不是同一个

  • 6 中C1 C2两个类中的方法锁定的都是同一个值即Main.class此时当他们同时执行时都需要获得Main.class才可执行。

原文地址:https://www.cnblogs.com/smalldong/p/14527130.html