现代操作系统基本用时分的形式调度线程,将操作系统分成很多小片,然后分配给线程,线程用完了就发生线程调度,等待下次分配。线程分配到的时间片多少决定了线程使用处理器资源的多少,而线程优先级就是决定线程需要分配多或者少分配一些处理器资源的线程属性。
(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 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
java线程在运行的生命周期中可能处于以下6种不同的状态,在某一时刻只能处于其中的一种状态
(1)运行和就绪两个状态合称为运行状态。
(2)等待和超时等待是同一级别的,都可以通过其他线程的通知结束等待,但超时等待还可以通过等待时间完成来结束等待。
(3)调用同步方法时,如果得不到锁就会进入阻塞。(阻塞状态在java.concurrent包中的Lock接口的线程状态是等待状态)。
(4)线程运行完了run方法就会进入终止。
线程运行前需要先构造一个线程,下面是java.lang.Thread中对线程进行初始化的部分
父线程就是当前线程(开启多线程的线程),子线程会具有与父线程一致的优先级, 守护线程,线程组,还会有父线程的可继承ThreadLocal。还会分配给一个唯一的ID。 init()运行完毕,线程对象就初始化好了,在堆内存中等待运行。
线程完成初始化后,调用start()方法就可以启动这个线程,线程start()的含义:当前线程(即parent线程)同步告知JVM,只要线程规划器空闲,应立即启动调用start()方法的线程。
注意:启动一个线程前,最好设置自定义的名称方便分析问题。
一个标识位属性
(1)设置
通过调用线程的 interrupt() 方法使其进入中断状态。
(2)检查
线程可以通过检查自身是否被中断来进行响应。线程通过方法 isInterrupted() 来判断是否被中断,也可以调用静态方法Thread.interrupted()对当前线程的中断进行复位。
(1)线程已经结束,即使线程曾经处于中断状态,调用线程对象的isInterrupted()依旧会返回false。
(2)只要线程进入打断状态(调用interrupt()方法),再调用sleep()会抛出异常InterruptedException。同时JVM会将线程的打断状态清空,此时再调用isInterrupted()会返回false。
suspend():暂停线程、resume():恢复线程、stop():停止线程,这三个方法都过期了。
原因:suspend()会导致线程占用资源进入休眠状态,容易导致死锁。stop()不能保证线程资源的正确释放,一旦调用直接结束,可能会导致程序运行在不确定的状态。
暂停恢复方法可以用后面的等待/通知机制完成。
---强制正在执行的线程休眠(暂停执行),单位是毫秒(不释放资源)。
注意:线程睡眠到期自动苏醒,并返回到可运行的状态。sleep()中指定的时间是指线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。
通过中断操作和 cancel()
方法均可使线程得以终止。这种通过标识位或者中断操作的方式能够使线程在终止时有机会去清理资源,而不是武断地将线程停止,因此这种终止线程的做法显得更加安全和优雅。
@Test
public void test1() throws InterruptedException {
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
logger.info("Current date:{}", System.currentTimeMillis());
}
});
thread.start();
Thread.sleep(3000);
thread.interrupt();
if(thread.isInterrupted()){
logger.info("Thread was interrupted..");
}
Thread.sleep(3000);
}
Daemon线程是一种支持型线程,因为它主要被用作程序中后台调度以及支持性工作。当一个Java虚拟机中不存在非Daemon线程的时候,Java虚拟机将会退出。
通过调用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块并没有执行。