一个小程序认识原子性:
package T05_YuanZiXing;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class T00_00_IPlusPlus {
private static long n = 0L;
public static void main(String[] args) throws Exception {
//Lock lock = new ReentrantLock();
Thread[] threads = new Thread[100];
CountDownLatch latch = new CountDownLatch(threads.length);
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 10000; j++) {
n++;
}
latch.countDown();
});
}
for (Thread t : threads) {
t.start();
}
latch.await();
System.out.println(n);
}
}
多个线程同时拿到了变量,同时++,然后同时写回去。
【一些概念】:
race condition => 竞争条件 , 指的是多个线程访问共享数据的时候产生竞争———上述程序中变量n即是共享数据。
数据的不一致(unconsistency),并发访问之下产生的不期望出现的结果
如何保障数据一致呢?–> 线程同步(线程执行的顺序安排好),
monitor (管程) —> 锁
critical section -> 临界区
如果临界区执行时间长,语句多,叫做 锁的粒度比较粗,反之,就是锁的粒度比较细。
【修改程序】:
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 10000; j++) {
synchronized (T00_01_IPlusPlus.class) {
//lock.lock();
n++;
//lock.unlock();
}
}
latch.countDown();
});
}
原子操作的意思就是不能够中间被打断 , 只能作为一个整体 , 不能并发执行 。
CPU级别汇编,需要查询汇编手册!
Java中的8大原子操作:(了解即可,无需背过)
//正常的情况下 , 你判断不了的情况下。这句话到底是否具备原子性呢?——你给它上锁就可以了。
1——将static的值给拿过来;
2——扔到栈空间里面;
3——将这个值++;
4——将值放回去;
5——返回。
synchronized (T00_01_IPlusPlus.class) {
n++;
}
//大括号里面的所有操作都被当作了一个整体——不可打断。
一句高级语言翻译成机器语言可能有好多句。需要保证这些操作不被打断。
package T05_YuanZiXing;
import Utils.SleepHelper;
public class T00_01_WhatIsLock {
private static Object o = new Object();
public static void main(String[] args) {
Runnable r = () -> {
System.out.println(Thread.currentThread().getName() + " start!");
SleepHelper.sleepSeconds(2);
System.out.println(Thread.currentThread().getName() + " end!");
};
for (int i = 0; i < 3; i++) {
new Thread(r).start();
}
}
}
【最终输出】:
//基本上是同时启动 , 2秒之后同时结束。
package T05_YuanZiXing;
import Utils.SleepHelper;
public class T00_01_WhatIsLock_02 {
private static Object o = new Object();
public static void main(String[] args) {
Runnable r = () -> {
synchronized (o) {
System.out.println(Thread.currentThread().getName() + " start!");
SleepHelper.sleepSeconds(2);
System.out.println(Thread.currentThread().getName() + " end!");
}
};
for (int i = 0; i < 3; i++) {
new Thread(r).start();
}
}
}
【最终输出】:
//红线处会等待2S , 一共执行完需要6S的时间。
原先上厕所,三个人同时一起完成,现在要排队一个一个完成了。
【程序演示】:
package T05_YuanZiXing;
import Utils.SleepHelper;
public class T00_02_SingleLockVSMultiLock {
private static Object o1 = new Object();
private static Object o2 = new Object();
private static Object o3 = new Object();
public static void main(String[] args) {
Runnable r1 = () -> {
synchronized (o1) {
System.out.println(Thread.currentThread().getName() + " start!");
SleepHelper.sleepSeconds(2);
System.out.println(Thread.currentThread().getName() + " end!");
}
};
Runnable r2 = () -> {
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + " start!");
SleepHelper.sleepSeconds(2);
System.out.println(Thread.currentThread().getName() + " end!");
}
};
Runnable r3 = () -> {
synchronized (o3) {
System.out.println(Thread.currentThread().getName() + " start!");
SleepHelper.sleepSeconds(2);
System.out.println(Thread.currentThread().getName() + " end!");
}
};
new Thread(r1).start();
new Thread(r2).start();
new Thread(r3).start();
}
}
【最终输出】:
//一共花了2秒。
synchronized是可以保障可见性的,一个线程结束了,会向主内存中做同步;
synchronized保障了原子性。
但是synchronized无法保障有序性。
sync操作中的绿色区域部分的执行顺序是完全可以发生变化的。
单线程保障最终一致性 , 它和锁没有关系。
//后面跟的那一部分叫做monitor。
// o 即 monitor。
sync大括号所包含的叫做临界区——(图中的红色区域)。
如果临界区执行的时间比较长,语句比较多 , 那我们就说锁的粒度比较粗 , 反之,就是锁的粒度比较细。
1)乐观锁
2)悲观锁