多线程基础(2)-入门类和接口
2.1 Thread类和Runnable接口
那么在Java中,我们是如何使用多线程的呢?
首先,我们需要有一个“线程”类。JDK提供了Thread类和Runnable接口来让我们实现自己的“线程”类。
- 继承
Thread类,并重写run方法; - 实现
Runnable接口的run方法;
2.1.1 继承Thread类
Thread的结构比较复杂

public class UsingThread {
public static void main(String[] args){
new MyThread().start();//继承Thread类
}
public static class MyThread extends Thread{
@Override
public void run(){
System.out.println("MyThread");
}
}
}
2.1.2 实现Runnable接口
首先看下Runnable接口,看注解函数式接口,其次只有一个抽象方法,属于比较轻量级的。
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
public class UsingThread { public static void main(String[] args){ new Thread(new MyRunnable()).start();//实现Runnable接口,代理模式,需要Thread来执行 } public static class MyRunnable implements Runnable{ @Override public void run() { System.out.println("MyRunnable"); } } }
2.1.3 Thread类与Runnable接口的比较:
实现一个自定义的线程类,可以有继承Thread类或者实现Runnable接口这两种方式,它们之间有什么优劣呢?
- 由于Java“单继承,多实现”的特性,Runnable接口使用起来比Thread更灵活。
- Runnable接口出现更符合面向对象,将线程单独进行对象的封装。
- Runnable接口出现,降低了线程对象和线程任务的耦合性。
- 如果使用线程时不需要使用Thread类的诸多方法,显然使用Runnable接口更为轻量。
PS:个人总结,实现Runnable接口,再通过Thread代理执行,这样解耦,“线程任务“”和“线程对象“”解耦。
2.1.4 Thread类 start方法解析
通过源码可以发现start()方法调用的native start0()方法
public synchronized void start() {
if (threadStatus != 0)//状态不是新建的话,抛出非法线程状态异常,调用启动后,状态不为0
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();//调用
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
2.1.5 Thread类 setDaemon方法
Daemon,守护;setDaemon设置为守护线程,在实例调用start方法之前可以调用,来转换为守护线程。
守护线程是一种用来提供通用服务的线程,为用户线程服务。当只有守护线程的时候,JVM会退出。JVM必须等待所有的用户线程执行完毕才能退出。
守护线程的子线程是守护线程,因为守护线程不会访问或者写入任何的资源,守护线程会中任何的时刻被终止,不会保证执行完毕。
//Marks this thread as either a {@linkplain #isDaemon daemon} thread
//or a user thread. The Java Virtual Machine exits when the only
//threads running are all daemon threads.
//throws IllegalThreadStatException if this thread is alive
//将线程转换为另一种线程,守护线程。当只有守护线程的时候,JVM会退出。
public final void setDaemon(boolean on) {
checkAccess();
if (isAlive()) {
throw new IllegalThreadStateException();
}
daemon = on;
}
/**
* Tests if this thread is alive. A thread is alive if it has
* been started and has not yet died.
*
* @return <code>true</code> if this thread is alive;
* <code>false</code> otherwise.
*/
public final native boolean isAlive();
2.2 Callable、Future与FutureTask
通常来说,我们使用Runnable和Thread来创建一个新的线程。但是它们有一个弊端,就是run方法是没有返回值的。而有时候我们希望开启一个线程去执行一个任务,并且这个任务执行完成后有一个返回值。JDK提供了Callable接口与Future接口为我们解决这个问题,这也是所谓的“异步”模型。
@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的呢?Callable一般是配合线程池工具ExecutorService(FutrueTask实现RunnableFutrue接口,这个接口继承Runnable接口,故可以提交到线程池执行)来使用的。我们会在后续章节解释线程池的使用。这里只介绍ExecutorService可以使用submit方法来让一个Callable接口执行。它会返回一个Future,我们后续的程序可以通过这个Future的get方法得到结果。
public class Callable2Runnable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();//java.util.concurrent
MyCallable callableTask = new MyCallable();
Future<String> callableFuture = executorService.submit(callableTask);//方法实现:将callable对象转换为FutureTask对象,FutrueTask实现RunnableFutrue接口,
//RunnableFuture继承Runnable接口
callableFuture.cancel(true);
System.out.println(callableFuture.isCancelled());//future线程执行结果
System.out.println(callableFuture.get());//future线程执行结果
MyRunnable runnableTask = new MyRunnable();
Future runnableFuture = executorService.submit(runnableTask);//submit也可以
executorService.execute(runnableTask);//可以通过execute方法执行
FutureTask<String> futureTask = new FutureTask<>(callableTask);//将runnable或者callable通过FutureTask来执行
executorService.submit(futureTask);
System.out.println(futureTask.get());
}
public static class MyRunnable implements Runnable{
@Override
public void run() {//void方法
System.out.println("MyRunnable");
}
}
public static class MyCallable implements Callable<String>{
@Override
public String call() throws Exception {//异常可以往外抛,有返回值
return "MyCallable";
}
}
}
将callable对象转换为FutureTask对象,FutrreTask实现了RunnableFuture接口,RunnableFuture接口继承类Runnable接口,所以可以提交到线程池来执行。
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}

总结下:

原文地址:http://redspider.group:4000/article/01/2.html
感恩作者
浙公网安备 33010602011771号