Java -> synchronized

2020-09-10  本文已影响0人  杨0612
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;
    }
总结:

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

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

上一篇下一篇

猜你喜欢

热点阅读