Java线程基础知识

Java线程基础

如下图所示,Java中线程可分为NEW,RUNABLE,RUNING,BLOCKED,WAITING,TIMED_WAITING,TERMINATED 共七个状态,一个状态是如何过渡到另一个状态图中标识的很清楚。

img

  • 初始状态(NEW)
    实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态。

  • 就绪状态(RUNNABLE)
    就绪状态只是说你资格运行,调度程序没有挑选到你,你就永远是就绪状态。

    • 调用线程的start()方法,此线程进入就绪状态。
    • 当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态。
    • 当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。
    • 锁池里的线程拿到对象锁后,进入就绪状态。
  • 运行中状态(RUNNING)
    线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。

  • 阻塞状态(BLOCKED)
    阻塞状态是线程阻塞在进入synchronized关键字(当然也包括ReentrantLock)修饰的方法或代码块(获取锁)时的状态。

  • 等待(WAITING)
    处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。

  • 超时等待(TIMED_WAITING)
    处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。

  • 终止状态(TERMINATED)
    当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它终止了。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦终止了,就不能复生。在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。

Java线程常用API

img

其中常用的方法为start(),yield(),sleep(),interrupt(),interrupted(),isInterrupted(),isAlive(),join(),

setDaemon(),setName(),setPriority(),其中stop方法和destroy方法,都是被废弃的方法在日常使用中不建议用。除了Thread类下的API,Object类下的wait(),notify(),notifyAll(),这三个方法也经常在多线程场景中出现。本文的目的,主要讲解的就是这些方法的使用和内部原理。

方法 方法说明 Exception
Thread.start 开启执行线程,由虚拟机负责调用run方法。(Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread.) IllegalThreadStateException if the thread was already started.
Thread.yield 让出CPU,但是仅仅对同线程级别(Yield is a heuristic attempt to improve relative progression between threads that would otherwise over-utilise a CPU)
Thread.sleep 使得正在执行的当前线程睡眠。敲黑板!但是不会让出任何锁的所有权( The thread does not lose ownership of any monitors.)这个特性很重要,也就是说在同步块中使用Thread.sleep要谨慎。 当线程中断会抛出InterruptedException异常,并同时清空中断标志位
Thread.interrupt 中断线程,但只是设置了中断标志位,此刻调用isInterrupted返回true。例子请参考下面的示例代码testInterrupt0方法。只会打印到0-9循环跳出 SecurityException if the current thread cannot modify this thread 请教这个异常什么时候会发生呢?
Thread.isInterrupted 查看线程是否处于中断状态.true为中断。调用之后不清除中断标志位。
Thread.interrupted 查看线程是否处于中断状态.true为中断。调用之后清除中断标志位。心细的同学已经发现和isInterrupted的区别了吧。
Thread.isAlive 线程是否存活,A thread is alive if it has been started and has not yet died.
Thread.join 等待线程死亡之后再执行。(Waits for this thread to die) 当线程中断会抛出InterruptedException异常,并同时清空中断标志位
Thread.setDaemon 设置为守护线程。任何非守护线程还在运行,守护线程就不会终止,最典型的守护线程是垃圾回收器的回收线程。 IllegalThreadStateException 当线程状态是alive的时候不能调用setDaemon
Thread.setName 设置线程的name
Thread.setPriority 设置线程的优先级。MIN_PRIORITY为1,MAX_PRIORITY为10,NORM_PRIORITY为5。
Object.wait 如果不指定timeout,则一直阻塞直到其他线程调用notify或者notifyAll。敲黑板!调用wait之后当前线程不在持有对象锁。
Object.notify 随机唤醒同一个对象monitor下的某个线程
Object.notifyAll 唤醒同一个对象monitor下的所有线程。查看testNotify示例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static void testInterrupt0()  Exception {
int i = 0;
while (!Thread.currentThread().isInterrupted()) {
System.out.println("loop" + i++);
if(i == 10) {
Thread.currentThread().interrupt();
}
}
//echo true
System.out.println(Thread.currentThread().isInterrupted());
//echo true
System.out.println(Thread.currentThread().interrupted());
//echo false
System.out.println(Thread.currentThread().isInterrupted());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class MyThread extends Thread {
private Object lock;
private String name;

public MyThread(Object lock, String name) {
this.lock = lock;
this.name = name;
}

@Override
public void run() {
synchronized (lock) {
try {
System.out.println(name + " get lock,interrupt =" + Thread.currentThread().isInterrupted());

lock.wait();

//Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println(name + " is interrupt. notify all interrupt =" + Thread.currentThread().isInterrupted());
lock.notifyAll();
}
System.out.println(name + ":notified...");
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static void testNotify() throws Exception {
MyThread t1 = new MyThread(lock, "t1");
MyThread t2 = new MyThread(lock, "t2");

Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);

thread1.start();
thread2.start();

Thread.sleep(1000);
long startTime = System.currentTimeMillis();
synchronized (lock) {
System.out.println("main get lock");
lock.notifyAll();
}

thread1.join();
thread2.join();
long endTime = System.currentTimeMillis();

System.out.println("notify lock.time =" + (endTime - startTime));
}

testNotify执行结果

t1 get lock,interrupt =false
t2 get lock,interrupt =false
main get lock
t2:notified…
t1:notified…