线程类的常见方法介绍
线程类有好多方法,下面介绍一些常用的方法。
1.start() 实例方法
启动一个线程用的是thread.start()方法,如果直接调用run方法是同步调用,相当于一个普通的方法调用。
start()方法使线程开始执行,JVM会自动调用线程的run方法。new出来线程,调用start()方法即处于RUNNABLE(可运行)状态了。处于RUNNABLE状态的线程可能正在Java虚拟机中运行,也可能正在等待处理器的资源,因为一个线程必须获得CPU的资源后,才可以运行其run()方法中的内容,否则排队等待。
/** * Causes this thread to begin execution; the Java Virtual Machine * calls the <code>run</code> method of this thread. * <p> * The result is that two threads are running concurrently: the * current thread (which returns from the call to the * <code>start</code> method) and the other thread (which executes its * <code>run</code> method). * <p> * It is never legal to start a thread more than once. * In particular, a thread may not be restarted once it has completed * execution. * * @exception IllegalThreadStateException if the thread was already * started. * @see #run() * @see #stop() */ public synchronized void start() { if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { } } }
根据线程的状态来判断是否已经调用其start()方法, threadStatus 可以保证只调用一次start,多次调用会报错。并且在start()方法中调用了一个start0()方法,start0()是一个native方法。
/* Java thread status for tools, * initialized to indicate thread 'not yet started' */ private volatile int threadStatus = 0;
private native void start0();
两次调用start()方法会报错:
package cn.qlq.thread.one; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SubThread extends Thread { private static final Logger log = LoggerFactory.getLogger(SubThread.class); @Override public void run() { log.debug("subThread run,threadname->{}", Thread.currentThread().getName()); } public static void main(String[] args) { SubThread subThread = new SubThread(); subThread.start(); subThread.start(); log.debug("运行结束,threadname->{}", Thread.currentThread().getName()); } }
结果:
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:705)
at cn.qlq.thread.one.SubThread.main(SubThread.java:18)
2018-12-05 14:04:26 [cn.qlq.thread.one.SubThread]-[DEBUG] subThread run,threadname->Thread-0
2. Thread.currentThread静态方法
/** * Returns a reference to the currently executing thread object. * * @return the currently executing thread. */ public static native Thread currentThread();
currentThread返回当前代码段正在被哪个调用的线程。例如:
package cn.qlq.thread.two; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 研究静态方法currentThread * * @author Administrator * */ public class CurrentThreadMethodDemo { private static final Logger log = LoggerFactory.getLogger(CurrentThreadMethodDemo.class); public static void main(String[] args) { Thread currentThread = Thread.currentThread(); log.debug("currentThread -> {}", currentThread); log.debug("currentThreadName -> {}", currentThread.getName()); } }
结果:
2018-12-05 16:07:51 [cn.qlq.thread.two.CurrentThreadMethodDemo]-[DEBUG] currentThread -> Thread[main,5,main]
2018-12-05 16:07:51 [cn.qlq.thread.two.CurrentThreadMethodDemo]-[DEBUG] currentThreadName -> main
查看Thread.toString()的源码返回的是线程的名称+优先级+所属组的名称:
public String toString() { ThreadGroup group = getThreadGroup(); if (group != null) { return "Thread[" + getName() + "," + getPriority() + "," + group.getName() + "]"; } else { return "Thread[" + getName() + "," + getPriority() + "," + "" + "]"; } }
3.实例方法 isAlive()
/** * Tests if this thread is alive. A thread is alive if it has * been started and has not yet died. * * @return <code>true</code> if this thread is alive; * <code>false</code> otherwise. */ public final native boolean isAlive();
isAlive方法用于判断当前线程是否处于活动状态。什么是活动状态呢?活动状态就是已经启动尚未终止的,线程处于正在运行或者准备开始运行的状态就认为线程是"活动"的。(新建状态的线程isAlive()返回的是false)
package cn.qlq.thread.two; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 研究isAlive方法 * * @author Administrator * */ public class IsAliveMethodDemo { private static final Logger log = LoggerFactory.getLogger(IsAliveMethodDemo.class); public static void main(String[] args) { Thread r1 = new Thread() { @Override public void run() { log.debug("run isAlive->{}", this.isAlive()); } }; log.debug("begain---r1 isAlive->{}", r1.isAlive()); r1.start(); log.debug("end---r1 isAlive->{}", r1.isAlive()); } }
结果:
2018-12-05 16:25:34 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] begain---r1 isAlive->false
2018-12-05 16:25:34 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] end---r1 isAlive->true
2018-12-05 16:25:34 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] run isAlive->true
需要说明如下代码:
log.debug("end---r1 isAlive->{}", r1.isAlive());
虽然在上面打印的结果是true,但是此值是不确定的。打印true是因为r1还没有执行完毕,将上面代码修改为下面:
log.debug("begain---r1 isAlive->{}", r1.isAlive()); r1.start(); Thread.sleep(1 * 1000); log.debug("end---r1 isAlive->{}", r1.isAlive());
结果:
2018-12-05 16:34:19 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] begain---r1 isAlive->false
2018-12-05 16:34:19 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] run isAlive->true
2018-12-05 16:34:20 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] end---r1 isAlive->false
再次修改代码run方法中休眠10秒钟:
package cn.qlq.thread.two; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 研究isAlive方法 * * @author Administrator * */ public class IsAliveMethodDemo { private static final Logger log = LoggerFactory.getLogger(IsAliveMethodDemo.class); public static void main(String[] args) throws InterruptedException { Thread r1 = new Thread() { @Override public void run() { try { Thread.sleep(1 * 10000); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("run isAlive->{}", this.isAlive());// F } }; log.debug("begain---r1 isAlive->{}", r1.isAlive());// T r1.start(); log.debug("end---r1 isAlive->{}", r1.isAlive());// T log.debug("finish"); } }
结果:
2018-12-05 17:08:52 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] begain---r1 isAlive->false
2018-12-05 17:08:52 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] end---r1 isAlive->true
2018-12-05 17:08:52 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] finish
2018-12-05 17:09:02 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] run isAlive->true
我们在主线程中执行r1.isAlive的时候会等待r1线程休眠过后才打印run方法中的isAlive。也就是说当一个Thread休眠之后,会继续执行休眠之后的代码。
4.静态方法 sleep()
/** * Causes the currently executing thread to sleep (temporarily cease * execution) for the specified number of milliseconds, subject to * the precision and accuracy of system timers and schedulers. The thread * does not lose ownership of any monitors. * * @param millis * the length of time to sleep in milliseconds * * @throws IllegalArgumentException * if the value of {@code millis} is negative * * @throws InterruptedException * if any thread has interrupted the current thread. The * <i>interrupted status</i> of the current thread is * cleared when this exception is thrown. */ public static native void sleep(long millis) throws InterruptedException;
方法sleep()的作用是在指定的毫秒数内让当前"正在执行的线程"休眠(暂停执行)。 这个"正在执行的线程"是指Thread.currentThread()返回的线程。但是sleep不会释放锁。(The thread does not lose ownership of any monitors.)
sleep(long)使当前线程进入超时等待(TIMED_WAITING)状态,所以执行sleep()的线程在指定的时间内肯定不会被执行;
sleep(long)可使优先级低的线程得到执行的机会,当然也可以让同优先级的线程有执行的机会;
sleep(long)是不会释放锁标志的。
会抛出中断异常
测试代码:
package cn.qlq.thread.two; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 研究sleep方法 * * @author Administrator * */ public class SleepMethodDemo { private static final Logger log = LoggerFactory.getLogger(SleepMethodDemo.class); public static void main(String[] args) throws InterruptedException { Thread r1 = new Thread() { @Override public void run() { try { Thread.sleep(10 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("over sleep,{}", System.currentTimeMillis()); } }; log.debug("begain->{}", System.currentTimeMillis()); r1.start(); log.debug("end---r1 isAlive->{}", r1.isAlive()); log.debug("end->{}", System.currentTimeMillis()); } }
结果:
2018-12-05 17:27:59 [cn.qlq.thread.two.SleepMethodDemo]-[DEBUG] begain->1544002079805
2018-12-05 17:27:59 [cn.qlq.thread.two.SleepMethodDemo]-[DEBUG] end---r1 isAlive->true
2018-12-05 17:27:59 [cn.qlq.thread.two.SleepMethodDemo]-[DEBUG] end->1544002079808
2018-12-05 17:28:09 [cn.qlq.thread.two.SleepMethodDemo]-[DEBUG] over sleep,1544002089808
补充:一道经典的面试题:
Thread.sleep(0)的作用是什么?
由于Java采用抢占式的线程调度算法,因此可能会出现某条线程常常获取到CPU控制权的情况,为了让某些优先级比较低的线程也能获取到CPU控制权,可以使用Thread.sleep(0)手动触发一次操作系统分配时间片的操作,这也是平衡CPU控制权的一种操作。
5.实例方法 getId()
/** * Returns the identifier of this Thread. The thread ID is a positive * <tt>long</tt> number generated when this thread was created. * The thread ID is unique and remains unchanged during its lifetime. * When a thread is terminated, this thread ID may be reused. * * @return this thread's ID. * @since 1.5 */ public long getId() { return tid; }
此方法返回线程的唯一表示,是一个long型的正数,在线程创建的时候被赋值。
其生成方式是一个静态成员变量一直在自增,并且自增方法也加了同步锁。也就是说线程的ID是0开始一直自增。
/* For generating thread ID */ private static long threadSeqNumber; private static synchronized long nextThreadID() { return ++threadSeqNumber; }
补充:顺便提一下name的生成方式:(Thread-0,默认是线程名称是Thread-加上一个自增的数, threadInitNumber用于name的自增)
/* For autonumbering anonymous threads. */ private static int threadInitNumber; private static synchronized int nextThreadNum() { return threadInitNumber++; } public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } private void init(ThreadGroup g, Runnable target, String name, long stackSize) { init(g, target, name, stackSize, null); }
测试代码:
package cn.qlq.thread.two; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 研究getId * * @author Administrator * */ public class GetIdMethodDemo extends Thread { private static final Logger log = LoggerFactory.getLogger(GetIdMethodDemo.class); @Override public void run() { log.debug("threadName->{},getId->{}", Thread.currentThread().getName(), Thread.currentThread().getId()); } public static void main(String[] args) throws InterruptedException { log.debug("threadName->{},getId->{}", Thread.currentThread().getName(), Thread.currentThread().getId()); for (int i = 0; i < 10; i++) { GetIdMethodDemo t1 = new GetIdMethodDemo(); t1.start(); } } }
结果:
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->main,getId->1
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-0,getId->9
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-1,getId->10
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-2,getId->11
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-3,getId->12
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-5,getId->14
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-4,getId->13
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-8,getId->17
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-6,getId->15
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-9,getId->18
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-7,getId->16
main线程的ID为1.其余线程名称后的默认序号是正常的递增,但是ID却是缺失了中间的2-8,这也是一直跟代码没有跟出来。。。。。。。(此处有疑问-----答案在最后补充中得以证实)
6.Thread.yield()静态方法没有参数
/** * A hint to the scheduler that the current thread is willing to yield * its current use of a processor. The scheduler is free to ignore this * hint. * * <p> Yield is a heuristic attempt to improve relative progression * between threads that would otherwise over-utilise a CPU. Its use * should be combined with detailed profiling and benchmarking to * ensure that it actually has the desired effect. * * <p> It is rarely appropriate to use this method. It may be useful * for debugging or testing purposes, where it may help to reproduce * bugs due to race conditions. It may also be useful when designing * concurrency control constructs such as the ones in the * {@link java.util.concurrent.locks} package. */ public static native void yield();
此方法的作用是放弃当前的CPU资源,将它让给其他的任务去占用CPU的时间。但是放弃的时间不确定,有可能刚刚放弃就马上获得CPU时间片。
sleep 方法使当前运行中的线程睡眠一段时间,进入超时等待状态,这段时间的长短是由程序设定的,yield方法使当前线程让出CPU占有权,但让出的时间是不可设定的。
yield()也不会释放锁标志。
yield()只是使当前线程重新回到可运行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
yield()只能使同优先级或更高优先级的线程有执行的机会。
实际上,yield()方法对应了如下操作;先检测当前是否有相同优先级的线程处于同可运行状态,如有,则把CPU的占有权交给次线程,否则继续运行原来的线程,所以yield()方法称为“退让”,它把运行机会让给了同等级的其他线程。
测试代码:
package cn.qlq.thread.two; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class YieldMethodDemo extends Thread { private static final Logger log = LoggerFactory.getLogger(YieldMethodDemo.class); @Override public void run() { long begainTime = System.currentTimeMillis(); int count = 0; for (int i = 0; i < 5000000; i++) { Thread.yield(); count = count + (i + 1); } long endTime = System.currentTimeMillis(); log.debug("用时:{}毫秒", endTime - begainTime); } public static void main(String[] args) { YieldMethodDemo tDemo = new YieldMethodDemo(); tDemo.start(); } }
结果:
2018-12-05 21:23:40 [cn.qlq.thread.two.YieldMethodDemo]-[DEBUG] 用时:6392毫秒
将上面Thread.yield()注释掉再次测试:
结果:
2018-12-05 21:27:26 [cn.qlq.thread.two.YieldMethodDemo]-[DEBUG] 用时:14毫秒
7. join实例方法:
join方法的主要作用就是同步,它可以使得线程之间的并行执行变为串行执行。在A线程中调用了B线程的join()方法时,表示只有当B线程执行完毕时,A线程才能继续执行。
join方法中如果传入参数,则表示这样的意思:如果A线程中掉用B线程的join(10),则表示A线程会等待B线程执行10毫秒,10毫秒过后,A、B线程并行执行。需要注意的是,jdk规定,join(0)的意思不是A线程等待B线程0秒,而是A线程等待B线程无限时间,直到B线程执行完毕,即join(0)等价于join()。
join方法必须在线程start方法调用之后调用才有意义。这个也很容易理解:如果一个线程都没有start,那它也就无法同步了。
源码如下:
/** * Waits for this thread to die.*/ public final void join() throws InterruptedException { join(0); }
public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } }
测试代码:
package cn.qlq.thread.two; /** * 原生的线程类Thread的使用方法 * * @author Administrator * */ public class JoinMethod extends Thread { /** * 更改线程名字 * * @param threadName */ public JoinMethod(String threadName) { this.setName(threadName); } @Override public void run() { for (int i = 0; i < 5; i++) { try { Thread.sleep(1 * 500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "-----" + i); } } public static void main(String[] args) { JoinMethod t1 = new JoinMethod("t1"); JoinMethod t2 = new JoinMethod("t2"); JoinMethod t3 = new JoinMethod("t3"); t1.start(); /** * join的意思是使得放弃当前线程的执行,并返回对应的线程,例如下面代码的意思就是: * 程序在main线程中调用t1线程的join方法,则main线程放弃cpu控制权,并返回t1线程继续执行直到线程t1执行完毕 * 所以结果是t1线程执行完后,才到主线程执行,相当于在main线程中同步t1线程,t1执行完了,main线程才有执行的机会 */ try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } if (t2.isAlive()) { System.out.println("t2 is alive"); } else { System.out.println("t2 is not alive"); } t2.start(); t3.start(); } }
结果:
t1-----0
t1-----1
t1-----2
t1-----3
t1-----4
t2 is not alive
t2-----0
t3-----0
t2-----1
t3-----1
t2-----2
t3-----2
t2-----3
t3-----3
t2-----4
t3-----4
8.线程的优先级
在操作系统中,线程可以划分优先级,优先级较高的线程可以获得更多的CPU资源,也就是CPU优先执行优先级较高的线程对象中的任务。
设置线程优先级有助于帮"线程规划器"确定在下一次选择哪一个线程来优先执行。
源码如下:
public final void setPriority(int newPriority) { ThreadGroup g; checkAccess(); if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) { throw new IllegalArgumentException(); } if((g = getThreadGroup()) != null) { if (newPriority > g.getMaxPriority()) { newPriority = g.getMaxPriority(); } setPriority0(priority = newPriority); } }
在Java中,线程的优先级分为1-10,小于1或者大于10JDK会抛出IllegalArgumentException异常
/** * The minimum priority that a thread can have. */ public final static int MIN_PRIORITY = 1; /** * The default priority that is assigned to a thread. */ public final static int NORM_PRIORITY = 5; /** * The maximum priority that a thread can have. */ public final static int MAX_PRIORITY = 10;
(1)线程优先级具有继承性,如果A线程创建B线程,则B线程的优先级与A线程是一样的
package cn.qlq.thread.two; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 线程优先级的方法 * * @author QiaoLiQiang * @time 2018年12月5日下午10:44:25 */ public class PriorityMethod extends Thread { private static final Logger log = LoggerFactory.getLogger(PriorityMethod.class); @Override public void run() { Runnable r1 = new Runnable() { @Override public void run() { try { Thread.sleep(1 * 100); } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread thread = new Thread(r1); thread.start(); log.debug("thread.getPriority->{}", thread.getPriority()); } public static void main(String[] args) { // 线程里面创建线程 PriorityMethod priorityMethod = new PriorityMethod(); priorityMethod.setPriority(9); priorityMethod.start(); } }
结果:
2018-12-05 22:52:50 [cn.qlq.thread.two.PriorityMethod]-[DEBUG] thread.getPriority->9
(2)优先级具有规则性--高优先级的线程总是大部分先执行完
package cn.qlq.thread.two; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 线程优先级的方法(高优先级的大部分先执行完) * * @author QiaoLiQiang * @time 2018年12月5日下午10:44:25 */ public class PriorityMethod2 extends Thread { private static final Logger log = LoggerFactory.getLogger(PriorityMethod2.class); public PriorityMethod2(int privority) { this.setPriority(privority); } @Override public void run() { for (int i = 0; i < 3; i++) { try { Thread.sleep(1 * 100); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("[end]thread.getPriority->{}", this.getPriority()); } public static void main(String[] args) { // 线程里面创建线程 PriorityMethod2 priorityMethod1 = new PriorityMethod2(9); PriorityMethod2 priorityMethod2 = new PriorityMethod2(9); PriorityMethod2 priorityMethod3 = new PriorityMethod2(9); PriorityMethod2 priorityMethod4 = new PriorityMethod2(2); PriorityMethod2 priorityMethod5 = new PriorityMethod2(2); PriorityMethod2 priorityMethod6 = new PriorityMethod2(2); PriorityMethod2 priorityMethod7 = new PriorityMethod2(5); PriorityMethod2 priorityMethod8 = new PriorityMethod2(5); PriorityMethod2 priorityMethod9 = new PriorityMethod2(5); priorityMethod1.start(); priorityMethod2.start(); priorityMethod3.start(); priorityMethod4.start(); priorityMethod5.start(); priorityMethod6.start(); priorityMethod7.start(); priorityMethod8.start(); priorityMethod9.start(); } }
结果:
2018-12-05 23:01:25 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->9
2018-12-05 23:01:25 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->5
2018-12-05 23:01:25 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->9
2018-12-05 23:01:25 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->9
2018-12-05 23:01:25 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->5
2018-12-05 23:01:25 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->5
2018-12-05 23:01:25 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->2
2018-12-05 23:01:25 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->2
2018-12-05 23:01:25 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->2
(3)优先级具有随机性
随机性是指不一定每次都是优先级高的线程先执行完。线程的执行具有不确定性和随机性。尤其是当优先级接近的时候更是具有不确定性。
package cn.qlq.thread.two; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 线程优先级的方法(高优先级的大部分先执行完) * * @author QiaoLiQiang * @time 2018年12月5日下午10:44:25 */ public class PriorityMethod2 extends Thread { private static final Logger log = LoggerFactory.getLogger(PriorityMethod2.class); public PriorityMethod2(int privority) { this.setPriority(privority); } @Override public void run() { for (int i = 0; i < 3; i++) { try { Thread.sleep(1 * 100); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("[end]thread.getPriority->{}", this.getPriority()); } public static void main(String[] args) { // 线程里面创建线程 PriorityMethod2 priorityMethod1 = new PriorityMethod2(9); PriorityMethod2 priorityMethod2 = new PriorityMethod2(9); PriorityMethod2 priorityMethod3 = new PriorityMethod2(9); PriorityMethod2 priorityMethod4 = new PriorityMethod2(8); PriorityMethod2 priorityMethod5 = new PriorityMethod2(8); PriorityMethod2 priorityMethod6 = new PriorityMethod2(8); PriorityMethod2 priorityMethod7 = new PriorityMethod2(7); PriorityMethod2 priorityMethod8 = new PriorityMethod2(7); PriorityMethod2 priorityMethod9 = new PriorityMethod2(7); priorityMethod1.start(); priorityMethod2.start(); priorityMethod3.start(); priorityMethod4.start(); priorityMethod5.start(); priorityMethod6.start(); priorityMethod7.start(); priorityMethod8.start(); priorityMethod9.start(); } }
结果:
2018-12-05 23:03:18 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->8
2018-12-05 23:03:18 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->7
2018-12-05 23:03:18 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->8
2018-12-05 23:03:18 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->8
2018-12-05 23:03:18 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->7
2018-12-05 23:03:18 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->7
2018-12-05 23:03:18 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->9
2018-12-05 23:03:18 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->9
2018-12-05 23:03:18 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->9
9.守护线程
在Java中有两种线程,一种是守护线程,一种是用户线程。
守护线程是一种特殊的线程,当进程中不存在非守护线程了,则守护线程自动销毁。典型的守护线程就是垃圾回收线程,当进程中没有非守护线程也就没有垃圾回收的必要了。
守护线程的作用就是为其他线程的运行提供便利,最典型的例子是GC线程。
关于守护线程应该注意:
(1) thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。
(2) 在Daemon线程中产生的新线程也是Daemon的。新建一个线程默认是用户线程。
(3) 守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断
package cn.qlq.thread.two; /** * 守护线程 * * @author QiaoLiQiang * @time 2018年12月5日下午11:09:49 */ public class DaemonThread extends Thread { private int i; @Override public void run() { while (true) { System.out.println(i++); try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { DaemonThread daemonThread = new DaemonThread(); daemonThread.setDaemon(true); daemonThread.start(); System.out.println(daemonThread.isDaemon()); Thread.sleep(5000); System.out.println("我离开后daemonThread也结束打印"); } }
结果:(因为主线程退出之后没有任何用户线程,所以守护线程也就结束了-------所有用户线程结束,所以守护线程也结束,对应的进程也就结束了)
true
0
1
2
3
4
我离开后daemonThread也结束打印
补充:关于守护线程和非守护线程更正确的认识是:
其实守护线程和非守护线程一样,只是如果是守护线程,在没有用户线程的情况下会结束(所有用户线程的结束会导致守护线程的结束,进而导致进程的结束);而如果是用户线程的话只有在线程完结的情况下才会结束。
例如:上面的代码中将设为守护线程的代码注掉,继续执行:
结果:(主线程结束但是仍然在打印,因为daemonThread是一个用户线程------虽然主线程结束,但是存在非守护,所以守护线程不会结束,进程也不会结束)
查看进程的线程信息,发现不存在main线程(main线程已经结束---这里也证明主线程的结束不会影响其他用户线程)
补充:守护线程run执行完毕也会结束线程,并不是说守护线程就一直存活
package cn.qlq.thread.two; /** * 守护线程 * * @author QiaoLiQiang * @time 2018年12月5日下午11:09:49 */ public class DaemonThread extends Thread { private int i; @Override public void run() { System.out.println(i++); } public static void main(String[] args) throws InterruptedException { DaemonThread daemonThread = new DaemonThread(); daemonThread.setDaemon(true); daemonThread.start(); while (true) { } } }
jvisualvm查看线程信息: (守护线程run执行完所以也正常结束)
10.其他方法
其他还有获取线程名称,线程组的方法等方法。
package cn.qlq.thread.two; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 研究其他方法的使用 * * @author QiaoLiQiang * @time 2018年12月5日下午10:21:16 */ public class OtherMethod extends Thread { private static final Logger log = LoggerFactory.getLogger(OtherMethod.class); @Override public void run() { try { Thread.sleep(10 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { OtherMethod otherMethod = new OtherMethod(); otherMethod.start(); log.debug("getName()->{}", otherMethod.getName());// 获取名称 log.debug("getPriority()->{}", otherMethod.getPriority());// 获取优先级 ThreadGroup threadGroup = otherMethod.getThreadGroup();// 获取线程组 log.debug("threadGroup - > {}", threadGroup); // 修改优先级 otherMethod.setPriority(9); log.debug("getPriority()->{}", otherMethod.getPriority()); // 修改名称 otherMethod.setName("newName"); log.debug("getName()->{}", otherMethod.getName()); // 获取所有线程的堆栈信息 Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces(); for (Map.Entry<Thread, StackTraceElement[]> stackTrace : allStackTraces.entrySet()) { Thread thread = (Thread) stackTrace.getKey(); StackTraceElement[] stackTraceElements = stackTrace.getValue(); log.debug(" 线程->" + thread.getName()); for (StackTraceElement s : stackTraceElements) { log.debug(" 线程stackTraceElements->" + s); } } } }
结果:
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] getName()->Thread-0
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] getPriority()->5
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] threadGroup - > java.lang.ThreadGroup[name=main,maxpri=10]
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] getPriority()->9
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] getName()->newName
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 线程->newName
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 线程stackTraceElements->java.lang.Thread.sleep(Native Method)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 线程stackTraceElements->cn.qlq.thread.two.OtherMethod.run(OtherMethod.java:21)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 线程->main
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 线程stackTraceElements->java.lang.Thread.dumpThreads(Native Method)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 线程stackTraceElements->java.lang.Thread.getAllStackTraces(Thread.java:1640)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 线程stackTraceElements->cn.qlq.thread.two.OtherMethod.main(OtherMethod.java:45)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 线程->Attach Listener
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 线程->Signal Dispatcher
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 线程->Reference Handler
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 线程stackTraceElements->java.lang.Object.wait(Native Method)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 线程stackTraceElements->java.lang.Object.wait(Object.java:503)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 线程stackTraceElements->java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 线程->Finalizer
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 线程stackTraceElements->java.lang.Object.wait(Native Method)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 线程stackTraceElements->java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 线程stackTraceElements->java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 线程stackTraceElements->java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
还可以获取所有活线程数量:
package cn.qlq.thread.two; public class OtherMethod2 { public static void main(String[] args) { // 构造一个线程并启动,取名为"my-thread" Thread myThread = new Thread(new Runnable() { public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }, "my-thread"); myThread.start(); // 返回一个活线程数量 int activeCount = Thread.activeCount(); System.out.println(activeCount); Thread[] ts = new Thread[activeCount]; Thread.enumerate(ts); for (Thread t : ts) { System.out.println("从主线程中找到线程名称为:" + t.getName() + ", 状态为: " + t.getState()); } } }
结果:
2
从主线程中找到线程名称为:main, 状态为: RUNNABLE
从主线程中找到线程名称为:my-thread, 状态为: TIMED_WAITING
补充:线程启动的时候JVM会创建几个默认的守护线程。
当我们启动一个线程的时候,JVM会自动创建几个默认的守护线程(这些守护线程并不是一直存活,有的线程会执行完毕),这也解释了上面的线程ID为什么缺失了0、2-8的ID。
例如:
package cn.qlq.thread.two; public class ThreadSleep2SeeJVMThread { public static void main(String[] args) { // 构造一个线程并启动,取名为"my-thread" Thread myThread = new Thread(new Runnable() { public void run() { try { Thread.sleep(100 * 10000); } catch (InterruptedException e) { e.printStackTrace(); } } }, "my-thread"); myThread.start(); System.out.println("myThread id ->" + myThread.getId()); try { Thread.sleep(500 * 10000); } catch (InterruptedException e) { e.printStackTrace(); } } }
结果:
myThread id ->9
在上面故意让主线程和新建的线程进行休眠是为了更好的查看线程的状态。现在我们利用JDK自带的JVisualVM查看线程:
总共11个线程,守护线程有9个。这也解释了上面的线程ID为什么缺失了0、2-8的ID。(查看线程的具体状态如下:)
可以看到main线程和my-thread是自己手动开启的线程,在休眠状态。
剩下的9个守护线程是JVM帮助我们创建的。解释其中几个重要的线程的意义:
- Attach Listener :线程是负责接收到外部的命令,而对该命令进行执行的并且吧结果返回给发送者。通常我们会用一些命令去要求jvm给我们一些反馈信息,如:java -version、jmap、jstack等等。如果该线程在jvm启动的时候没有初始化,那么,则会在用户第一次执行jvm命令时,得到启动。
- signal dispather: 前面我们提到第一个Attach Listener线程的职责是接收外部jvm命令,当命令接收成功后,会交给signal dispather线程去进行分发到各个不同的模块处理命令,并且返回处理结果。signal dispather线程也是在第一次接收外部jvm命令时,进行初始化工作。
- Finalizer: 用来执行所有用户Finalizer 方法的线程
- Reference Handler :它主要用于处理引用对象本身(软引用、弱引用、虚引用)的垃圾回收问题。
关于JVM自动启动的线程的解释参考:http://ifeve.com/jvm-thread/