Java线程安全状态专题解析

网友投稿 246 2022-10-25

Java线程安全状态专题解析

一、观察线程的所有状态

线程的状态是一个枚举类型 Thread.State

public static void main(String[] args) {

for (Thread.State state : Thread.State.values()){

System.out.println(state);

}

}

NEW: 安排了工作, 还未开始行动

RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作.就绪状态

BLOCKED: 这几个都表示排队等着其他事情

WAITING: 这几个都表示排队等着其他事情

TIMED_WAITING: 这几个都表示排队等着其他事情

TERMINATED: 工作完成了.

二、线程状态和状态转移的意义

NEW:Thread对象有了,但是PCB还没有

RUNNABLE:线程正在CPU上执行或者即将到CPU上执行(PCB在就绪队列中,随时可能被调度到)

WAITING:wait方法导致

TIMED_WAITING:sleep方法导致

BLOCKED:等待锁导致

TERMINATED:对象还在,但PCB已经没了

public static void main(String[] args) {

Thread t = new Thread(){

@Override

public void run() {

for (int i = 0; i < 100_00; i++){

}

}

};

System.out.println("线程启动前:" + t.getState());

t.start();

while (t.isAlive()){

System.out.println("线程运行中:" + t.getState());

}

System.out.println("线程结束后:" + t.getState());

}

三、多线程带来的风险

线程不安全的原因

①线程是抢占式执行的

线程不安全的万恶之源,线程之间的调度完全由内核负责,用户代码中感知不到,也无法控制。线程之间谁先执行,谁后执行,谁执行到哪里从CPU上下来,这样的过程用户无法控制也无法感知到的。

②自增操作不是原子的

每次++都能拆成三个步骤

把内存中的数据读取到CPU中

把CPU中的数据+1

把计算的数据写回内存中

如果两个线程串行执行,此时计算结果为2。

如果两个线程并行执行,线程1进行++操作到一半的时候,线程也进行了++操作,此时自增两次,但结果为1。

必须保证线程1save结束了,线程2再load,此时计算结果才正确

③多个线程尝试修改同一个变量

如果是一个线程修改一个变量,线程安全

如果多个线程读取同一个变量,线程安全

如果多个线程修改不同的变量。线程安全

④内存可见性导致线程安全问题

⑤指令重排序

java的编译器在编译代码时,会对指令进行优化,调整指令的先后顺序,保证原有的逻辑不变的情况下,提高程序的运行效率

四,解决线程安全问题

锁-synchronized

未加锁

static class Counter{

public int count = 0;

public void increase(){

count++;

}

}

public static void main(String[] args) throws InterruptedException {

Counter counter = new Counter();

Thread t1 = new Thread(){

@Override

public void run() {

for (int i = 0; i < 50000; i++){

counter.increase();

}

}

};

Thread t2 = new Thread(){

@Override

public void run() {

for (int i = 0; i < 50000; i++){

counter.increase();

}

}

};

t1.start();

t2.start();

t1.join();

t2.join();

System.out.println(counter.count);

}

已加锁

static class Counter{

public int count = 0;

synchronized public void increase(){

count++;

}

}

public static void main(String[] args) throws InterruptedException {

Counter counter = new Counter();

Thread t1 = new Thread(){

@Override

public void run() {

for (int i = 0; i < 50000; i++){

counter.increase();

http:// }

}

};

Thread t2 = new Thread(){

@Override

public void run() {

for (int i = 0; i < 50000; i++){

counter.increase();

}

}

};

t1.start();

t2.start();

t1.join();

t2.join();

System.out.println(counter.count);

}

此处的synchronized就是针对counter这个对象来加锁,进入increase方法内部,就把加锁状态设为ture,increase方法退出之后,就把加锁状态设为false,如果某个线程已经把加锁状态设为ture,此处的其他的线程尝试去加锁,就会阻塞

synchronized的特性——刷新内存

synchronized 的工作过程:

1. 获得互斥锁

2. 从主内存拷贝变量的最新副本到工作的内存

3. 执行代码

4. 将更改后的共享变量的值刷新到主内存

5. 释放互斥锁

synchronized的特性——互斥

public static void main(String[] args) {

Object locker = new Object();

Thread t1 = new Thread(){

@Override

public void run() {

Scanner scanner = new Scanner(System.in);

synchronized (locker) {

System.out.println("输入一个整数");

int num = scanner.nextInt();

System.out.println("num= " + num);

}

}

};

t1.start();

Thread t2 = new Thread(){

@Override

public void run()nwCkLuj {

while (true){

synchronized (locker){

System.out.println("线程2获取到锁");

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

};

t2.start();

}

一旦线程一获取到锁,并且没有释放的话,线程2就会一直在锁这里阻塞等待

public static void main(String[] args) {

Object locker1 = new Object();

Object locker2 = new Object();

Thread t1 = new Thread(){

@Override

public void run() {

Scanner scanner = new Scanner(System.in);

synchronized (locker1) {

System.out.println("输入一个整数");

int num = scanner.nextInt();

System.out.println("num= " + num);

}

}

};

t1.start();

Thread t2 = new Thread(){

@Override

public void run() {

while (true){

synchronized (locker2){

System.out.println("线程2获取到锁");

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

};

t2.start();

}

不是同一把锁,就不回出现竞争,就没有互斥了。

public static void main(String[] args) {

Object locker1 = new Object();

Object locker2 = new Object();

Thread t1 = new Thread(){

@Override

public void run() {nwCkLuj

Scanner scanner = new Scanner(System.in);

synchronized (locker1.getClass()) {

System.out.println("输入一个整数");

int num = scanner.nextInt();

System.out.println("num= " + num);

}

}

};

t1.start();

Thread t2 = new Thread(){

@Override

public void run() {

while (true){

synchronized (locker2.getClass()){

System.out.println("线程2获取到锁");

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

};

t2.start();

}

这个代码中,两个线程都在针对locker1和locker2的类对象进行竞争,此处的locker1和locker2的类型都是Object,对应的对象都是相同的对象。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:k8s集群中部署jenkins、gitlab、nexus实现CICD
下一篇:跟我学docker:Docker Compose 配置文件详解
相关文章

 发表评论

暂时没有评论,来抢沙发吧~