1 前言
在java中我们经常会遇到多线程问题,包括线程的互斥,同步,线程通信,线程池等一些问题,也是面试中经常问道的。这里做一个笔记。
本篇主要介绍创建线程的三种方式,后续会陆续介绍同步,线程通信等问题
2 继承Thread类
继承Thread类并复写run()方法,是一种很简单的方式,代码如下:
package thread;
/**
* Created by qiyei2015 on 2017/2/6.
*/
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name + "已经运行");
}
}
run()方法内就是线程体,可以做我们自己的工作。另外线程的启动是调用start()方法。
import thread.MyThread;
public class Main {
public static void main(String[] args) {
new MyThread("线程一").start();
}
}
结果:
可以看到线程已经运行起来了。
3 实现Runnable接口
这个是我们经常使用的方式之一,代码如下:
package thread;
/**
* Created by qiyei2015 on 2017/2/6.
*/
public class MyTask implements Runnable {
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name + "已经运行");
}
}
测试代码如下:
import thread.MyTask;
public class Main {
public static void main(String[] args) {
//new MyThread("线程一").start();
new Thread(new MyTask(),"线程二").start();
}
}
结果:
一般我们推荐的就是实现Runable接口这种方式来创建线程,这是因为由于java的单继承限制,我们继承了Thread类就不能再继承其他的类了。
4 Callable与Future创建线程
我们看到不管是Thread还是Runable接口,其run()都是无返回值的,并且无法抛出异常的,如果我们有需要返回值或者抛出异常怎么办?这个时候就需要用到Callable与Feature了。
先来看类的继承关系
可以看到Callable是一个接口,里面有个V call()方法,这个V就是我们返回值类型,同时还有Future相关的类,注意观察FutureTask类的构造函数,我们发现其中一个构造函数的参数是Callable类型,这里就把两个内联系起来了。Callable与Future的用法如下:
用法一:使用FutureTask来启动Callable线程
实现Callable接口的V call()方法,并构造FutureTask 对象,调用该对象的run()方法
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Main {
public static void main(String[] args) {
//new MyThread("线程一").start();
//new Thread(new MyTask(),"线程二").start();
callableTest1();
}
private static void callableTest1(){
//这里指定返回String类型
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("Callable 已经运行啦");
return "this is Callable is running";
}
};
FutureTask<String> futureTask = new FutureTask<String>(callable);
futureTask.run();
try {
if (futureTask.isDone()){ //任务完成
System.out.println(futureTask.get());
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
结果如下:
可以看到线程已经运行了,并且我们也收到返回值了。其中call()就是我们的线程体。
用法二:使用 ExecutorService线程池来提交Callable任务
这里我们主要用到线程池,有关线程池的问题后面再细讲。这里只需要知道怎么用就行。我们还是使用Future来获取返回的结果,代码如下:
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) {
//new MyThread("线程一").start();
//new Thread(new MyTask(),"线程二").start();
//callableTest1();
callableTest2();
}
private static void callableTest2(){
ExecutorService executorService = Executors.newSingleThreadExecutor();
//这里指定返回Integer类型
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("Callable 已经运行啦");
return 1024;
}
};
Future<Integer> futureTask = executorService.submit(callable);
try {
Thread.sleep(100);
if (futureTask.isDone()){
System.out.println(futureTask.get());
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
结果:
好,本文到这里就结束了。