基本概念
Java语言是支持多线程的,一个正在运行的Java程序可以称之为一个进程(process),在每个进程里面包含多个线程,线程是进程中单一的顺序控制流,CPU在执行计算机指令的时候都是按顺序执行,但是由于其执行速度很快,可以把时间分成很细小的时间片,交替执行,线程和进程的区别在于
- 创建进程的开销大于创建线程的开销,进程之间的通信比线程间要难
- 线程不能独立存在,依托于进程而存在,线程也可以看作轻量级的进程
- 多进程的稳定性高于多线程,一个进程的运行不会影响其他进程,但线程崩溃往往会引起程序的崩溃
在实际开发中运用好多线程可以大大提高效率,可以类比火车站卖票的时候,把买票这个功能理解为一个进程,每个开放的窗口就是进程中的线程,当购票人数突增的时候,增加开放的窗口可以提高卖票的效率,在卖票的过程中,各个窗口互不影响但是,他们共享着同一份资源,票的库存,使用多线程来操作,就要考虑的多线程情况下引起的高并发,这就涉及到锁的概念,Java中涉及并发的包和工具类在java.util.concurret
包里面。
生命周期
Java中线程创建后有以下几种状态,在Thread类中有一个枚举类State
声明
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
- NEW:新创建的线程,尚未执行,没有调用
start()
方法 - RUNNABLE:运行中的线程,正在执行
run()
方法的Java代码; - BLOCKED:运行中的线程,因为某些操作被阻塞而挂起;
- WAITING:运行中的线程,因为某些操作在等待中,如
wait()
方法; - TIMED_WAITING:运行中的线程,因为执行
sleep()
方法正在计时等待; - TERMINATED:线程已终止,因为
run()
方法执行完毕。
线程的生命周期可以由下图表示
创建线程
Java中如何创建一个线程Thread,提供如下方法
- 继承Thread类,重写run方法
public class Test extends Thread{
@Override
public void run() {
System.out.println("Thread is Created");
}
public static void main(String[] args) {
Test test = new Test();
Thread thread = new Thread(test);
thread.start();
System.out.println(Thread.currentThread().getName());
}
}
输出结果
/*新建线程先执行*/
Thread is Created
main
/*主线程先执行*/
main
Thread is Created
- 实现Runnable接口,重写run方法
public class Test implements Runnable{
@Override
public void run() {
System.out.println("Thread is Created");
}
public static void main(String[] args) {
Test test = new Test();
Thread thread = new Thread(test);
thread.start();
System.out.println(Thread.currentThread().getName());
}
}
输出结果
/*新建线程先执行*/
Thread is Created
main
/*主线程先执行*/
main
Thread is Created
Thread类和Runnable接口都位于java.lang.Thread
包下,Thread类本身实现了Runnable接口,Runnable接口里面只定义了一个run方法
/**Java8新增,该注解表明Runnable函数式接口,可使用lambda表达式创建对象*/
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
严格来讲,参考Oracle官方文档,创建一个Thread只有这两种方式,无论实现Runable接口,还是继承Thread类,都存在一些缺陷,我们无法获得线程的执行结果,无法处理执行过程的异常,这里提供另外一种创建线程的方式。
- 实现Callable接口
Callable是JDK 1.5新增的接口,位于java.util.concurrent
包下,这个包是Java并发编程包,Callable接口里面定义了call方法,call方法是run方法的增强版,可以通过实现Callable接口时传入泛型来指定call方法的返回值,并且可以声明抛出异常。
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
Java启动一个线程需要调用Thread的start
方法,这里看一下在JDK1.8中Thread类的构造方法
/**分配一个新的Thread对象。*/
Thread()
/**分配一个新的Thread对象。*/
Thread(Runnable target)
/**分配一个新的Thread对象。*/
Thread(Runnable target, String name)
/**分配一个新的Thread对象。*/
Thread(String name)*/
/**分配一个新的Thread对象。*/
Thread(ThreadGroup group, Runnable target)
/**分配一个新的Thread对象,使其具有target作为其运行对象,具有指定的name作为其名称,属于group引用的线程组。*/
Thread(ThreadGroup group, Runnable target, String name)
/**分配一个新的Thread对象,以便它具有 target作为其运行对象,将指定的name正如其名,以及属于该线程组由称作group ,并具有指定的堆栈大小。*/
Thread(ThreadGroup group, Runnable target, String name, long stackSize)
/**分配一个新的 Thread对象。*/
Thread(ThreadGroup group, String name)
无论哪一种构造方法都没有Callable类型的target,只能传入Runnable类型的target,但是Callable和Runnable是位于不同包下面的接口,没有直接关系,那如何把Thread和Callable联系起来,这里就要引入Future
接口和FutureTask
实现类。
Future接口定义如下方法
/**尝试取消执行此任务。*/
boolean cancel(boolean mayInterruptIfRunning)
/**等待计算完成,然后检索其结果。*/
V get()
/**如果需要等待最多在给定的时间计算完成,然后检索其结果(如果可用)。*/
V get(long timeout, TimeUnit unit)
/**如果此任务在正常完成之前被取消,则返回 true 。*/
boolean isCancelled()
/**返回 true如果任务已完成。*/
boolean isDone()
查看源码,RunnableFuture作为一个过渡同时继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
查看FutureTask的继承关系
再查看FutureTask的构造方法
/**创建一个FutureTask ,它将在运行时执行给定的Callable 。*/
FutureTask(Callable<V> callable)
/**创建一个 FutureTask ,将在运行时执行给定的Runnable ,并安排get将在成功完成后返回给定的结果。*/
FutureTask(Runnable runnable, V result)
再回到最初的问题如何将实现了Callable接口的线程类作为Thread实例的target,这里经过了以下过程
- 创建线程类实现Callable接口,重写call方法
- 创建FutureTask实例,将实现Callable接口的线程类实例化对象作为FutureTask的target
- 创建Thread类,将FutureTask实例化对象作为Thread的target
Future接口和FutureTask类在中间做了一层包装,代码展示如下
public class Test implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("Thread is Created");
return "OK";
}
public static void main(String[] args) throws Exception {
Test test = new Test();
FutureTask futureTask = new FutureTask(test);
Thread thread = new Thread(futureTask);
thread.start();
String str = (String) futureTask.get(5,TimeUnit.SECONDS);
System.out.println(str);
System.out.println(Thread.currentThread().getName());
}
}
输出结果
Thread is Created
OK
main
这里总结一下三种创建线程的方法特点
-
继承Thread类实现多线程:
- 实现起来简单,而且要获取当前线程,无需调用Thread.currentThread()方法,直接使用this即可获取当前线程
- 线程类已经继承Thread类了,就不能再继承其他类
- 多个线程不能共享同一份资源
-
通过实现Runnable接口或者Callable接口实现多线程:
- 线程类只是实现了接口,还可以继承其他类
- 多个线程可以使用同一个target对象,适合多个线程处理同一份资源的情况
- 通过这种方式实现多线程,相较于第一类方式,编程较复杂
- 要访问当前线程,必须调用Thread.currentThread()方法
一般推荐使用以实现接口的方式创建线程类。
Thread API
-
static int activeCount()
返回当前线程的thread group及其子组中活动线程数的估计。 -
void checkAccess()
确定当前正在运行的线程是否有权限修改此线程。 -
protected Object clone()
将CloneNotSupportedException作为线程抛出无法有意义地克隆。 -
static Thread currentThread()
返回对当前正在执行的线程对象的引用。 -
static void dumpStack()
将当前线程的堆栈跟踪打印到标准错误流。 -
static int enumerate(Thread[] tarray)
将当前线程的线程组及其子组中的每个活动线程复制到指定的数组中。 -
static Map<Thread,StackTraceElement[]> getAllStackTraces()
返回所有活动线程的堆栈跟踪图。 -
ClassLoader getContextClassLoader()
返回此Thread的上下文ClassLoader。 -
static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler()
返回当线程由于未捕获异常突然终止而调用的默认处理程序。 -
long getId()
返回此线程的标识符。 -
String getName()
返回此线程的名称。 -
int getPriority()
返回此线程的优先级。 -
StackTraceElement[] getStackTrace()
返回表示此线程的堆栈转储的堆栈跟踪元素数组。 -
Thread.State getState()
返回此线程的状态。 -
ThreadGroup getThreadGroup()
返回此线程所属的线程组。 -
Thread.UncaughtExceptionHandler getUncaughtExceptionHandler()
返回由于未捕获的异常,此线程突然终止时调用的处理程序。 -
static boolean holdsLock(Object obj)
返回 true当且仅当当前线程在指定的对象上保持监视器锁。 -
void interrupt()
中断这个线程。 -
static boolean interrupted()
测试当前线程是否中断。 -
boolean isAlive()
测试这个线程是否活着。 -
boolean isDaemon()
测试这个线程是否是守护线程。 -
boolean isInterrupted()
测试这个线程是否被中断。 -
void join()
等待这个线程死亡。 -
void join(long millis)
等待这个线程死亡最多 millis毫秒。 -
void join(long millis, int nanos)
等待最多 millis毫秒加上 nanos纳秒这个线程死亡。 -
void run()
如果这个线程使用单独的Runnable运行对象构造,则调用该Runnable对象的run方法; 否则,此方法不执行任何操作并返回。 -
void setContextClassLoader(ClassLoader cl)
设置此线程的上下文ClassLoader。 -
void setDaemon(boolean on)
将此线程标记为 daemon线程或用户线程。 -
static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
设置当线程由于未捕获的异常突然终止而调用的默认处理程序,并且没有为该线程定义其他处理程序。 -
void setName(String name)
将此线程的名称更改为等于参数 name 。 -
void setPriority(int newPriority)
更改此线程的优先级。 -
void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
设置当该线程由于未捕获的异常而突然终止时调用的处理程序。 -
static void sleep(long millis)
使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。 -
static void sleep(long millis, int nanos)
导致正在执行的线程以指定的毫秒数加上指定的纳秒数来暂停(临时停止执行),这取决于系统定时器和调度器的精度和准确性。 -
void start()
导致此线程开始执行; Java虚拟机调用此线程的run方法。 -
String toString()
返回此线程的字符串表示,包括线程的名称,优先级和线程组。 -
static void yield()
对调度程序的一个暗示,即当前线程愿意产生当前使用的处理器。
应用案例
-
NEW
public class Test{ public static void main(String[] args) throws Exception{ System.out.println("Thread State is:"+new Thread().getState()); } }
输出结果
Thread State is:NEW
-
RUNNABLE
public class Test implements Runnable{ @Override public void run() { System.out.println("Thread State is:"+Thread.currentThread().getState()); } public static void main(String[] args) throws Exception{ Test test = new Test(); Thread thread = new Thread(test); thread.start(); } }
输出结果
Thread State is:RUNNABLE
-
BLOCKED
- 创建线程T1,T2调用
start()
方法,T1,T2状态均为RUNNABLE
- 若T1获得锁,T1状态为
RUNNABLE
,T2状态变为BLOCKED
- 等待T1执行完释放锁,T2获得锁,T2状态
RUNNABLE
class BlockThread extends Thread { private String name; //当前线程名称 private Object lock; //锁 public BlockThread(Object lock,String name){ this.lock = lock; this.name = name; } @Override public void run() { System.out.println("Thread "+name+" State is "+Thread.currentThread().getState()); synchronized (lock) { System.out.println("Thread "+name+" hold the lock"); try { System.out.println("Thread "+name+" State is "+Thread.currentThread().getState()); Thread.sleep(1000 * 10); //抢到锁的线程执行逻辑,这里用睡眠模拟 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread " + name + " release the lock"); } } } public class Test{ public static void main(String[] args) throws Exception{ Object lock = new Object();//锁 BlockThread t1 = new BlockThread(lock,"T1"); BlockThread t2 = new BlockThread(lock,"T2"); t1.start(); //线程 T1开始运行 t2.start(); //线程 T2开始运行 Thread.sleep(100); //阻塞主线程,等待T1,T2抢锁 System.out.println("Thread T1 State is " + t1.getState()); //获取T1线程状态 System.out.println("Thread T2 State is " + t2.getState()); //获取T2线程状态 } }
输出结果
Thread T1 State is RUNNABLE Thread T1 hold the lock Thread T1 State is RUNNABLE Thread T2 State is RUNNABLE Thread T1 State is TIMED_WAITING Thread T2 State is BLOCKED Thread T1 release the lock Thread T2 hold the lock Thread T2 State is RUNNABLE Thread T2 release the lock
- 创建线程T1,T2调用
-
WAITING
class WaitingThread extends Thread { private Object lock; public WaitingThread(String name, Object lock) { super(name); this.lock = lock; } @Override public void run() { System.out.println("Thread " + Thread.currentThread().getName()+" try to wait"); synchronized (lock) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Test{ public static void main(String[] args) throws Exception { Object lock = new Object(); WaitingThread t = new WaitingThread("T", lock); t.start(); Thread.sleep(1000); System.out.println("Thread T State is " + t.getState()); System.out.println("Thread "+Thread.currentThread().getName()+" State is " + Thread.currentThread().getState()); } }
输出结果
Thread T try to wait Thread T State is WAITING Thread main State is RUNNABLE
-
TIMED_WAITING
线程调用
sleep()
方法,进入TIMED_WAITING状态class WaitingThread extends Thread { private Object lock; public WaitingThread(String name, Object lock) { super(name); this.lock = lock; } @Override public void run() { synchronized (lock) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Test{ public static void main(String[] args) throws Exception { Object lock = new Object(); WaitingThread t1 = new WaitingThread("T1", lock); WaitingThread t2 = new WaitingThread("T2", lock); t1.start(); t2.start(); Thread.sleep(1000); System.out.println("Thread T1 State is " + t1.getState()); System.out.println("Thread T2 State is " + t2.getState()); } }
输出结果
Thread T1 State is TERMINATED Thread T2 State is TIMED_WAITING
-
TERMINATED
public class Test implements Runnable{ @Override public void run() { } public static void main(String[] args) throws Exception{ Test test = new Test(); Thread thread = new Thread(test); thread.start(); System.out.println("Thread State is "+thread.getState()); } }
输出结果
Thread State is TERMINATED