JUC--上下文切换
JUC–上下文切换
背景
-
JUC–上下文切换
-
博主以黑马JUC进行学习
分析
1 | public class Test1{ |
- 结果不为0,因为由于分时系统造成的线程切换而导致的安全问题
临界区 Critical Section
- 一个程序运行多个线程本身是没有问题的
- 问题出在多个线程访问共享资源
- 多个线程读共享资源其实也没有问题
- 在多个线程对共享资源读写时发生指令交错,就会出现问题
- 一段代码块内如果存在对共享资源的多线程读写操作,称这段代码为临界区
1 | static int counter = 0; |
竞态条件 Race Condition
- 多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件
synchronized 解决方案
-
为了避免临界区的竞态条件发生,有多种手段可以达到目的。
- 阻塞式的解决方案:synchronized,Lock
- 非阻塞式的解决方案:原子变量
-
使用synchronized解决问题,即熟称的【对象锁】,它采用互斥的方式让同一时刻至多只有一个线程能持有【对象锁】,其它线程再想获得这个【对象锁】时就会阻塞住。这样就能保证拥有锁的线程可以安全的执行临界区的代码,不用担心线程上下文切换
- 注意
- 虽然java中互斥和同步都可以采用synchronized 关键字来完成,但它们还是有区别的:
- 互斥是保证临界区的竞态条件发生,同一时刻只能有一个线程执行临界区代码
- 同步是由于线程执行的先后、顺序不同、需要一个线程等待其它线程运行到某个点
-
synchronized
- 语法
1
2
3
4synchronized(对象)
{
临界区
}- 解决
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26static int counter = 0;
static final Object room = new Object(); //对象
public static void main(String[] args) throws InterruptedException{
Thread t1 = new Thread(() -> {
for(int i = 0;i < 5000; i++){
synchronized(room){
counter++;
}
}
},"t1");
Thread t2 = new Thread(() -> {
for(int i = 0;i < 5000; i++){
synchronized(room){
counter--;
}
}
},"t2");
t1.start();
t2.start();
t1.join();
t2.join();
log.debug("{}",counter);
}- synchronized 实际是用对象锁保证了临界区内代码的原子性,临界区内的代码对外是不可分割的,不会被线程切换所打断。
- 如果把synchronized(obj)放在for循环的外面,如何理解?–原子性
- 如果t1 synchronized(obj1) 而t2 synchronized(obj2) 会怎样运作?–锁对象
- 如果t1 synchronized(obj) 而t2没有加会怎么样?如何理解? --锁对象
-
锁对象面向对象改进
- 把需要保护的共享变量放入一个类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43public class Test2{
public static void main(String[] args) throws InterruptedException{
Room room = new Room();
Thread t1 = new Thread(() -> {
for(int i = 0;i < 5000; i++){
room.increment();
}
},"t1");
Thread t2 = new Thread(() -> {
for(int i = 0;i < 5000; i++){
room.decrement();
}
},"t2");
t1.start();
t2.start();
t1.join();
t2.join();
log.debug("{}",room.counter);
}
}
class Room{
private int counter = 0;
public void increment(){
synchronized(this){
counter++;
}
}
public void decrement(){
synchronized(this){
counter--;
}
}
public int getCounter(){
synchronized(this){
return counter;
}
}
}- 两种语法
1
2
3
4
5
6
7
8
9
10
11
12
13class Test{
public synchronized void test(){
}
}
等价于
class Test{
public void test(){
synchronized(this){
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13class Test{
public synchronized static void test(){
}
}
等价于
class Test{
public static void test(){
synchronized(Test.class){
}
}
}- 不加synchronized的方法
- 不加synchronized 的方法就好比不遵守规则的人,不去老实排队