创建Thread的两种方式
网上看到说啥的都有,最权威的还得看JDK文档,所以各位小伙伴在遇到这个面试题的时候不要答错哦!!!
一种是class继承Thread,重写run方法,在run方法里实现我们自己的逻辑
class MyThread extends Thread{
@override
public void run(){
/*我们自己的逻辑*/
}
}
new MyThread().start();
一种是class实现Runnable接口,重写run方法,然后新建Thread类
class MyTask implements Runnable{
@override
public void run(){
/*我们自己的逻辑*/
}
}
MyTask task = new MyTask;
new MyThread(task).start();
线程状态
底层都是一个变量表示
private volatile int threadStatus = 0;
→ NEW
还没启动的线程处于这种状态
→ RUNNABLE
在Java虚拟机中执行的线程处于这种状态。
→ BLOCKED
线程被阻塞等待锁释放,进入synchronized代码块/synchronized修饰的方法 或者 重新synchronized代码块/synchronized修饰的方法后,调用Object#wait()
进入此状态。
→ WAITING,无限期地等待另一个线程执行特定操作的线程处于此状态。
- Object#wait()不指定超时时间
- Thread#join()不指定超时时间
- LockSupport#park()
→ TIMED_WAITING
指定的时间内等待另一个线程执行特定操作的线程处于此状态。
- Thread#sleep()
- Object#wait()指定超时时间
- Thread#join()指定超时时间
- LockSupport#parkNanos
- LockSupport#parkUntil
→ TERMINATED
执行完毕退出的线程处于这个状态。
一个线程在一个时间点只能有一个状态,状态仅仅是虚拟机定义的,跟操作系统无关。
上述可知:
-
在 Java 调用 start() 后,操作系统中才真正出现了一个线程,并且立刻运行。
-
Java中的线程,和操作系统内核中的线程,是一对一的关系。
-
调用 start 后,线程状态变为 RUNNABLE,这是由 native 方法里的某部分代码造成的。
线程名称
没有指定默认就是「Thread-N」
public Thread() {
init(null, null, "Thread-" +nextThreadNum(), 0);
}
线程组的概念(不是很常用)
每个线程都可以属于一个线程组,如果我们没有指定线程组,默认使用的是当前执行线程的线程组,比如main线程的线程组。
if (g == null) {
g = parent.getThreadGroup();
}
daemon thread(守护线程)
父线程就是当前执行创建线程的线程。
如果我们不指定一个线程是否是守护线程,就会用父线程的isDaemon状态。默认情况下,父线程是守护线程,子线程也是,反之亦然。
如果一个线程是守护线程,在主线程退出后,守护线程也会自动退出。如果是非守护线程,则主线程退出后,非守护线程不会退出,而是等到所有逻辑执行完后退出
this.daemon = parent.isDaemon();
Thread ID
每个线程还有一个线程ID的概念,就是用来标识的
/* Set thread ID */
tid = nextThreadID();
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
}
start方法
- 当我们调用start方法的时候,jvm就会默认调用我们实现的run方法。
- 不能多次调用一个线程的start方法,否则抛出
IllegalThreadStateException
异常
sleep方法
- 让当前线程睡眠,睡眠时间我们可以自己指定毫秒数。
- sleep线程睡眠之后,并不会丢失拥有的锁。
public static native void sleep(long millis) throws InterruptedException;
JDK1.5之后引入了TimeUnit这个类,我们可以很方便的去指定要睡眠的时间。for example:
TimeUnit.HOURS.sleep(1); //1小时
TimeUnit.MINUTES.sleep(1); //1分钟
TimeUnit.SECONDS.sleep(1);//1秒
TimeUnit.MILLISECONDS.sleep(1);//1毫秒
yield方法
提示当前线程是否愿意放弃当前使用的处理器,一般是用于测试和调试的方法。
join方法
main线程创建子线程,然后调用子线程的join方法,就会阻塞住main线程,直到子线程执行完毕。当然看代码我们也可以指定阻塞多少毫秒,等你多久就不等了!
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");
}
//下面其实就是阻塞的关键代码,使用了Object.wait()方法
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
//millis其实就是等待的时间,最多等待多少秒
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
interrupted方法
case1
有一个标志位,isInterrupted
,判断线程是否中断,调用interrupt方法后,就会返回true,可以停止执行一些动作。
interrupt
方法是和isInterrupted
这个标志位来配合使用的,并不会真的打断线程的运行。
Thread subThread = new Thread(()->{
try {
while (!Thread.currentThread().isInterrupted()){
System.out.println("子线程执行....");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
subThread.start();
Thread.sleep(2000);
System.out.println("中断子线程的执行.....");
//打断子线程的执行后,isInterrupted的标志位就会变成true,
//interrupt
subThread.interrupt();
System.out.println(subThread.isInterrupted());
/*
output
------------------
子线程执行....
子线程执行....
子线程执行....
子线程执行....
中断子线程的执行.....
true
//
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.mazai.concurrent.Demo2.lambda$main$0(Demo2.java:13)
at java.lang.Thread.run(Thread.java:748)
*/
case2
同时interrupt方法也可以打断线程的休眠,让线程从休眠中醒过来。
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
myThread.interrupt();
}
private static class MyThread extends Thread{
private boolean isRun = true;
@Override
public void run(){
while (isRun){
try {
System.out.println("睡5s......");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
isRun = false;
}
}
}
}
/*
output
-----------------------
睡5s......
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.mazai.concurrent.Demo2$MyThread.run(Demo2.java:21)
*/