final class Accumulator {
//线程二可能在不停读读取这个值
private double result = 0.0D;
public void addAll(double[] values) {
for (double value : values) {
//线程一在不断地进行累加
result += value;
}
}
}
final class Accumulator2 {
private double result = 0.0D;
public void addAll(double[] values) {
double sum = 0.0D;
for (double value : values) {
sum += value;
}
result += sum;
}
}
第二种写法比第一种写法出现不一致性的概率要小,因为我们在方法完成之前,读不到中间状态的脏数据
尽量少暴露线程计算过程的中间状态
能用范围小的变量,不用范围大的变量

public class T01_DeadLock {
public static void main(String[] args) {
ChopStick cs0 = new ChopStick();
ChopStick cs1 = new ChopStick();
ChopStick cs2 = new ChopStick();
ChopStick cs3 = new ChopStick();
ChopStick cs4 = new ChopStick();
Philosohper p0 = new Philosohper("p0", 0, cs0, cs1);
Philosohper p1 = new Philosohper("p1", 1, cs1, cs2);
Philosohper p2 = new Philosohper("p2", 2, cs2, cs3);
Philosohper p3 = new Philosohper("p3", 3, cs3, cs4);
Philosohper p4 = new Philosohper("p4", 4, cs4, cs0);
p0.start();
p1.start();
p2.start();
p3.start();
p4.start();
}
public static class Philosohper extends Thread {
private ChopStick left, right;
private int index;
public Philosohper(String name, int index, ChopStick left, ChopStick right) {
this.setName(name);
this.index = index;
this.left = left;
this.right = right;
}
/**
* 1、先锁定右边,再锁定左边这时就表明有一个人吃完了,因为只有拿到两个筷子才能吃饭
* 2、注意:这个方法是有问题的,一出现死锁,那么谁都拿不到筷子,大家都得饿死
**/
@Override
public void run () {
//锁定右边筷子
synchronized (left) {
Thread.sleep(1 + index);
//锁定左边筷子
synchronized (right) {
SleepHelper.sleepSeconds(1);
//打印出这个表示有一个吃饱了
System.out.println(index + " 号 哲学家已经吃完");
}
}
}
}
public void run() {
// try {
// Thread.sleep(new Random().nextInt(5));
// } catch (InterruptedException e) {
// System.out.println(e);
// }
if (index == 0) {
//左撇子算法 也可以index % 2 == 0
synchronized (left) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
System.out.println(e);
}
synchronized (right) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
System.out.println(e);
}
System.out.println(index + " 吃完了!");
}
}
} else {
synchronized (right) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
System.out.println(e);
}
synchronized (left) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
System.out.println(e);
}
System.out.println(index + " 吃完了!");
}
}
}
}

public class T02_00_LockSupport {
static Thread t1 = null, t2 = null;
public static void main(String[] args) throws Exception {
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
t1 = new Thread(() -> {
for (char c : aI) {
System.out.print(c);
LockSupport.unpark(t2); // 叫醒t2
LockSupport.park(); // t1阻塞 当前线程阻塞
}
}, "t1");
t2 = new Thread(() -> {
for (char c : aC) {
LockSupport.park(); // t2挂起
System.out.print(c);
LockSupport.unpark(t1); // 叫醒t1
}
}, "t2");
t1.start();
t2.start();
}
}

public class T06_00_sync_wait_notify {
public static void main(String[] args) {
final Object o = new Object();
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
new Thread(() -> {
// 首先创建一把锁
synchronized (o) {
for (char c : aI) {
System.out.print(c);
try {
o.notify(); // 叫醒等待队列里面的一个线程,对本程序来说就是另一个线程
o.wait(); // 让出锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
o.notify(); // 必须,否则无法停止程序
}
}, "t1").start();
new Thread(() -> {
synchronized (o) {
for (char c : aC) {
System.out.print(c);
try {
o.notify();
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
o.notify();
}
}, "t2").start();
}
}
从上面这段代码中我们可能会引出一个问题,那么就是:notify() 和 wait() 调用顺序是否会有直接影响?
答案:是的,那么下面我们就来看看,notify() 和 wait() 的执行流程
这道题曾经也是华为的笔试填空题

从上图代码片段中可以看出,如果我们先执行 wait(),会先让自己直接进入等待队列。那么自己和另一个线程都在等待队列中等待,两个线程一直在那傻等,谁也叫不醒对方,也就是根本执行不了notify()
public class T07_00_sync_wait_notify {
private static CountDownLatch latch = new CountDownLatch(1); // 设置门栓的参数为1,即只有一个门栓
public static void main(String[] args) {
final Object o = new Object();
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
new Thread(() -> {
synchronized (o) {
for (char c : aI) {
System.out.print(c);
latch.countDown(); // 门栓的数值-1,即打开门
try {
o.notify();
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
o.notify();
}
}, "t1").start();
new Thread(() -> {
try {
latch.await(); // 想哪个线程后执行,await()就放在哪个线程里
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o) {
for (char c : aC) {
System.out.print(c);
try {
o.notify();
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
o.notify();
}
}, "t2").start();
}
}
public class T08_00_lock_condition {
public static void main(String[] args) {
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(() -> {
lock.lock();
try {
for (char c : aI) {
System.out.print(c);
condition.signal(); // notify()
condition.await(); // wait()
}
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "t1").start();
new Thread(() -> {
lock.lock(); // synchronized
try {
for (char c : aC) {
System.out.print(c);
condition.signal(); // o.notify
condition.await(); // o.wait
}
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "t2").start();
}
}


Lock 本身就可以解决这个问题,靠的就是 Condition,Condition 可以做到精确唤醒
Condition 是条件的意思,但我们可以把它当做队列来看待
一个 condition 就是一个等待队列
public class T08_00_lock_condition {
public static void main(String[] args) {
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
Lock lock = new ReentrantLock();
Condition conditionT1 = lock.newCondition(); // 队列1
Condition conditionT2 = lock.newCondition(); // 队列2
CountDownLatch latch = new CountDownLatch(1);
new Thread(() -> {
lock.lock(); // synchronized
try {
for (char c : aI) {
System.out.print(c);
latch.countDown();
conditionT2.signal(); // o.notify()
conditionT1.await(); // o.wait()
}
conditionT2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "t1").start();
new Thread(() -> {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock(); // synchronized
try {
for (char c : aC) {
System.out.print(c);
conditionT1.signal(); // o.notify
conditionT2.await(); // o.wait
}
conditionT1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "t2").start();
}
}