线程性能问题
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 查看线程是否发生死锁, 线程的饥饿与公平, 线程安全性问题: 从字节码角度来分析线程安全性问题, 如何解决线程安全的问题以及在什么情况下会出现线程安全性问题.
来源: http://www.bubuko.com/infodetail-3097733.html