Java Thread基础知识
java程序中只有一种途径去启动一个线程,即调用Thread类的start()方法。
java中的线程状态
在Thread类中有一个内部的枚举类State描述了线程的各个状态。
public enum State {
/**
* 线程创建但未启动时会处于此状态
*/
NEW,
/**
* 线程调用start方法,java虚拟机会启动一个线程,被认为是RUNNABLE状态,
* 但此时可能在等待操作系统资源,比如等待cpu的处理器执行,
* 所以可能并没有在操作系统中真正获取到处理器的执行权。
*/
RUNNABLE,
/**
* 获取监视器失败后会处于此状态,比如调用了synchronized方法没有获取到锁时。
*/
BLOCKED,
/**
* 线程在等待另一个线程执行特定操作时,会处于此状态,比如调用了Object.wait(),
* 则会等待另一个线程调用Object.notify()或Object.notifyAll()方法。
* 或者调用thread.join()等待被调用线程的执行结束。
* 调用以下方法会使线程处于此状态
* Object.wait()
*Thread.join()
* LockSupport.park()
* 注意:以上方法都是没有设置设置超时时间
*/
WAITING,
/**
* 相较于WAITING状态,当设置了指定时间时,等待一定时间继续执行,线程处于此状态
* 调用以下方法会使线程处于此状态
* Object.wait( timeout )
* Thread.sleep( timeout )
*Thread.join( timeout )
* LockSupport.parkNanos
*LockSupport.parkUntil
*/
TIMED_WAITING,
/**
* 终止状态,线程执行完成。
*/
TERMINATED;
}
线程常用方法
start
调用start()会开始执行一个线程,此时java虚拟机启动一个线程并调用该类的run()方法。
sleep
使当前线程休眠,放弃cpu使用权则或者放弃竞争cpu使用权,则放弃。
注意:此时如果线程持有锁,不会释放。
join
此方法为线程的实例方法。A线程调用B线程的join方法,A线程将处于等待状态,直到B线程执行完后A线程才继续执行。
interrupt
中断线程。将线程从当前的一个状态中释放出来。当调用该方法时会将线程interrupt状态设置为true。比例调用了线程的 wait、join、sleep时,调用interrupt会中断线程的等待状态,继续执行,此时以上方法会抛出InterruptedException异常。或者线程阻塞于IO操作时,调用该方法会将线程中断,关闭IO操作流。
Object的wait和notify
wait
此方法为Object的方法。调用此方法的前提为必须持有当前obj的锁,如果没有获取到锁而调用该方法会抛出java.lang.IllegalMonitorStateException。将当前运行的线程进入等待状态。并且释放obj的锁。
大多场景为当线程执行到特定逻辑时需要某些条件,当条件不满足时,调用wait方法进行等待。比如生产者/消费者模式,消费者需要等待生产者的数据准备好后才能执行。
notify/notifyAll
此方法为Object的方法。唤醒等待该obj的锁的线程,调用此方法的前提为必须持有当前obj的锁,如果没有获取到锁而调用该方法会抛出java.lang.IllegalMonitorStateException。此时被唤醒的线程需要重新获取该obj的锁才能运行。notify方法会从等待obj的多个线程中选择一个唤醒,notifyAll会唤醒所有的等待线程。
Thread与Runnbale的关系
开发线程类的两种方式
继承Thread,重写run方法
public class TestThread extends Thread {
public static void main(String[] args) {
new TestThread().start();
}
@Override
public void run() {
System.out.println("线程启动");
}
}
实现Runnable接口,调用new Thread(target).start();
public class TestRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程启动");
}
public static void main(String[] args) {
TestRunnable target = new TestRunnable();
new Thread(target).start();
}
}
启动线程的方式
上面代码是我们开发线程常用的两种方式,之所以说是开发不是启动,是因为java中启动线程只能通过Thread类的start方法。
public class Thread implements Runnable {
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) {
}
}
}
private native void start0();
}
通过源码可以看到 start()调用了start0()方法,start0()是真正让java虚拟机去启动一个线程,此方法为虚拟机内部方法,虚拟机启动线程时会调用run()去执行具体的代码逻辑。所以结论为start方法为让虚拟机真正启动一个线程,而线程的执行逻辑则为run方法。
如有不实,还望指正