java线程的几种状态和线程安全的原因和几种解决办法

线程的几种状态:
1.NEW:Thread对象创建好了线程,但是还没有调用start();
2.RUNNABLE:就绪状态,可工作的,又可以细分为正在工作中和即将开始工作。
3:BLOCKED:当前线程在等待锁,导致了阻塞。
4:WAITING:当前线程在等待唤醒 导致了阻塞。
5:TIMED_WAITING:当前的线程在一定时间内是阻塞的。
6:TERMINATED: 操作系统中的线程已经执行完毕 销毁了 但是Thread对象还在 。
线程安全的原因和几种解决办法:
操作系统调度线程的时候是随机的,正是因为这样的随机性,就可能导致程序执行出现一些BUG,这样就认为代码是线程不安全的,如果没有带来BUG,就认为线程是安全的。
抢占式BUG:
先看一下代码:要求t1和t2线程各自自增5000:
在这里插入图片描述

在这里插入图片描述
给第一个线程和第二个线程各自加5000次,(先执行t2,在执行t1)这样按照顺序执行,符合要求。按照顺序执行一次,count应该是2。(圆圈代表CPU)其实和串行没什么区别了。
在这里插入图片描述

但是不要忘记操作系统是并发执行的看下面的执行顺序:
在这里插入图片描述
如果是这种顺序的话,结果是
在这里插入图片描述
这种情况就产生了线程安全问题。
可以用加锁的方法解决上述问题:
在这里插入图片描述
在这里,lock就会一直阻塞,直到t1线程执行完,释放锁以后,才执行t2线程。通过阻塞就把乱序的并发转化为有序的执行。代码如下:
在这里插入图片描述

产生线程安全的几个原因:
1、线程是抢占式执行,线程间的调度充满随机性,这才是线程安全问题最核心的点。
2、多个线程对同一个变量进行操作 (如果是多个线程针对不同的变量进行修改 没事 如果多个线程对同个变量读 也没有问题)
3、针对变量的操作不是原子的性的。
4、 内存可见性 也会影响线程安全是编译器优化的副作用 一个线程频繁在读 一个线程写。
一个线程读 、一个线程写 但是写的那个迟迟不写,读的那个就从读内存 到寄存器(更快)了 这样写的那个操作了 读就不知道了。
5、指令重排序 也是编译器优化的副作用 编译器会自动调整代码顺序 (保证逻辑不变的前提)。

解决线程安全的方法:
1:使用synchronized关键字
synchronized 不光能保证指令的原子性 、同时也能保证内存的可见性 、也能禁止指令重排序
被synchronized包裹起来的代码,编译器就不敢轻易做出上述假设,相当于手动禁止了编译器的优化。
synchronized的几种特性:
1、互斥特性,某个线程执行到某个对象的 synchronized 中时, 其他线程如果也执行到
同一个对象 synchronized 就会阻塞等待。
2、刷新内存:每次取数据都是从内存中取。
3、可重入:第一次加锁的时候加锁次数加一 ,后续再加锁不是真的加锁(不失败) 只是单纯的把计数给自增 枷锁次数为2,解锁的时候要必须解锁两次才是真的解锁。
synchronized的使用:
1、直接修饰普通方法:
在这里插入图片描述
2、直接加到代码块上
在这里插入图片描述
2:使用volatile关键字
volatile和原子性无关 ,但是可以保证内存可见性,禁止编译器做出上述优化 编译器每次执行 判定相等 都会重新从内存读取isQuit的值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值