美文网首页
Java -> synchronized

Java -> synchronized

作者: 杨0612 | 来源:发表于2020-09-10 16:09 被阅读0次
    synchronized 作用

    (1)互斥性,同一时刻只有获得锁的线程才能访问同步代码,其他线程处于同步状态,这样的互斥也实现了原子性操作;
    (2)可见性,在退出同步代码释放锁之前,对共享变量的修改会立即同步到主存,修改对其他线程可见。

    以下代码,因为没有使用synchronized ,所以多线程访问print方法不会产生互斥,大家可以打印日志看看。

    public class PrintManager {
        public void print() {
            String threadName = Thread.currentThread().getName();
            Log.d("test", "1 threadName=" + threadName);
            Log.d("test", "2 threadName=" + threadName);
            Log.d("test", "3 threadName=" + threadName);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Log.d("test", "4 threadName=" + threadName);
            Log.d("test", "5 threadName=" + threadName);
        }
    }
    
    synchronized 修饰方法

    (1)修饰成员方法
    synchronized修饰print方法,那么当前对象就是锁,当不同线程访问方法时,需要竞争该锁,其他没有获取到锁的线程处于同步阻塞状态。

    注意:案例2,当不同对象访问print方法是不会产生互斥的,因为线程抢占的锁基本不是同一个。
    public synchronized void print() {  
              .......
    }
    
    
    //案例1,以下调用会产生互斥
    PrintManager  printManager=new PrintManager ();
    //线程A执行printManager.print();
    //线程B执行printManager.print();
    
    //案例2,以下调用会产生不会互斥
    PrintManager  printManager1=new PrintManager ();
    PrintManager  printManager2=new PrintManager ();
    //线程A执行printManager1.print();
    //线程B执行printManager2.print();
    

    (2)修饰静态方法
    static synchronized修饰print,那么当前类对象就是锁,当不同线程访问方法时,需要竞争该锁。其实这个锁也是对象,只不过这个对象比较特殊,是PrintManager.class。虚拟机加载每个类都会生成一个.class对象,一个类只有一个.class对象。

    注意:如案例2,通过对象方法print()时,即使是不同对象,也会产生互斥,因为它们抢占的锁是同一个,就是.class,而这个对象是所有实例共享的。
    public static synchronized void print() {  
              .......
    }
    
    
    //案例1,以下调用会产生互斥
    PrintManager  printManager=new PrintManager ();
    //线程A执行PrintManager  .print();
    //线程B执行PrintManager  .print();
    
    //案例2,以下调用也会产生互斥
    PrintManager  printManager1=new PrintManager ();
    PrintManager  printManager2=new PrintManager ();
    //线程A执行printManager1.print();
    //线程B执行printManager2.print();
    
    synchronized 修饰代码块

    (1)参数为成员变量或this对象
    传入的参数为成员变量或this对象,那么当前成员变量或对象就是锁,当不同线程访问代码块时,需要竞争该锁。效果跟修饰成员方法一致。

    注意:如果传入的参数为局部变量,因为局部变量每次都发生变化,所以不同线程访问代码块时抢占的锁根本不是同一个,也就不会产生互斥;
    注意:案例2,当不同对象访问代码块不会产生互斥,线程抢占的锁基本不是同一个。
    private Object object = new Object();//成员变量
        public void print() {
            synchronized (object) {//或者 synchronized (this)
                  ......
            }
        }
    
    //案例1,以下调用会产生互斥
    PrintManager  printManager=new PrintManager ();
    //线程A执行PrintManager .print();
    //线程B执行PrintManager .print();
    
    //案例2,以下调用会产生不会互斥
    PrintManager  printManager1=new PrintManager ();
    PrintManager  printManager2=new PrintManager ();
    //线程A执行printManager1.print();
    //线程B执行printManager2.print();
    

    (2)参数为.class获取全局变量
    传入的参数为.class,那么.class对象就是锁。前面提到,所有实例都共享这个对象,当不同线程访问代码块时,需要竞争该锁。如果参数是全局变量,原理跟.class对象一样。

    注意:案例2,因为抢占的是同一个锁,所有也会产生互斥。
    public static Object object = new Object();//全局变量
        public void print() {
            synchronized (PrintManager.class) {//或者 synchronized (object )
                  ......
            }
        }
    
    //案例1,以下调用会产生互斥
    PrintManager  printManager=new PrintManager ();
    //线程A执行PrintManager .print();
    //线程B执行PrintManager .print();
    
    //案例2,以下调用也会产生互斥
    PrintManager  printManager1=new PrintManager ();
    PrintManager  printManager2=new PrintManager ();
    //线程A执行printManager1.print();
    //线程B执行printManager2.print();
    
    为什么构建单例是用class对象作为锁呢?

    因为getInstance是静态方法,所有用this对象或者成员变量,显然不正确,因为静态方法还没有this对象跟成员变量呢。
    至于class对象和全局变量,我觉得两者都可以,效果一样,只不过用class对象就少一些定义全局变量的语句而已。

    public class PrintManager {
        private Object object = new Object();
        private static volatile PrintManager INSTANCE;
        public static PrintManager getInstance() {
            if (INSTANCE == null) {
                synchronized (PrintManager.class) {
                    if (INSTANCE == null) {
                        INSTANCE = new PrintManager();
                    }
                }
            }
            return INSTANCE;
        }
    
    总结:

    不同线程访问是不是会产生互斥性,关键看锁的是什么对象。

    以上分析有不对的地方,请指出,互相学习,谢谢哦!

    相关文章

      网友评论

          本文标题:Java -> synchronized

          本文链接:https://www.haomeiwen.com/subject/xusvsktx.html