目录java
在以前的学习过程当中,已经不止一次地提到了并发啊,线程啊,同步异步的内容,可是出于内容的局部一体,以前老是几笔带过,并附上:之后学习的时候再细说。编程
那么,如今到了细说的时候,在翻阅并参考了介绍Java并发编程的书以后,忽然感受压力有些大,由于有些概念确实比较抽象。因此以后的内容不定长短,可是天天都会试着输出一些。设计模式
一个进程能够拥有多个线程,一个线程必须拥有一个父进程。
进程:当前操做系统正在执行的任务,也是操做系统运行程序的一次执行过程。
线程:是进程的执行单元,是进程中正在执行的子任务。
就好像咱们正在使用的QQ,正在放歌的音乐软件,正在打的游戏,就是一个个的进程。咱们在QQ进程中执行的各类操做,就是一个个的线程。安全
每一个Java的应用程序运行的时候其实就是个进程,JVM启动以后,会建立一些进行自身常规管理的线程,如垃圾回收和终结管理,和一个运行main函数的主线程。多线程
如今大部分的操做系统都是支持多进程并发运行的,就像咱们如今正在使用电脑,能够经过任务管理器查查看,会发现有几十个几百个进程在“同时执行”。”同时执行“被打上了引号,显然事实上并非。并发
并发:就拿进程来讲,在同一个时刻,只能有一条指令执行,可是多个进程能够被快速地轮换执行,CPU的执行速度之快,让人产生这些个进程就是在同时执行。
并行:就是同一时刻,多条进程指令在多个处理器上同时执行。异步
看看下面的图就懂了:ide
接下来是我对于并发和并行假想场景:
并发场景:假设如今有一台只能一我的玩的电脑,老大和老二兄弟俩都想玩一小会儿,那没办法,得想办法解决啊。打一架吧,谁抢到算谁的。不论是谁抢到,他们必定玩到知足才会罢休,这就是如今操做系统所采用的高效率的抢占式多任务操做策略。
并行场景:如今有两台电脑,老大老二都各自玩各自的电脑,不争也不抢。函数
线程被称为轻量级进程,大多数状况下,进程中的多线程的执行是抢占式的,就和操做系统的并发多进程同样。学习
线程拥有本身的堆栈、程序计数器和局部变量,容许程序控制流的多重分支同时存在于一个线程,共享进程范围内的资源,所以,同一进程中的线程访问相同的变量,并从同一个堆中分配对象,实现良好的数据共享,可是若是处理不当,会为线程安全形成必定的隐患。
多线程相比于多进程的优点:
如下参考自《JAVA并发编程实战》:
- 一个单线程应用程序一次之能运行在一个处理器上。在双处理器系统中只运行一个应用程序,至关于其中一个处理器空闲,50%的CPU资源没有利用上。随着处理器的增多,单线程的应用程序放弃的CPU资源将会更多。这一点,正好也侧面反映了多线程可以更有效地利用空闲的处理器资源。
- 处理器在某些状况是空闲的,如在等待一个同步IO操做完成的时候。这个时候,暂且不论多处理器,仅仅针对单处理器,多线程的优点也是至关明显的,能够很好地利用处理器空闲的时间运行另一个线程。
先来看看多线程编程中这个至关关键的类,java.lang.Thread
,官方文档说了:有两种方式建立线程,就是下面这俩:
Thread
的子类。Thread
的run()
方法。建立线程以下:
/*建立线程*/ //建立一个类继承Thread类 class TDemo extends Thread{ //线程要执行的任务在run方法中 @Override public void run(){ for (int i = 0; i < 5; i++) { System.out.println(i); } } }
启动线程以下:
public static void main(String[] args){ //建立了TDemo的实例 TDemo t1 = new TDemo(); //启动线程,并调用run方法 t1.start(); System.out.print("main"); } //输出结果:main01234
建立TDemo的实例对象不等于启动了该实例所对应的线程,启动须要调用线程对象的start()
方法。
线程对象调用start()
方法以后,线程就会处于就绪状态,JVM会为其建立方法调用栈和程序计数器,表示这个线程能够执行,但真正啥时候开始执行取决于JVM中线程调度器的调度。
以后才进入运行状态,执行run()
方法中的方法体。
咱们试着把start()方法换成run()方法看看结果:01234main
咱们经过输出结果能够看到,调用start()方法,系统会把run()方法当成线程执行体处理,主线程和咱们建立的线程将并发执行。但若是单纯调用run()方法,系统会把线程对象当成一个普通的对象,run()方法也只是普通对象方法的一部分,是主线程的一部分。
这是Runnable
接口的内容,@FunctionalInterface
注解表示函数式接口,和Java8新特性lambda表达式相关,以后再作学习总结。
@FunctionalInterface public interface Runnable { public abstract void run(); }
//实现Runnable接口 class RDemo implements Runnable{ @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(i); } } } //建立并启动线程 Thread t = new Thread(new RDemo()); t.start();
调用public Thread(Runnable target)
构造器,将Runnble接口类型对象传入做为参数,构建线程对象。
固然还能够用匿名内部类的形式:
//匿名内部类建立并启动线程 new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.print(i); } } }).start();
这是Callable接口的内容:
@FunctionalInterface public interface Callable<V> { V call() throws Exception; }
除了上面两种方法以外,从书上看到还有一种Java5新增的方法,利用Callable接口,官方文档是这样描述的:
光有Callable
接口还不行,毕竟隔了5年才出来,为了尽可能避免修改以前的代码,适应当前环境,Java5还新增了配套的Future
接口:
public interface Future<V> { //试图取消Callable中任务的执行,若是任务已经完成、已经被取消、或因其余缘由没法被取消,返回false。 boolean cancel(boolean mayInterruptIfRunning); //若是此任务在正常完成以前被取消,则返回true boolean isCancelled(); //若是此任务已完成(正常的终止、异常或取消),则返回true boolean isDone(); //若是须要,则等待计算完成,而后检索其结果。 V get() throws InterruptedException, ExecutionException; //若是须要,将等待最多给定的时间以完成计算,而后检索其结果。 V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
经过继承关系能够发现,RunnableFuture
接口同时继承了Runnable
和Future
接口,意味着实现RunnableFuture
接口的类既是Runnable的是实现类,又是Future的实现类。FutureTask就是充当这样的角色,它的实例能够做为target传入Thread的构造器中。
经过查看源码,能够发现FutureTask内部维护了一个Callable的对象,能够经过下面的这个构造器初始化Callable对象。
public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable }
FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() { @Override public Integer call() throws Exception { int i = 0; while(i<10){ System.out.println(Thread.currentThread().getName()); i++; } return i; } });
public Thread(Runnable target, String name)
将task对象做为参数建立新线程并启动。name
参数是能够自定义线程的名字。new Thread(task,"name").start();
try { System.out.println(task.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }
继承类Thread和实现接口(Runnable或Callable)这两种方式的区别?
currentThread()
。//继承Thread System.out.print(this.getName()+i); //实现Runnable接口 System.out.print(Thread.currentThread().getName()+i);
前者线程类每建立一个线程都须要建立一个对象,对象之间不能共享实例变量。然后者经过接口的实现类建立的多个线程能够共享同一个Runnable类型的target,也就是这个线程类的实例变量。
前者定义线程类须要继承Thread,而Java只支持单继承,支持接口多实现,显然在灵活性方面,后者优于前者。
本文做为我的学习笔记,仍停留在比较浅显的层面,还须要大量的实践去感悟并发编程的奥义。
参考资料:《JAVA并发编程实战》、《疯狂Java讲义》、《JAVA多线程设计模式》