Java中实现线程原理:
Java中实现多线程的方式中最核心的就是 run()方法,不管何种方式其最终都是通过run()来运行。
新建线程时,我们常用new Thread(Runnable).start() 或者线程池搭载Runnable的两种方法来运行。
Java刚发布时也就是JDK 1.0版本提供了两种实现方式:
一个是继承Thread类,
一个是实现Runnable接口。
两种方式都是去重写run()方法,
在run()方法中去实现具体的业务代码。
继承Thread类和实现Runnable接口实现方式存在的弊端:
因为run()方法是没有返回值的,所以通过这两种方式实现的多线程的读是无法获得执行的结果。
为了解决以上两种实现方式存在的弊端而出现了第三种实现方法:
为了解决这个问题在JDK 1.5的时候引入一个Callable接口,根据泛型V设定返回值的类型,实现他的call()方法,可以获得线程执行的返回结果。虽然call()方法可以获得返回值,但它需要配合一个Future才能拿到返回结果,而这个Future又是继承了Runnable的一个接口。 通过查阅源码就可以发现Future的实现FutureTask其在做具体业务代码执行的时候仍是在run()里面实现的。
Java多线程实现方式的代码示例:
一.通过继承Thread类实现,但无法获得执行的结果
实现步骤:
①定义类继承Thread类,并重写Thread类的run()方法,该run()方法的方法体就代表了线程需要完成的任务。因此把run()方法称为线程执行体。
②创建Thread子类的实例,即创建了线程对象。
②调用线程对象的start()方法来启动该线程。
例子:
public class XianChengThread {
public static void main(String[] args) throws Exception {
//方式一:通过继承Thread类实现
//创建Thread类
Thread myThread1 = new MyThread();//线程名称:Thread-0
Thread myThread2 = new MyThread();//线程名称:Thread-1
Thread myThread3 = new MyThread();//线程名称:Thread-2
//调用start方法方可启动线程
//不能调用run()方法,run方法只是thread的一个普通方法,还是在主线程里执行。
myThread1.start();
myThread2.start();
myThread3.start();
}
}
class MyThread extends Thread {
@Override
public void run() {
for(int i=0;i<3;i++){
System.out.println("Thread Name:" + Thread.currentThread().getName());
}
}
}
执行结果:
二.通过实现Runnable接口实现,但无法获得执行的结果
实现步骤:
①定义类实现Runnable接口,并重写Runnable接口的run()方法,该run()方法的 方法体就代表了线程需要完成的任务。
②创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
②调用该Thread对象的start()方法来启动该线程。
例子:
public class XianChengRunnable {
public static void main(String[] args) {
//方式二,通过实现Runnable接口实现
//创建Runnable实现类的实例
MyRunnableThread myRunnable = new MyRunnableThread();
//创建Thread类,并把Runnable实现类的实例作为参数
Thread myRunnableThread1 = new Thread(myRunnable);//线程名称:Thread-0
Thread myRunnableThread2 = new Thread(myRunnable);//线程名称:Thread-1
//start() 方法启动线程
myRunnableThread1.start();
myRunnableThread2.start();
}
}
class MyRunnableThread implements Runnable {
@Override
public void run() {
// 实现Runnable接口的,无法直接使用getName()等方法
// 需要使用Thread.currentThread() 来获取到当前对象才行
for(int i=0;i<5;i++){
System.out.println("Thread Name:" + Thread.currentThread().getName());
}
}
}
执行结果:
三.通过实现Callable接口实现,可获得执行的结果
例子:
public class XianChengCallable {
public static void main(String[] args) throws Exception {
//方式三:通过实现Callable接口来实现
//执行线程方法一:通过线程start()方法执行
//创建Callable实现类的实例
Callable<String> myCallable = new MyCallableThread();
//创建一个 FutureTask,一旦运行就执行给定的 Callable。
FutureTask<String> futureTask = new FutureTask<>(myCallable);
//创建Thread类,并把创建的FutureTask实例作为参数
Thread myCallableThread = new Thread(futureTask);
//给线程名称赋值
myCallableThread.setName("通过实现Callable接口来实现的线程");
//通过start()方法执行
myCallableThread.start();
//FutureTask.get()方法可以等待计算完成,然后获取其结果。
System.out.println("获取线程的结果:" + futureTask.get());
//执行线程方法二:通过线程池执行
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(futureTask);
executorService.shutdown();
System.out.println("获取通过线程池执行的结果:" + futureTask.get());
}
}
class MyCallableThread implements Callable {
@Override
//call()方法可以计算结果,如果无法计算结果,则抛出一个异常。
public String call() throws Exception {
return Thread.currentThread().getName();
}
}
执行结果:
Callable与Runnable区别:
1.Runnable没有返回值,Callable可以获取返回值并返回
2.Callable接口是带有泛型的,Callable。该泛型T,也是Callable返回值的类型;Callable接口需要实现的方法为call方法;
Runnable接口需要实现的方法为run方法;
3.Callable一般配合线程池的submit方法以及FutureTask使用,Runnable一般是配合new Thread或者线程池使用;
4.Callable配合FutureTask,可以通过Future来控制任务执行、取消,查看任务是否完成等。Runnable也可以通过Future来实现以上功能,但方式不一样
Callable、Runnable 、Future与FutureTask的联系:
Callable的价值,在Future上体现。
Future是一个接口,而FutureTask是Future接口的官方唯一实现类。
Future以及其实现类,是用于搭载Runnable或者Callable,执行任务、控制任务并能有效返回结果。FutureTask类不止实现了Future接口,还实现了其他的接口!如Runnable接口
FutureTask有一个很重要的方法,是Done(),用于表示该FutureTask中的任务已执行完毕。