一、线程的优先级
1、多线程实现原理
现代操作系统基本用时分的形式调度线程,将操作系统分成很多小片,然后分配给线程,线程用完了就发生线程调度,等待下次分配。线程分配到的时间片多少决定了线程使用处理器资源的多少,而线程优先级就是决定线程需要分配多或者少分配一些处理器资源的线程属性。
2、线程优先级实现
(1)决定线程分配时间片的数量;
范围从1~10,默认为5,可通过setPriority(int)
方法修改优先级;
(2)对于频繁阻塞的(休眠或者I/O操作)线程,设置较高优先级;对于偏重计算的,设置较低的优先级;
(3)不同的JVM及操作系统中,线程规划会存在差异,有的甚至会忽略对线程优先级的设定。如windows环境是生效的,ubuntu 14.04不生效。
demo:
public class Priority {
private static volatile boolean notStart = true;
private static volatile boolean notEnd = true;
public static void main(String[] args) throws InterruptedException {
List<Job> jobs = new ArrayList<>();
for (int i = 0; i < 10; i++) {
// 前五个最小优先级,后五个最大优先级
int priority = i < 5 ? Thread.MIN_PRIORITY : Thread.MAX_PRIORITY;
// 这个类里面的priority只是为了做标记
Job job = new Job(priority);
jobs.add(job);
// 第一个参数是要调用的对象,当线程start的时候,就调用他的run
Thread thread = new Thread(job, "Thread:" + i);
thread.setPriority(priority);
thread.start();
}
// 之前所有的都start,然后都陷在了while循环中
// 这里一改,全部就都动起来了
notStart = false;
// 为什么要等待呢?
// 答:其他线程已经启动了,这个等待并不会停止其他线程,应是只停止了main自己的
// 留出10s的时间,方便其他计算线程体现出差距
TimeUnit.SECONDS.sleep(10);
notEnd = false;
for (Job job : jobs){
System.out.println("Job Priority : " + job.priority + ", Count: " + job.jobCount);
}
}
// 以实现Runnable来控制线程
static class Job implements Runnable{
private int priority;
private long jobCount;
// 构造函数
public Job(int priority){
this.priority = priority;
}
@Override
public void run() {
while (notStart){
// 提示调度程序当前线程愿意放弃当前对处理器的使用。
// 调度器可以忽略这个提示。
Thread.yield();
}
while (notEnd){
// 可以让出cpu,受优先级影响?
// 让出完了再抢回来,看谁抢的厉害?
Thread.yield();
jobCount++;
}
}
}
}
输出
Job Priority : 1, Count: 4305207
Job Priority : 1, Count: 4097408
Job Priority : 1, Count: 4047924
Job Priority : 1, Count: 4051995
Job Priority : 1, Count: 4278124
Job Priority : 10, Count: 6725872
Job Priority : 10, Count: 6589712
Job Priority : 10, Count: 7407455
Job Priority : 10, Count: 7430622
Job Priority : 10, Count: 6347184
二、线程的状态
1、6种状态
java线程在运行的生命周期中可能处于以下6种不同的状态,在某一时刻只能处于其中的一种状态
2、java线程切换图
三、线程状态切换方法
1、创建:
线程运行前需要先构造一个线程,下面是java.lang.Thread中对线程进行初始化的部分
父线程就是当前线程(开启多线程的线程),子线程会具有与父线程一致的优先级, 守护线程,线程组,还会有父线程的可继承ThreadLocal。还会分配给一个唯一的ID。 init()运行完毕,线程对象就初始化好了,在堆内存中等待运行。
2、就绪:
线程完成初始化后,调用start()方法就可以启动这个线程,调用start()方法后等待CPU调度。
线程start()的含义:当前线程(即parent线程)同步告知JVM,只要线程规划器空闲,应立即启动调用start()方法的线程。注意:启动一个线程前,最好设置自定义的名称方便分析问题。
run() 和 start() 的区别:
start() 方法用于启动线程,run() 方法用于执行线程的运行时代码。run() 可以重复调用,而 start() 只能调用一次。
3、 运行:
就绪的线程被调用并获取处理器资源;运行和就绪两个状态也合称为运行状态。
4、阻塞:
一个正在执行的线程被挂起,sleep、wait、suspend。
调用同步方法时,如果得不到锁就会进入阻塞。(阻塞状态在java.concurrent包中的Lock接口的线程状态是等待状态)。等待和超时等待是同一级别的,都可以通过其他线程的通知结束等待,但超时等待还可以通过等待时间完成来结束等待。
5、死亡:
线程调用stop方法或者是线程运行完了run方法就会进入终止。
具体方法的使用详解见下一篇。
三、线程的其他方法
1、设置为Daemon线程
1.1介绍
Daemon线程是一种支持型线程,因为它主要被用作程序中后台调度以及支持性工作。当一个Java虚拟机中不存在非Daemon线程的时候,Java虚拟机将会退出。
1.2、实现
通过调用Thread.setDaemon(true)将线程设置为Daemon线程。Daemon属性需要在启动线程之前设置,不能在启动线程之后设置。在构建Daemon线程时,不能依靠finally块中的内容来确保执行关闭或清理资源的逻辑。
public class Daemon {
public static void main(String[] args) {
Thread thread = new Thread(new DeamonRunner(),"DeamonRunner");
thread.setDaemon(true);
thread.start();
}
static class DeamonRunner implements Runnable{
@Override
public void run() {
try {
Thread.sleep(2000l);
} finally {
System.out.println("DeamonThread finally run.");
}
}
}
}
运行Deamon程序,可以看到在终端或者命令提示符没有任何输出。
没有任何输出原因:mian()线程(非Daemon线程)在启动了线程DaemonRunner之后随着main方法执行完毕而终止,而此时Java虚拟机 中已经没有非Daemon线程,虚拟机需要退出。Java虚拟机中的所有Daemon线程都需要立即终止,因此DaemonRunner立即终止,但是DaemonRunner中的finally块并没有执行。