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;
}
总结:
不同线程访问是不是会产生互斥性,关键看锁的是什么对象。
以上分析有不对的地方,请指出,互相学习,谢谢哦!
网友评论