首先我们了解一下什么是线程?
线程
线程时操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程值得是进程中一个单一顺序的控制流。
多线程是一个进程中可以并发多个线程,每条线程并行执行不同的任务。
Java中线程的实现
一、实现Runnable接口
这里举一个小例子:
public class RunnableTest implements Runnable{
@Override
public void run() {
int i = 0;
for (int j = 0; j < 10000; j++) {
i++;
}
System.out.println(i);
}
}
这里实现了Runnable接口,并且重写了run方法。实现了我们的逻辑需求。
但我们需要启动这个线程,那么需要以下的操作。
public static void main(String[] args) {
new Thread(new RunnableTest()).start();
}
调用Thread的start方法将我们创建的RunnabkeTest的实例化作为参数传入即可开启线程。启动线程是交给JVM启动run方法实现的。
二、继承Thread类
再举一个小例子
public class ThreadTest extends Thread {
public void run(){
int i = 0;
for (int j = 0; j < 10000; j++) {
i++;
}
}
}
这里继承了Thread类 重写了run方法,实现了逻辑代码。
启动线程:
public static void main(String[] args) {
new ThreadTest().start();
}
运用继承Thread的线程类可以直接调用start方法完成启动。
但两者有什么区别呢?
三、实现Callable接口
Callable具有返回参数的线程实现方式。
下面是接口实现方式。
class Data implements Callable<String>{
protected final String string;
public Data(String string) {
this.string = string;
}
@Override
public String call() throws Exception {
return string;
}
}
实现callable接口后重现call接口,返回一个参数,可以完成线程异步加载数据等功能。
public class CallableTest {
final static String MY_NAME = "furenmin";
private static String s;
public static void main(String[] args) {
Data data = new Data(CallableTest.MY_NAME);
FutureTask<String> stringFutureTask = new FutureTask<String>(data);
new Thread(stringFutureTask).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
s = stringFutureTask.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(s);
}
}
利用FutureTask进行封装Callable对象,本身FutureTask实现Runnable接口,所以线程开启和Runnable一样。
区别
上述代码中,重写了run方法。这两者有什么关系么?
其实Thread也实现了Runnable接口了。继承Thread重写run方法也就是重写Runnable的run方法。
public class Thread implements Runnable {.....}
这是Thread类源码的声明。
区别:
1、 Thread是类,所以只能单继承,而Runnable是接口可以多实现。
2、线程池只能对实现Runnable。Callable的接口的线程进行操作。
线程的生命周期
关于多线程的生命周期,先看一看这张图。
上述图介绍了线程的运行流程,借此整理一下线程的生命周期
-
新建状态New:通过new和Thread类创建线程对象后,线程就进入了新建状态。
-
就绪状态Runnable:就绪状态的线程已经具有运行的条件,但没有分配CPU,处于就绪队列中,也就是Thread.start()时还未真正的启动线程。
-
运行状态Running: 线程执行自己的run方法时,才真正的启动线程并执行方法。
-
阻塞状态Blocked:因某些情况,进入阻塞状态,如执行了sleep方法,将cpu暂时让出并停止运行本线程,直到阻塞原因消除,线程继续执行未完成的任务。
-
死亡状态Dead:死亡状态是线程的最后一个阶段,通过线程任务执行结束即为死亡,另一个是通过调用stop或destroy方法终止线程的运行。