-
1:继承Thread类创建线程
-
2:实现Runnable接口创建线程
-
3:使用Callable和FutureTask创建线程
-
4:使用线程池,例如用Executor框架创建线程
继承Thread类创建线程
继承Thread类创建一个新的线程类,重写其run()
方法
-
因为java单继承的机制无法再继承其他的类
-
直接使用this即可获得当前线程
-
简单示例
public class ExtendThread {
static class ThreadDemo extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(getName() + " --> i");
}
System.out.println("End.....");
}
}
public static void main(String[] args) {
Thread thread = new ThreadDemo();
thread.start();
}
}
实现Runnable接口
观察Thread
类的源码,发现Thread
也是一个实现了Runnable
接口的类。观察他的run()
法,如果他的执行目标target不为空,则执行target的run方法。target是此类的一个实例对象,其类型是Runnable
。在Thead中拥有可初始化此target的构造器。综上我们可以通过实现Runnable
接口的方式,而后将实例传递给Thread
从而实现线程的创建。
public class Thread implements Runnable
/* What will be run. */
private Runnable target;
public Thread(Runnable target) { ... }
@Override
public void run() {
if (target != null) {
target.run();
}
}
看一下Runnable
接口的真面目:
@FunctionalInterface // 标示这是一个函数式接口:有且只有一个抽象方法,非必要的注解
public interface Runnable {
public abstract void run();
}
Runnable
是一个很简单的函数式接口,只有一个抽象方法,代表被执行的用户逻辑的抽象,此方法将在Thread中的target.run();
被执行。
- 简单示例
public class ByRunnable {
static class ThreadDemo implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " --> i");
}
}
}
public static void main(String[] args) {
new Thread(new ThreadDemo()).start();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " --> i");
}
}).start();
}
}
-
当使用此方法创建线程时,创建的类不是线程类而是线程类的目标类,需要将他交给线程类才能创建真正的线程。在访问线程时,需要通过
Thread.currentThread()
先获取到当前线程示例才能访问和控制当前线程 -
好处是一方面他可以避免Java的单继承带来的局限性,另一方面也可以使逻辑和数据更好的分离
实现Callable接口
Callable
接口位于java.util.concurrent
包下:
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
他跟Runnable
类似,也是一个函数式接口。不同的是他唯一的抽象对象是有返回值的,返回类型是接口的泛型,还有一个Exception
的异常声明。在Thread
中的target的类型是Runnable
,并且这两个接口之间也没有任何的继承关系,显然我们不能使用Callable实例作为target直接创建线程了。为解决此问题,JDK我我们提供了一个中间桥接的接口:RunnableFuture
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
从上面代码可以出该接口继承了Runnable接口保证了其实例可作为Thread的目标类。同时继承了Future接口拥有了一些控制异步任务的功能,如取消执行中任务任务,判断异步任务是否完成以及获取异步任务执行结果等。
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
RunnableFuture
既可以作为Thread
的目标类,也可以获取异步任务的执行结果,作为Thread和Callable间一个重要的桥接角色。在创建对象时,需要使用到 RunnableFuture
的一个实现类FutureTask
.
Callable中的call()方法最终也是在Runnable的run()中被执行。
// FutureTask.java
public void run() {
...
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 在这里执行了Callable中的call()方法
result = c.call();
ran = true;
} catch (Throwable ex) { ... }
if (ran)
set(result);
}
} finally { ... }
}
简单示例
public class ByCallable {
public static final int COMPUTE_TIME = 1000000;
static class ThreadDemo implements Callable<Long> {
@Override
public Long call() throws Exception {
long startTime = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName() + " Started ...");
Thread.sleep(1000);
for (int i = 0; i < COMPUTE_TIME; i++) {
int j = i * 10000;
}
long used = System.currentTimeMillis() - startTime;
System.out.println(Thread.currentThread().getName() + " End ...");
Thread.sleep(3000);
return used;
}
}
public static void main(String[] args) throws Exception {
ThreadDemo task = new ThreadDemo();
FutureTask<Long> futureTask = new FutureTask<>(task);
Thread thread = new Thread(futureTask, "returnableThread");
thread.start();
Thread.sleep(500);
System.out.println(Thread.currentThread().getName() + " 让子弹飞一会...");
System.out.println(Thread.currentThread().getName() + " 做一点自己的事情...");
for (int i = 0; i < COMPUTE_TIME; i++) {
int j = i * 10000;
}
System.out.println(Thread.currentThread().getName() + " 获取并发任务的执行结果");
try {
System.out.println(thread.getName() + "线程占用时间:" + futureTask.get());
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 运行结束...");
}
}
线程池技术
使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源开销,解决资源不足的问题。如果不使用线程池,有可能会造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
public class ThreadPool {
private static final int CORE_POOL_SIZE = 5;
private static final int MAX_POOL_SIZE = 10;
private static final int QUEUE_CAPACITY = 100;
private static final Long KEEP_ALIVE_TIME = 1L;
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(QUEUE_CAPACITY),
new ThreadPoolExecutor.CallerRunsPolicy());
Future<Long> future = executor.submit(new Callable<Long>() {
@Override
public Long call() throws Exception {
Thread.sleep(3000);
System.out.println("异步任务开始。。。。");
return 3L;
}
});
Long result = future.get();
System.out.println("异步任务结果:" + result);
for (int i = 0; i < 5; i++) {
executor.execute(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " --> i");
}
}
});
}
//终止线程池
executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println("Finished all threads");
}
}