线程安全性问题
线程性能问题
CPU时间片
上下文切换,切换的时候需要保持任务的状态, 以便后续接着任务的状态执行。比较消耗CPU 资源。
活跃性问题
- 死锁: 双方都有对方需要的资源,并且都不释放给对方使用
- 活锁:一直互相释放资源给对方
- 饥饿: 线程优先级比较低的线程可能一直得不到资源而无法运行
死锁问题
可以通过jconsole 工具来进行检测:

饥饿和活锁问题不好检测
饥饿与公平
- 高优先级吞噬所有低优先级的CPU 时间片
以下代码低优先级的线程还是有可能大多数情况下获取CPU时间片的,只是概率上会变小。
public class Target implements Runnable { @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + "..."); } } } public class Demo8 { public static void main(String[] args) { Thread t = new Thread(new Target()); Thread t1 = new Thread(new Target()); Thread t2 = new Thread(new Target()); Thread t3 = new Thread(new Target()); t.setPriority(Thread.MAX_PRIORITY); //不同平台优先级值不一样,建议使用常量 t2.setPriority(Thread.MIN_PRIORITY); t.start(); t2.start(); } }
- 线程被永久堵塞在一个等待进入同步块的状态
- 等待的线程永远不被唤醒
如何尽量避免饥饿问题:
- 设置合理的优先级
- 使用锁来代替synchronized
线程安全性问题
写一个数值生成器:
单线程环境下:
public class Sequence { private int value; public int getNext() { return value++; } public static void main(String[] args) { Sequence sequence = new Sequence(); while (true) { System.out.println(sequence.getNext()); } } }
多线程环境:
package thread; public class Sequence { private int value; public int getNext() { return value++; } public static void main(String[] args) { Sequence sequence = new Sequence(); new Thread(new Runnable() { @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " " + sequence.getNext()); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " " + sequence.getNext()); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " " + sequence.getNext()); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } }
会有数据重复的问题,这就是线程安全性问题

线程是多个执行顺序流,value++ 相当于 value = value + 1, 是两步操作
从java字节码角度来看线程安全性问题:
可以使用 javap -verbose Sequence.class 来看字节码文件
0: aload_0 1: dup 2: getfield #2 // Field value:I 5: dup_x1 6: iconst_1 7: iadd 8: putfield #2 // Field value:I 11: ireturn
类的实例化对象它是放在堆内存中,堆是线程所共享的区域
程序计数器是线程所独享的区域
Value 是线程共享的区域
- 第一个线程执行完,值变为1 ,只是在操作数栈中变为1,还没有设置value, 因此value 还是0
- 第二个线程获取到时间片,首先是获取值 getfield, 获取的value为0,因为第一个线程还没有写进去
- 第一个线程获取到时间片,开始把1 往value 放,value 变为1
- 第二个线程获取到时间片,也开始把加完后的1 往value 放,value 还是1
- 本来最终应该是2,结果是1,这就是线程安全性问题
如何解决上面代码的线程安全性问题:
package thread; public class Sequence { private int value; public synchronized int getNext() { return value++; } public static void main(String[] args) { Sequence sequence = new Sequence(); new Thread(new Runnable() { @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " " + sequence.getNext()); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " " + sequence.getNext()); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " " + sequence.getNext()); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } }
在 getNext() 方法前加了synchronized关键字,让方法变成同步方法就不会再有重复的数据
Synchronized 相当于一个门加了一把锁,当一个线程执行的时候,就获取了这把锁,然后把门锁上,其他线程来的时候就要在外面等待,等执行完毕后把锁释放,其他线程才能进来。就会导致同一时刻只会有一个线程执行该段代码。
什么情况会出现线程安全性问题:
- 多线程环境
- 多个线程共享一个资源
- 对资源进行非原子性操作
总结
本文主要介绍了线程的性能问题,死锁问题以及如何使用jconsole 查看线程是否发生死锁,线程的饥饿与公平,线程安全性问题:从字节码角度来分析线程安全性问题、如何解决线程安全的问题以及在什么情况下会出现线程安全性问题。
微信公众号

出处:https://www.cnblogs.com/bigdata1024/p/11048231.html
本文以学习、研究和分享为主,如需转载,请联系本人,标明作者和出处,非商业用途!
posted on 2019-06-19 22:24 chaplinthink 阅读(194) 评论(0) 编辑 收藏 举报
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 理解Rust引用及其生命周期标识(下)
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 2025成都.NET开发者Connect圆满结束
· 后端思维之高并发处理方案
· 在 VS Code 中,一键安装 MCP Server!
· 千万级大表的优化技巧
· 10年+ .NET Coder 心语 ── 继承的思维:从思维模式到架构设计的深度解析