zoukankan      html  css  js  c++  java
  • 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 锁的是引用指向的对象,而非引用本身)

  • 相关阅读:
    vmware和主机通信方法
    曾经的读书计划
    Linux下的autoconf和automake
    Nor Flash读写方法
    Linux下的动态库和静态库
    asp.net读取Xml文件到DataSet,以及DataSet保存为Xml,利用自带的强大功能
    SqlServer 2000/2005 列转行 行转列收集
    GridView 模板列 在后台获取该行某控件的值 例如批量修改
    C# DllImport的用法 调用Window的一些常用功能
    C#通用数据库操作类 支持Access/MSSql/Orale/MySql等数据库
  • 原文地址:https://www.cnblogs.com/lkc9/p/12499134.html
Copyright © 2011-2022 走看看