1.活跃性问题
- 死锁:指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进(五个哲学家吃饭,每个人一只筷子,谁都不肯舍弃自己的筷子,最终饿死的事情,其实就是一个死锁的问题)
- 饥饿:线程因无法访问所需资源而无法执行下去的情况(例如购买车票,买完车票占着窗口不走,导致后面的人无法购票)。
- 活锁:活锁恰恰与死锁相反,死锁是大家都拿不到资源都占用着对方的资源,而活锁是拿到资源却又相互释放不执行。
1.1饥饿情景
- 高优先级吞噬所有低优先级的CPU时间片
public class Demo8 { /** * 高优先级吞噬低优先级的CPU时间片 * 当高优先级线程多的时候可能就会造成低优先级线程无法抢占时间片的问题 */ public static void main (String[] args) { Thread thread1 = new Thread(new Target()); Thread thread2 = new Thread(new Target()); // 设置优先级,实际开发的时候建议不要自己指定等级 // 因为不同的操作系统优先级不同,建议使用Thread常量MIN_PRIORITY与MAX_PRIORITY // MIN_PRIORITY与MAX_PRIORITY在不同的操作系统中对应的值也不同 thread1.setPriority(Thread.MIN_PRIORITY); thread2.setPriority(Thread.MAX_PRIORITY); thread1.start(); thread2.start(); } } public class Target implements Runnable{ @Override public void run () { while (true){ System.out.println(Thread.currentThread().getName()+"~~~~~~~~~~~~~runnable init."); } } }
- 线程被永久堵塞在一个等待进入同步快的状态
- 等待的线程永远不被唤醒
如何尽量避免饥饿问题
- 设置合理的线程优先级(thread.setPriority)
- 使用锁来代替synchronized(尽可能保证公平)
2.性能问题
时间片(timeslice)又称为“量子(quantum)”或“处理器片(processor slice)”是分时操作系统分配给每个正在运行的进程微观上的一段CPU时间(在抢占内核中是:从进程开始运行直到被抢占的时间)。
通常状况下,一个系统中所有的进程被分配到的时间片长短并不是相等的,尽管初始时间片基本相等(在Linux系统中,初始时间片也不相等,而是各自父进程的一半),系统通过测量进程处于“睡眠”和“正在运行”状态的时间长短来计算每个进程的交互性,交互性和每个进程预设的静态优先级(Nice值)的叠加即是动态优先级,动态优先级按比例缩放就是要分配给那个进程时间片的长短。一般地,为了获得较快的响应速度,交互性强的进程(即趋向于IO消耗型)被分配到的时间片要长于交互性弱的(趋向于处理器消耗型)进程。 [1]
时间片分配线程时间,线程进行任务切换(上下文切换)的过程中也会消耗性能,线程未执行完时间片切换是要记录当前线程执行的状态,下次执行的时候,拿到执行状态继续执行,在上下文切换的过程中会非常浪费cpu资源
3.安全性问题
产生原因:多线程环境下,共享一个资源,对共享资源进行非原子性操作
- synchronized 同步解决共享数据计算不一致问题(可以去掉synchronized跑一下,看数据结果,会发现数据小于等于10000)
public class Demo1 { private volatile static int value; private static synchronized int getNext(){ return value++; } /** * * 模仿公用变量多线程引起数据不一致,多个线程共享一个变量, * 在字节码执行计算时(iadd、putfield、ireturn)可能出现问题 * 堆:线程所共享区域(value这个对象在共享区域) * 实例化对象:在队列中 * 程序接收器:线程独享区域 * @param args */ public static void main (String[] args) { for (int i = 0; i < 10; i++) { new Thread(new Runnable() { @Override public void run () { for (int i = 0; i < 1000; i++) { getNext(); } } }).start(); } // 睡眠1秒 保证线程执行完成后,数据写完成 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(String.valueOf(value)); } }
- 当进行++时一个线程通过总线后会导致其他线程中读取的属性失效,从而导致一个线程中的一个循环未成功进行+1操作