多线程据本人调研主要分为以下三种实现方法:
1.实现Runnable 接口
2.继承Thread类
3.通过Callable和Future
1和2的方法其实基本相似,翻看实现后发现Thread类的本质也是实现了Runnable接口,由于网上相似的资料实在太多,随便一搜就能找到很多用法,因此不在这里过多阐述,只是简单介绍一些实现细节而非使用方法
进程和线程的区别可以理解为进程就是一个执行中的应用程序,线程是进程中的执行单元
一个进程可以启动多个线程,比如JAVA的虚拟机其实就是一个进程,JVM的主线程调用main方法
在同时启动一个垃圾回收线程负责回收垃圾
因此JVM中最少有两个并发,一个是main的主线程,一个是垃圾回收线程
对于单核cpu来说,多线程并不是真正的并行,而是频繁的进行切换,给人的感觉是同时执行多个线程,但是如果是多核cpu来说就可以同时执行多个线程并发了
多线程并发环境下数据的安全问题
其实之前写的时候也遇到过数据的安全问题
当满足多线程,有共享数据,共享数据被修改的时候就会产生数据安全问题
如何解决线程安全问题呢
线程排队执行,不能并发,也叫做线程同步,线程同步就会牺牲一部分效率,但是没有办法,数据安全更重要
异步编程模型和同步编程模型:
线程a和b各自执行各自的,叫做异步
线程a和b在a执行的时候需要b,叫做同步
同步就是排队,异步就是并发
介绍完这些之后我们来看一下第三种多线程实现方法,即使用
Callable和Future
在使用传统接口runnable的时候并不会返回任何值,如果我们希望任务完成之后有返回值,可以使用callable接口,他的类型参数方法为call()而非run(),我们使用的时候要使用ExecutorService.submit()进行调用
先看一下callable和future的源码
@FunctionalInterfacepublic 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;}
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;}
写一个测试用例
import java.util.concurrent.Callable;public class CallableTestClass implements Callable<String> { private int id; public CallableTestClass(int id){ this.id=id; } @Override public String call() throws Exception { return "it is a test:"+id; }}
import org.testng.annotations.Test;import java.util.ArrayList;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;public class ThreadTest { @Test public void test1(){ ExecutorService executorService= Executors.newCachedThreadPool(); ArrayList<Future<String>> results=new ArrayList<Future<String>>(); for(int i=0;i<10;i++){ results.add(executorService.submit(new CallableTestClass(i))); } for(Future<String> fs:results){ try{ System.out.println(fs.get()); }catch (InterruptedException e){ System.out.print(e); }catch (ExecutionException e){ System.out.print(e); }finally { executorService.shutdown(); } } }}
可以看到使用过程中我们使用Future来进行参数的接受,为什么要有Future呢?
原因是使用传统的Runnable的时候执行完之后无法获得执行结果
Future的核心思想是让主线程能够在同步等待的这段时间用来做其他的事情
Future有三种功能:
1)判断任务是否完成;
2)能够中断任务;
3)能够获取任务执行结果。
由于Future只是一个接口无法直接创建对象使用,因此有了FutureTask这个东西
具体实现先不看了,先看一下大概的东西
public class FutureTask<V> implements RunnableFuture<V>
它实现了RunnableFuture
然后
public interface RunnableFuture<V> extends Runnable, Future<V> { /** * Sets this Future to the result of its computation * unless it has been cancelled. */ void run();}
从上面可以看出来RunnableFuture既可以作为Runnable被线程执行,又可以作为Future得到Callable 的返回值
因此使用的时候我们不仅可以使用Callable+future来获取执行结果,也可以只用Callable+FutureTask来获取执行结果
如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future 形式类型、并返回 null 作为底层任务的结果。