一、Linux中线程的状态
Linux中线程状态一共有5种:
- 初始状态(New):对应 Java中的 NEW 状态
- 可运行状态(Ready):对应 Java中的 RUNNBALE 状态
- 运行状态(Running):对应 Java中的 RUNNBALE 状态
- 等待状态(Waiting):该状态在 Java中被划分为了 BLOCKED、WAITING、TIMED_WAITING 三种状态
- 终止状态 (Terminated):对应 Java中的 TERMINATED 状态
二、java中线程的状态
Java中线程的状态一共有6种(生命周期)对应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(新创建):新创建了一个线程对象,但还没有调用Thread的start()方法
- Runnable(可运行):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象创建后,其它线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)
- Blocked(被阻塞):从箭头的流转方向可以看出,从 Runnable 状态进入 Blocked 状态只有一种可能,就是进入 synchronized 保护的代码时没有抢到 monitor 锁,无论是进入 synchronized 代码块,还是 synchronized 方法,都是一样。当处于 Blocked 的线程抢到 monitor 锁,就会从 Blocked 状态回到Runnable 状态。
- Waiting(等待):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)
- Timed Waiting(计时等待):该状态不同于WAITING,它可以在指定的时间后自行返回
- Terminated(被终止):表示该线程已经执行完毕。线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常
- 如果想要确定线程当前的状态,可以通过 getState() 方法,并且线程在任何时刻只可能处于 1 种状态。
三、java线的实现方式
3.1、从编程的角度上来说,在java中线程的实现方式有三种
- 继承 Thread 类
- 实现 Runnable 接口
- 实现 Callable 接口
但是实质上都是 new Thread(…).start(); 最终还是由Thread类来实现线程。
3.2、继承自Thread类,步骤如下
- 创建 MyThread 类,让其继承 Thread 类并重写 run() 方法。
- 创建 MyThread 类的实例对象,即创建一个新线程。
- 调用 start() 方法,启动线程。
代码示例:
public class MyThread extends Thread{
@Override
public void run() {
System.out.println(" 继承自 Thread 类 创建的线程,ThreadName:"+Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread myThread1 = new MyThread();
myThread1.setName("Thread-1");
System.out.println(myThread1.getState());
myThread1.start();
MyThread myThread2 = new MyThread();
myThread2.setName("Thread-2");
System.out.println(myThread2.getState());
myThread2.start();
MyThread myThread3 = new MyThread();
myThread3.setName("Thread-3");
System.out.println(myThread3.getState());
myThread3.start();
System.out.println(myThread1.getName()+" state:"+myThread1.getState());
System.out.println(myThread2.getName()+" state:"+myThread2.getState());
System.out.println(myThread3.getName()+" state:"+myThread3.getState());
}
}
运行结果:
从结果上可以看到,线程执行的顺序是随机的,只要不改变最终的结果,执行的顺序可以进行调整的。
使用了Thread类中的方法
1、set/getName() 为新线程设置名字。
2、getState() 获取当前线程的运行状态。
3、start() 开启线程运行。
3.3 实现 Runnable() 接口
步骤如下:
- 定义一个类RunableTask实现Runnable接口;
- 重写run()方法;
- 创建RunableTask对象;
- 创建Thread类的对象,将RunableTask对象作为构造方法的参数;
- 启动线程。
代码示例:
public class RunableTask implements Runnable {
@Override
public void run() {
System.out.println(" 实现 Runnable接口 创建的线程,ThreadName:" + Thread.currentThread().getName()
+ "线程状态 state:" + Thread.currentThread().getState() +
" 线程的上下文类加载器: "+Thread.currentThread().getContextClassLoader()
+" 线程 Id :" + Thread.currentThread().getId() +
"线程所属分组:" + Thread.currentThread().getThreadGroup() +
"线程的优先级:" +Thread.currentThread().getPriority()
);
}
public static void main(String[] args) {
RunableTask runableTask = new RunableTask();
Thread thread1 = new Thread(runableTask);
Thread thread2 = new Thread(runableTask);
Thread thread3 = new Thread(runableTask);
thread1.start();
thread2.start();
thread3.start();
System.out.println(" state :"+thread1.getState());
System.out.println(" state :"+thread2.getState());
System.out.println(" state :"+thread3.getState());
}
}
在通过调用Thread.currentThread()方法,打印出线程相关的信息
state :RUNNABLE
实现 Runnable接口 创建的线程,ThreadName:Thread-2线程状态 state:RUNNABLE 线程的上下文类加载器: sun.misc.Launcher$AppClassLoader@b4aac2 线程 Id :11线程所属分组:java.lang.ThreadGroup[name=main,maxpri=10]线程的优先级:5
实现 Runnable接口 创建的线程,ThreadName:Thread-1线程状态 state:RUNNABLE 线程的上下文类加载器: sun.misc.Launcher$AppClassLoader@b4aac2 线程 Id :10线程所属分组:java.lang.ThreadGroup[name=main,maxpri=10]线程的优先级:5
实现 Runnable接口 创建的线程,ThreadName:Thread-0线程状态 state:RUNNABLE 线程的上下文类加载器: sun.misc.Launcher$AppClassLoader@b4aac2 线程 Id :9线程所属分组:java.lang.ThreadGroup[name=main,maxpri=10]线程的优先级:5
state :BLOCKED
state :TERMINATED
在不指定线程名字和优先级的情况下,默认的线程名字为Thread- + 线程id,id从1开始自增。同时分配给线程默认分组 为main已经分组的优先级为10,线程的优先级统一为 5
实现Runnable接口这种方式出现的目的,是因为继承Thread类的方式具有一定的局限性。
理由:
- 单继承的局限性,继承了Thread类,就无法继承其他类了,限制了可扩展性。
- 从代码架构上考虑,具体执行任务应该(run方法)和线程解耦的。
- 从性能损耗上来讲,如果每次任务都新建一个独立的线程,这种方式对性能开销比较大。因为线程,创建,执行和销毁都需要占用资源。
3.4 通过实现Callable接口实现线程
Callable接口中只有一个 call() 方法,该方法是抛出异常的,源码如下:
package java.util.concurrent;
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
说明:
- Callable能返回任务线程执行结果,而Runable不能返回
- Callable的call方法允许抛出异常,而Runable异常只能在run方法内部消化
Callable需要和FutureTask搭配使用。
通过实现 Callable 接口实现多线程的步骤如下:
- 创建 CallableTask 类实现 Callable 接口。
- 创建 CallableTask 类的实例对象 callableTask 。
- 把实例对象 callableTask 作为参数来创建 FutureTask 类的实例对象 futureTask。
- 把实例对象 futureTask 作为参数来创建 Thread 类的实例对象 thread,实例对象 thread 就是一个新线程。
- 调用 start() 方法,启动线程。
- 调用FutureTask类对象的get()方法来获取线程执行的返回值,即任务类对象中call()方法的返回值。
代码示例如下:
package com.copy.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println(" 实现 Runnable接口 创建的线程,ThreadName:" + Thread.currentThread().getName()
+ "线程状态 state:" + Thread.currentThread().getState() +
" 线程的上下文类加载器: "+Thread.currentThread().getContextClassLoader()
+" 线程 Id :" + Thread.currentThread().getId() +
"线程所属分组:" + Thread.currentThread().getThreadGroup() +
"线程的优先级:" +Thread.currentThread().getPriority()
);
int a = 4;
int b = 3;
return a+b;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableTask callableTask = new CallableTask();
FutureTask<Integer> futureTask = new FutureTask<>(callableTask);
//创建线程
Thread thread = new Thread(futureTask);
thread.start();
System.out.println(" 返回值 :" + futureTask.get());
}
}
结果:
实现 Callable接口 创建的线程,
线程名字 Thread Name:Thread-0
线程状态 Thread State:RUNNABLE
线程的上下文类加载器: sun.misc.Launcher$AppClassLoader@b4aac2
线程 Id :9
线程所属分组:java.lang.ThreadGroup[name=main,maxpri=10]
线程的优先级:5
返回值 :7
问题:
1、通过代码的流程,可以看出,最终任务的执行还是调用的Thread的start方法进行执行,那么FutureTask 和 Callable是如何实现多线程的?
- 因为FutureTask实现了RunnableFuture , RunnableFuture 又继承了Runnable接口,所以,Thread构造方法中我们就可以传递Runnable接口的子类FutureTask,最终调FutureTask用子类重写RunnableFuture 的run方法,而在FutureTask的run方法中,我们最终又调用了属性Callable的call方法,最终实现多线程
- 在FutureTask的run方法中,除了执行属性Callable的run方法,我们还做了进一步扩展,比如当call方法执行完毕后初始化result返回值属性,从而得到返回值
- 设计模式——>适配器模式,Callable接口和Runnable接口,两者本身无关联,通过Runnable接口的实现类的run方法中调用call方法,最终实现关联,这是典型的适配器模式,把Callable call接口和Runnable run接口进行适配并做扩展,Future就相当于一个适配器.
FutureTask 的UML结构如下: