目录
线程构造方法
构造方法名 | 名称 |
---|---|
Thread() | |
Thread(String name) | name为线程名字 |
Thread(Runnable target) | |
Thread(Runnable target, String name) | name为线程名字 |
线程的三种方式
一、直接 继承Thread
法
编写一个类,直接 继承 java.lang.Thread 线程类
,重写 run方法
。
- 随便写一个类
- 让这个类继承线程的方法
- 重写继承线程类的run方法
- 创建该类的线程对象(new一个继承线程的类)
- 调继承父类(线程类)的start (现在这个线程已经在就绪状态)
- 启动线程(当我调用线程对象的start()方法代表已经启动线程)
package 多线程2;
public class ThreadTest01 {
public static void main(String[] args) {
// 定义MyThread继承线程类
class MyThread extends Thread{
public void run(){
}
}
// 创建线程对象
MyThread t = new MyThread();
// 启动线程。
t.start();
}
}
run与start方法的区别
线程的run方法,不会启动线程,不会分配新的分支栈。(这种方式就是单线程)
还是走之前的主线程(main线程),执行完直接在上方弹栈
线程的start方法,是启动线程的方法,当调用该方法就是启用线程,这个线程就从新建状态到达就绪状态。(这种就是多线程)
作用是:
- 启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。
- 这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来,start()方法就结束了。线程就启动成功了。
- 启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)。
- run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。
开辟一个分支栈,栈与栈之间相互独立、互不干扰
package 多线程2;
public class ThreadTest02 {
public static void main(String[] args) {
MyThread t = new MyThread();
//t.run(); 不会启动线程,不会分配新的分支栈。(这种方式就是单线程。)
// 启动线程
t.start();
// 这里的代码还是运行在主线程中(主线程)
for(int i = 0; i < 1000; i++){
System.out.println("主线程--->" + i);
}
}
}
class MyThread extends Thread {
@Override
public void run() {
// 编写程序,这段程序运行在分支线程中(分支线程)。
for(int i = 0; i < 1000; i++){
System.out.println("分支线程--->" + i);
}
}
}
执行上方代码打印对应线程记录,可以看到有主线程与分支线程并发执行。
二、实现Runnable
法
编写一个类,实现 java.lang.Runnable
接口,实现run方法
。
- 随便写一个类MyRunnable
- 让这个类实现
Runnable
接口 - 重写run方法(注意,该类还不是一个线程类,只是一个可以运行的类还不是一个线程)
- new一个线程对象把这个类传进去(new线程类传入可运行的类/接口,这个才是线程)
- 调用线程对象的
start()
方法。(这个就是启动线程)
package 多线程;
public class ThreadTest03 {
public static void main(String[] args) {
//线程
Thread t = new Thread(new MyRunnable());
// 启动线程
t.start();
}
}
// 这并不是一个线程类,是一个可运行的类。它还不是一个线程。
class MyRunnable implements Runnable {
@Override
public void run() {
}
}
package 多线程;
public class ThreadTest03 {
public static void main(String[] args) {
//线程
Thread t = new Thread(new MyRunnable());
// 启动线程
t.start();
for(int i = 0; i < 100; i++){
System.out.println("主线程--->" + i);
}
}
}
// 这并不是一个线程类,是一个可运行的类。它还不是一个线程。
class MyRunnable implements Runnable {
@Override
public void run() {
for(int i = 0; i < 100; i++){
System.out.println("分支线程--->" + i);
}
}
}
执行上方代码打印对应线程记录,可以看到有主线程与分支线程并发执行。
采用匿名内部类创建
package 多线程;
//匿名内部类
public class ThreadTest04 {
public static void main(String[] args) {
// 创建线程对象,采用匿名内部类方式。
Thread t = new Thread(new Runnable(){
@Override
public void run() {
for(int i = 0; i < 100; i++){
System.out.println("t线程---> " + i);
}
}
});
// 启动线程
t.start();
for(int i = 0; i < 100; i++){
System.out.println("main线程---> " + i);
}
}
}
采用lambda表达(常用)
package 多线程;
public class ThreadTest04 {
public static void main(String[] args) {
//
Thread t = new Thread(() -> {
for(int i = 0; i < 100; i++){
System.out.println("t线程---> " + i);
}
});
// 启动线程
t.start();
for(int i = 0; i < 100; i++){
System.out.println("main线程---> " + i);
}
}
}
注意:
第二种方式实现接口比较常用,因为一个类实现了接口,它还可以去继承其它的类,更灵活。
三、FutureTask方式,实现Callable接口
这种方式实现的线程可以获取线程的返回值。
上述讲解的那两种方式是无法获取线程返回值的,因为run方法返回void。
思考: 系统委派一个线程去执行一个任务,该线程执行完任务之后,可能会有一个执行结果,我们怎么能拿到这个执行结果呢?
使用第三种方式:实现Callable接口方式。
Callable和Future
它俩很有意思的,一个产生结果,一个拿到结果
- Callable接口类似于Runnable,从名字就可以看出来了
- 但是Runnable不会返回结果,并且无法抛出返回结果的异常
- 而Callable功能更强大一些,被线程执行后,可以返回值
- 这个返回值可以被Future拿到也就是说,Future可以拿到异步执行任务的返回值
下面来看一个简单的例子:
package 多线程;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableAndFuture {
public static void main(String[] args) {
Callable<Integer> callable = new Callable<Integer>() {
public Integer call() throws Exception {
return new Random().nextInt(100);
}
};
FutureTask<Integer> future = new FutureTask<Integer>(callable);
new Thread(future).start();
try {
Thread.sleep(5000);
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
FutureTask实现了两个接口,Runnable和Future
所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
那么这个组合的使用有什么好处呢?
假设有一个很耗时的返回值需要计算,并且这个返回值不是立刻需要的话,那么就可以使用这个组合,用另一个线程去计算返回值,而当前线程在使用这个返回值之前可以做其它的操作,等到需要这个返回值时,再通过Future得到。
上述的例子可以看到
- Callable<Integer> callable = new Callable<Integer>()相当于实现
Runnable的可运行的类
- FutureTask<Integer> future = new FutureTask<Integer>(callable)把这个类传进去,这个future就是线程对象
- new Thread(future).start() new一个线程对象并开启线程
- System.out.println(future.get()) 获取线程的返回值
Executor
ExecutorService继承自Executor,它的目的是为我们管理Thread对象,从而简化并发线程
Executor使我们无需显示的去管理线程的生命周期,是JDK 5之后启动任务的首选方式。执行多个带返回值的任务,并取得多个返回值。
代码如下:
package 多线程;
import java.util.concurrent.*;
public class CallableAndFuture03 {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newCachedThreadPool();
CompletionService<Integer> cs = new ExecutorCompletionService<Integer>(threadPool);
for(int i = 1; i < 5; i++) {
final int taskID = i;
cs.submit(new Callable<Integer>() {
public Integer call() {
return taskID;
}
});
}
for(int i = 1; i < 5; i++) {
try {
System.out.println(cs.take().get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
打印获取线程的返回值。