原子性
操作可以理解为是不可分割的操作,要么完全执行,要么不执行,不存在中间状态。在并发环境下,如果多个线程同时访问和修改相同的数据,如果没有保证原子性,可能会导致数据损坏、计算错误或其它异常结果。
可见性
表示内存的可见性,即变量在线程中的工作内存是不确定的,需要从主内存中读取
有序性
为了提高执行效率,编译器、CPU处理器在运行时会对代码指令进行重排,重排过程中会遵循as-if-serial语义,即不影响单线程的运行结果
java momory model (java内存模型)
-
主内存:所有线程都共享的,类的成员变量,静态的成员变量
-
工作内存:每一个线程有自己的工作内存,工作内存只存储该线程对共享变量的副本。线程对变量的所有操作(读、取)都必须在工作内存中完成,而不是直接读写主内存中的变量,不同线程之间也不能直接访问对方工作内存中的变量。
Java内存模型的作用
Java内存模型是一套在多线程读写共享数据时,对共享数据的可见性、有序性和原子性的规则和保障
volatile、synchronized
JMM内存模型和CPU硬件内存的关系
volatile 只保证可见性
volatile主要是保证内存的可见性,即变量在寄存器中的工作内存是不确定的,需要从主内存中读取。synchronized主要是解决多个线程访问资源的同步性。volatile作用于变量,synchronized作用于代码块或者方法。volatile仅可以保证数据的可见性,不能保证数据的原子性。synchronized可以保证数据的可见性和原子性。volatile不会造成线程的阻塞,synchronized会造成线程的阻塞。
synchronized 保证可见性、原子性、有序性
内置锁;锁的管理是 jvm 管理的;
不可中断性
一个线程获取到锁之后,另外一个线程处在阻塞等待状态,不可中断;只有第一个线程释放锁,第二个线程才会继续执行;
wait/notify
-
wait 会释放锁、释放cpu
-
nofity 会通知 wait 的线程继续执行 , 并不是立即通知 wait 而是在 synchronized 代码执行完之后才会通知 wait 地方;
-
notifyAll 通知所有 wait 的线程;
修饰方法 synchronized
作用范围是整个方法,所以方法中所有的代码都是同步的:
- 修饰非静态方法,同一个实例的线程访问会被拦截,非同一实例可以同时访问。 即此时是默认对象锁(this)。
private synchronized void sync5() {
Log.d(TAG, Thread.currentThread().getName() + "_Sync5: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
- 修饰静态方法,默认类锁,静态的方法的使用只通过类就可以调用;
修饰代码块
synchronized(this)
HandlerThread 使用对象锁,作用范围是handlerThread 这个对象;
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
synchronized(Class)
单例模式使用的类锁;
类锁:所有的线程都会拦截进行阻塞;
public static RetrofitManager getInstance() {
if (instance == null) {
synchronized (RetrofitManager.class) {
if (instance == null) {
instance = new RetrofitManager();
}
}
}
return instance;
}
monitor 才是真正的锁,
synchronized 代码块的锁对象会关联一个 monitor,monitor 不是我们主动创建,而是JVM执行到同步代码块时,发现没有 monitor 对象就会创建 monitor ,monitor 内部有两个重要的成员变量 owner 拥有这把锁的线程,recursions 会记录线程拥有锁的的次数,
-
monitor owner:拥有锁的对象 recursions:拥有锁的次数
-
monitorenter recursions += 1; owner = 当前对象;
-
monitorexit recursions -= 1; owner = null;
-
同步方法会隐式加上 monitorenter 和 monitorexit
同步代码块出现异常会立刻释放锁
synchronized 总结
1、对于静态方法,由于此时对象还未生成,所以只能采用类锁;
2、只要采用类锁,就会拦截所有线程,只能让一个线程访问。
3、对于对象锁(this),如果是同一个实例,就会按顺序访问,但是如果是不同实例,就可以同时访问。
4、如果对象锁跟访问的对象没有关系,那么就会都同时访问。
wait notify join sleep 区别
wait 和 join
| 区别 | wait | join |
|---|---|---|
| 类 | Object类 | Thread类 |
| 目的 | 线程间通信 | 同步作用,使线程之间的执行从“并行”变成“串行” |
| 同步 | 需要synchronized | 不需要synchronized |
| 相同点 | 暂停当前的线程 | 暂停当前的线程 |
| 相同点 | 可通过 Interrupte 中断唤醒 | 可通过 Interrupte 中断唤醒 |
wait 和 sleep
| 区别 | wait | sleep |
|---|---|---|
| 类 | Object类 | Thread类 |
| 目的 | notify、notifyall 都是Object对象的方法,一起使用的,用于锁机制,所以会释放锁 | 跟锁没关系,不会释放锁 |
| 相同点 | 让出cpu资源 | 让出cpu资源 |