使用标准类库 java.lang.Thread 实现线程
Thread 类或其子类就是一个线程。Thread 两个常用的构造器是: Thread() 和 Thread(Runnable target) 。相应的,java创建线程也有两种方式:
1、定义 Thread 类的子类,在该子类中 Override run方法并在方法中实现线程任务处理逻辑;
public class WecomeApp { public static void main(String[] args) { Thread wecomeThread = new WecomeThread(); wecomeThread.start(); System.out.println("1 i am " + Thread.currentThread().getName()); } } public class WecomeThread extends Thread{ @Override public void run() { System.out.println("i am "+ Thread.currentThread().getName()); } } 1 i am main i am Thread-02、创建一个 java.lang.Runnable 接口实例,并在该实例的 run 方法中实现任务处理逻辑
public class WecomeApp { public static void main(String[] args) { Thread wecomeThread = new Thread(new WecomeThread2()); wecomeThread.start(); System.out.println("i am " + Thread.currentThread().getName()); } } public class WecomeThread2 implements Runnable{ @Override public void run() { System.out.println("2 i am " + Thread.currentThread().getName()); } } i am main 2 i am Thread-0使用Callable和Future实现线程
创建Callable 接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并有返回值。创建 Callable 实现类的实例, 使用 FutureTask 类来包装 Callable 对象, 该 FutureTask对象封装了该 Callable对象的 call() 方法的返回值。使用 FutureTask 对象作为 Thread 对象的target创建线程并启动新线程。调用 FutureTask 对象的get()方法来获得子线程执行结束后的返回值。如下代码所示:
public class BuildThreads { public static void main(String[] args) { CallableThread callableThread = new CallableThread(); FutureTask<Integer> futureTask = new FutureTask<>(callableThread); Thread thread = new Thread(futureTask, "有返回值的线程"); thread.start(); try { System.out.println("子线程的返回值:"+ futureTask.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } public class CallableThread implements Callable { @Override public Integer call() throws Exception { int i = 0; for ( ; i < 5; i++){ System.out.println(Thread.currentThread().getName() + " " + i); } return i; } }输出为:
有返回值的线程 0 有返回值的线程 1 有返回值的线程 2 有返回值的线程 3 有返回值的线程 4 子线程的返回值:5创建线程三种方式对比:
从面向对象角度来看:使用实现 Runnable、Callable 接口的方式创建线程只是实现了Runnable接口或者Callable接口,还可以继承其他类。在这种方式下,多个线程可以共享一个target对象,所以非常适合多个线程来处理同一份资源的情况, 从而可以将CPU、代码和数据分开,形成清晰的模型,较好的体现了面向对象的思想。 而线程类已经继承了其它类,所以不能继承其它父类。
使用接口方法编程比较复杂,如果要访问当前线程,则必须使用 Thread.currentThread()方法。而使用继承Thread 类的方式创建多线程的优势是编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。
使用Callable接口方法可以声明抛出异常。
由上代码可知,线程的执行过程大致为 : 创建线程 >>> 启动线程 >>> 执行线程 。Thread 类 使用 start()方法启动相应的线程(启动线程的实质是请求java虚拟机运行相应的线程,而这个线程何时能够运行由线程调度器决定)。另外 run方法是线程任务处理逻辑的入口方法,它由java 虚拟机在运行线程时直接调用。不管采用哪种方式创建线程,一旦run方法执行结束,相应的线程运行也就结束了。执行流程如下如所示:
new : 一个已经创建而未启动的线程处于该状态。由于一个线程只能够被启动一次,因此一个线程只可能有一次处于该状态。
RUNNABLE : 该状态可以被看作一个符合状态。它包括两个子状态:READY和RUNNING。前者该线程可以被线程调度器进行调度而使之处于RUNNING状态。后者表示处于该状态的线程正在运行,即相应线程对象的run方法所对应的指令正在由处理器执行。 执行Thread.yield()的线程,其状态可能会由RUNNING转换为READY。
BLOCKED : 一个线程发起一个阻塞式 I/O 操作后,或者申请一个由其它线程持有的独占资源时,相应的线程会处于该状态。处于BLOCK状态的线程并不会占用处理器资源。
WAITING : 一个线程执行了某些特定的方法之后就会处于这种等待其它线程执行另外一些特定操作的状态(即等待其它线程显示的唤醒)。
进入waiting方法退出waiting方法Object.wait()无参数Object.notify()/notifyAll()Thread.join()无参数被调用的线程执行完毕LockSupport.park() 方法LockSupport.unpark(Thread)TIMED_WAITING : 该状态和WAITING类似,差别在于处于该状态的线程并非无限制的等待其它线程执行特定操作,而是处于带有时间的等待状态。 即无需等待其它线程显式地唤醒,在一定时间之后会被系统自动唤醒。
TERMINATED : 已经执行结束的线程处于该状态。
