Java多线程-synchronized(非this对象)

3个结论

synchronized(非this对象 x) 是将 x 对象本身作为“对象监视器”,因此有如下三个结论:

1)当多个线程同时执行 synchronized(x){}同步代码块时呈同步效果

2)当其他线程执行 x 对象中 synchronized 同步方法时呈同步效果

3)当其他线程执行 x 对象方法里的 synchronized(this) 代码块时也呈现同步效果。

 

静态同步方法 与 synchronized(class)代码块

关键字 synchronized 还可以应用在 static 静态方法上,如果这样写,则是对当前“类对象”进行持锁

注意区别:

  synchronized 作用在非 static 方法上时,是“对象锁”,作用到 static 方法上时,是“Class锁”

示例:

public class SyncNotThis {
    public static void main(String[] args) {
        Service service = new Service();

        Thread t1 = new Thread(() -> {
            Service.printA();
        });
        Thread t2 = new Thread(() -> {
            Service.printB();
        });
        Thread t3 = new Thread(() -> {
            service.printC();
        });
        t1.start();
        t2.start();
        t3.start();
    }

    static class Service {
        /**
         * 静态方法 A
         */
        synchronized public static void printA() {
            try {
                System.out.println("线程名称为:" + Thread.currentThread().getName() + "开始执行 A 方法");
                Thread.sleep(1000);
                System.out.println("线程名称为:" + Thread.currentThread().getName() + "结束执行 A 方法");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        /**
         * 静态方法B
         */
        synchronized public static void printB() {
            try {
                System.out.println("线程名称为:" + Thread.currentThread().getName() + "开始执行 B 方法");
                Thread.sleep(1000);
                System.out.println("线程名称为:" + Thread.currentThread().getName() + "结束执行 B 方法");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        /**
         * 非静态方法C
         */
        synchronized public void printC() {
            try {
                System.out.println("线程名称为:" + Thread.currentThread().getName() + "开始执行 C 方法");
                Thread.sleep(500);
                System.out.println("线程名称为:" + Thread.currentThread().getName() + "结束执行 C 方法");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果如图:

 

 可以发现 方法 A、B的运行呈现同步效果,方法C与 A、B呈现异步效果,异步的原因是方法 C与 方法 A、B持有的是不同的锁

 

synchronized(String对象)的特殊情况

因为JVM中String常量池的存在,会导致 synchronized锁到同一对象

例:

public class SyncString {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            String aa = "AA";
            print(aa);
        });
        Thread t2 = new Thread(() -> {
            String aa = "AA";
            print(aa);
        });
        t1.start();
        t2.start();
    }

    public static void print(String stringParam) {
        synchronized (stringParam) {
            try {
                System.out.println(Thread.currentThread().getName() + "begin");
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + "end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果如下:

 

 因为String 常量池导致了锁的是同一 String 对象,所以两个线程是同步执行的

 

锁对象的改变

示例:

public class LockChange {
    public static void main(String[] args) {
        MyService service = new MyService();
        MyThread t1 = new MyThread(service, "A");
        MyThread t2 = new MyThread(service, "B");
        t1.start();
        t2.start();
    }

    static class MyThread extends Thread {
        private MyService service;

        public MyThread(MyService service, String name) {
            super(name);
            this.service = service;
        }

        @Override
        public void run() {
            service.testMethod();
        }
    }

    static class MyService {
        private String lock = "123";
        public void testMethod() {
            try {
                synchronized (lock) {
                    System.out.println(Thread.currentThread().getName() + " begin " + System.currentTimeMillis());
                    lock = "456";
                    Thread.sleep(2000);
                    System.out.println(Thread.currentThread().getName() + " end " + System.currentTimeMillis());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果如下:

 

 分析:

线程A 和 线程 B持有的锁都是 “123”,虽然 lock 的引用从 "123" 变成了 "456",但是 A 和 B 争抢的锁还是 “123” ,所以尽管线程 A中间改变了 lock 的引用,但是结果还是同步的(或者换一种理解:synchronized 锁的是引用指向的对象,而非引用本身)

posted @ 2020-03-15 18:25  lkc9  阅读(403)  评论(0编辑  收藏  举报