线程通信
前言
一、线程通信
1.1JVM下的内存区域划分
1.2 代码中的存储理解
二、线程安全
2.1线程不安全举例
// 演示线程不安全的现象
public class Main {
// 定义一个共享的数据 —— 静态属性的方式来体现
static int r = 0;
// 定义加减的次数
// COUNT 越大,出错的概率越大;COUNT 越小,出错的概率越小
static final int COUNT = 100;
// 定义两个线程,分别对 r 进行 加法 + 减法操作
static class Add extends Thread {
@Override
public void run() {
for (int i = 0; i < COUNT; i++) {
r++;
}
}
}
static class Sub extends Thread {
@Override
public void run() {
for (int i = 0; i < COUNT; i++) {
r--;
}
}
}
public static void main(String[] args) throws InterruptedException {
Add add = new Add();
add.start();
Sub sub = new Sub();
sub.start();
add.join();
sub.join();
// 理论上,r 被加了 COUNT 次,也被减了 COUNT 次
// 所以,结果应该是 0
//但是每次输出结果都不同
System.out.println(r);
}
}
2.2线程不安全原因(重点知识、面试重点)
2.2.1站在开发者的角度
2.2.2系统角度解释
2.2.2.1原子性被破坏
2.2.2.2 内存可见性,导致某些线程读取“脏数据”
2.2.2.3 代码重排序
2.3线程安全问题小结
2.4常见类的线程安全问题
三、保护线程安全的措施——锁(lock)
3.1synchronized 锁 /同步锁/monitor锁(监视器锁)
3.1.1语法
一个是对当前对象加锁,一个是对当前类加锁
3.1.2什么是锁
3.1.3判断一下那些会互斥
3.1.4 JVM中的锁
3.1.5synchronized 的作用
加锁操作使得互斥(synchronized和我们一起配合(我们需要真正的使用synchronized))
3.1.5.1保证临界区的原子性
看4/24的开头和00:58~1:18
3.1.5.2内存可见性
3.1.5.3增加约束代码的重排序
3.2JUC下的锁
3.2.1lock()
public class Main1 {
private static final Lock lock = new ReentrantLock();
static class MyThread extends Thread {
@Override
public void run() {
lock.lock();
System.out.println("子线程进入临界区"); // 理论上,这句代码永远到达不了
}
}
public static void main(String[] args) throws InterruptedException {
lock.lock();
MyThread t = new MyThread();
t.start();
t.join();
}
}
上述代码中的永远加不到锁的理解
3.2.2.lockInterruptibly()
使用lock.lockInterruptibly
输出结果:收到停止信号,停止运行
public class Main3 {
private static final Lock lock = new ReentrantLock();
static class MyThread extends Thread {
@Override
public void run() {
try {
lock.lockInterruptibly();//允许停止的情况
System.out.println("子线程进入临界区"); // 理论上,这句代码永远到达不了
} catch (InterruptedException e) {
System.out.println("收到停止信号,停止运行");
}
}
}
public static void main(String[] args) throws InterruptedException {
lock.lock();
MyThread t = new MyThread();
t.start();
TimeUnit.SECONDS.sleep(2);
t.interrupt();//直接使用尝试让子线程停下来,但实际会徒劳无功,所以用上面的捕获
t.join();
}
}
3.2.3try.lock()
使用lock.try.lock返回一个boolean,加锁失败后还可以有其他操作,更加灵活
输出:加锁失败
static class MyThread extends Thread {
@Override
public void run() {
boolean b = lock.tryLock();
if (b == true) {
// 加锁成功了
System.out.println("加锁成功");
System.out.println("子线程进入临界区"); // 理论上,这句代码永远到达不了
} else {
System