一、synchronized的作用
能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果。互斥性,可见性。
1、synchronized使用前
public class SynRunnable implements Runnable {
@Override
public void run() {
try {
Log.e("aaa",Thread.currentThread().getName()+"...睡眠前...时间="+System.currentTimeMillis());
Thread.sleep(3000);
Log.e("aaa",Thread.currentThread().getName()+"...睡眠后...时间="+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
SynRunnable runnable = new SynRunnable();
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
t1.start();
t2.start();
运行结果:
Thread-4...睡眠前...时间=1565085002442
Thread-5...睡眠前...时间=1565085002445
Thread-4...睡眠后...时间=1565085005443
Thread-5...睡眠后...时间=1565085005447
结论,两个线程并非顺序执行,而是并发执行。
2、synchronized使用后
public class SynRunnable implements Runnable {
@Override
public void run() {
synchronized(this) {
try {
Log.e("aaa", Thread.currentThread().getName() + "...睡眠前...时间=" + System.currentTimeMillis());
Thread.sleep(3000);
Log.e("aaa", Thread.currentThread().getName() + "...睡眠后...时间=" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
执行结果:
Thread-4...睡眠前...时间=1565085387805
Thread-4...睡眠后...时间=1565085390806
Thread-5...睡眠前...时间=1565085390807
Thread-5...睡眠后...时间=1565085393808
结论:添加synchronized,顺序执行,不是并发执行
二、synchronized分类
对象锁,类的对象可以有多个,每个对象有其独立的对象锁,互不干扰。
类锁,每个类只有一个class对象,所有每个类只有一个类锁。
1、根据修饰对象分类
修饰代码块
synchronized(this | object)
synchronized(.class)
修饰方法
修饰非静态方法
修饰静态方法
2、根据获取的锁分类
获取对象锁
synchronized(this | object)
修饰非静态方法
获取类锁
synchronized(.class)
修饰静态方法
二、synchronized的两种用法
1、对象锁
包括方法锁,(默认锁对象为this当前实例对象)和同步代码块锁(自己指定所对象)
(1)单个synchronized(this)
看 synchronized的作用模块
(2)单个同步代码块锁,synchronized(object)
public class SynRunnable implements Runnable {
Object object=new Object();
@Override
public void run() {
synchronized(object) {
try {
Log.e("aaa", Thread.currentThread().getName() + "...睡眠前...时间=" + System.currentTimeMillis());
Thread.sleep(3000);
Log.e("aaa", Thread.currentThread().getName() + "...睡眠后...时间=" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果:顺序执行,非并发
Thread-4...睡眠前...时间=1565085707838
Thread-4...睡眠后...时间=1565085710841
Thread-5...睡眠前...时间=1565085710843
Thread-5...睡眠后...时间=1565085713845
(3)多个synchronized(this) 和多个synchronized(object)一样,如果this代表的对象和object代表的对象不一样,反映的结论也是一样的
public class SynRunnable implements Runnable {
Object objectA = new Object();
Object objectB = new Object();
@Override
public void run() {
methodA();
methodB();
}
private void methodA() {
synchronized (objectA) {
try {
Log.e("aaa", Thread.currentThread().getName() + "...方法a...睡眠前...时间=" + System.currentTimeMillis());
Thread.sleep(3000);
Log.e("aaa", Thread.currentThread().getName() + "...方法a...睡眠后...时间=" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void methodB() {
synchronized (objectB) {
try {
Log.e("aaa", Thread.currentThread().getName() + "...方法b...睡眠前...时间=" + System.currentTimeMillis());
Thread.sleep(3000);
Log.e("aaa", Thread.currentThread().getName() + "...方法b...睡眠后...时间=" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果:
Thread-4...方法a...睡眠前...时间=1565087245228
Thread-4...方法a...睡眠后...时间=1565087248229
Thread-4...方法b...睡眠前...时间=1565087248229
Thread-5...方法a...睡眠前...时间=1565087248229
Thread-5...方法a...睡眠后...时间=1565087251230
Thread-4...方法b...睡眠后...时间=1565087251230
Thread-5...方法b...睡眠前...时间=1565087251230
Thread-5...方法b...睡眠后...时间=1565087254231
可以看出,方法a和方法b加的对象锁不一样,是不影响对方的执行,两者之间可以并行。同一个锁对象的不同方法是串行的,互斥
2、类锁
指synchronized修饰静态的方法或者指定锁为Class类锁
(1)class类锁
public class SynRunnable implements Runnable {
@Override
public void run() {
methodA();
methodB();
}
private void methodA() {
synchronized (SynRunnable.class) {
try {
Log.e("aaa", Thread.currentThread().getName() + "...方法a...睡眠前...时间=" + System.currentTimeMillis());
Thread.sleep(3000);
Log.e("aaa", Thread.currentThread().getName() + "...方法a...睡眠后...时间=" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void methodB() {
synchronized (SynRunnable.class) {
try {
Log.e("aaa", Thread.currentThread().getName() + "...方法b...睡眠前...时间=" + System.currentTimeMillis());
Thread.sleep(3000);
Log.e("aaa", Thread.currentThread().getName() + "...方法b...睡眠后...时间=" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果:
Thread-5...方法a...睡眠前...时间=1565087517322
Thread-5...方法a...睡眠后...时间=1565087520323
Thread-5...方法b...睡眠前...时间=1565087520324
Thread-5...方法b...睡眠后...时间=1565087523325
Thread-4...方法a...睡眠前...时间=1565087523326
Thread-4...方法a...睡眠后...时间=1565087526328
Thread-4...方法b...睡眠前...时间=1565087526328
Thread-4...方法b...睡眠后...时间=1565087529331
结论:类锁是串行,互斥
(2)静态方法
public class SynRunnable implements Runnable {
@Override
public void run() {
methodA();
methodB();
}
private void methodA() {
synchronized (SynRunnable.class) {
try {
Log.e("aaa", Thread.currentThread().getName() + "...方法a...睡眠前...时间=" + System.currentTimeMillis());
Thread.sleep(3000);
Log.e("aaa", Thread.currentThread().getName() + "...方法a...睡眠后...时间=" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static synchronized void methodB() {
try {
Log.e("aaa", Thread.currentThread().getName() + "...方法b...睡眠前...时间=" + System.currentTimeMillis());
Thread.sleep(3000);
Log.e("aaa", Thread.currentThread().getName() + "...方法b...睡眠后...时间=" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
Thread-4...方法a...睡眠前...时间=1565087756890
Thread-4...方法a...睡眠后...时间=1565087759892
Thread-4...方法b...睡眠前...时间=1565087759893
Thread-4...方法b...睡眠后...时间=1565087762895
Thread-5...方法a...睡眠前...时间=1565087762899
Thread-5...方法a...睡眠后...时间=1565087765901
Thread-5...方法b...睡眠前...时间=1565087765901
Thread-5...方法b...睡眠后...时间=1565087768903
结论:静态方法和类锁一样,是串行,互斥。
三、synchronized和volatile的区别
1、volatile本质高告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized是锁定当前对象,只有当前线程可以访问变量,其他线程被阻塞。
2、valtaile仅使用在变量上,synchronized则可以使用在变量,方法和类上
3、volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性。