并发
进程&线程
进程
进程的本质是一个正在执行的程序,程序运行时系统会建立一个进程,而且给每一个进程分配独立的内存地址空间保证每一个进程地址不会相互干扰。进程是具备必定独立功能的程序、它是系统进行资源分配和调度的一个独立单位,重点在系统调度和单独的单位。同时,在 CPU 对进程作时间片的切换时,保证进程切换过程当中仍然要从进程切换以前运行的位置处开始执行。因此进程一般还会包括程序计数器、堆栈指针。 有了进程之后,可让操做系统从宏观层面实现多应用并发。而并发的实现是经过 CPU 时间片不端切换执行的。对于单核 CPU 来讲,在任意一个时刻只会有一个进程在被CPU 调度。单CPU进行进程调度的时候,须要读取上下文+执行程序+保存上下文,即进程切换。安全
线程
线程是进程的一个实体,是CPU调度和分派的基本单位,他是比进程更小的能独立运行的基本单位,线程本身基本上不拥有系统资源。在运行时,只是暂用一些计数器、寄存器和栈 。轻量级的进程, 线程能够合理利用多核心CPU资源,提升程序的吞吐量。多线程
线程的存在感
- 在多核 CPU 中,利用多线程能够实现真正意义上的并行执行
- 在一个应用进程中,会存在多个同时执行的任务,若是其中一个任务被阻塞,将会引发不依赖该任务的任务也被阻塞。经过对不一样任务建立不一样的线程去处理,能够提高程序处理的实时性
- 线程能够认为是轻量级的进程,因此线程的建立、销毁比进程更快
- 在进程中的不一样线程为了使用CPU核心,则会进行线程切换,可是因为共享了程序执行环境,这个线程切换比进程切换开销少了不少。在这里依然是并发,惟一核心同时刻只能执行一个线程。
总结:并发
- 单CPU中进程只能是并发,多CPU计算机中进程能够并行。
- 单CPU单核中线程只能并发,单CPU多核中线程能够并行。
- 不管是并发仍是并行,使用者来看,看到的是多进程,多线程。
- 一个线程只能属于一个进程,而一个进程能够有多个线程,但至少有一个线程(一般说的主线程)。
- 资源分配给进程,同一进程的全部线程共享该进程的全部资源。
- 线程在执行过程当中,须要协做同步。不一样进程的线程间要利用消息通讯的办法实现同步。
- 处理机分给线程,即真正在处理机上运行的是线程。
- 线程是指进程内的一个执行单元,也是进程内的可调度实体。
从三个角度来剖析两者之间的区别
- 调度:线程做为调度和分配的基本单位,进程做为拥有资源的基本单位。
- 并发性:不只进程之间能够并发执行,同一个进程的多个线程之间也能够并发执行。
- 拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但能够访问隶属于进程的资源。
线程的应用
如何应用多线程
在 Java 中,有多种方式来实现多线程。继承 Thread 类、实现 Runnable 接口、使用 ExecutorService、Callable、Future 实现带返回结果的多线程。jvm
- 继承 Thread 类建立线程
Thread 类本质上是实现了 Runnable 接口的一个实例,表明一个线程的实例。启动线程的惟一方法就是经过 Thread类的 start()实例方法。start()方法是一个 native 方法,它会启动一个新线程,并执行 run()方法。这种方式实现多线程很简单,经过本身的类直接 extend Thread,并复写 run()方法,就能够启动新线程并执行本身定义的 run()方法。
public class MyThread extends Thread { public void run() { System.out.println("MyThread.run()"); } } MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); myThread1.start(); myThread2.start();
- 实现 Runnable 接口建立线程
若是本身的类已经 extends 另外一个类,就没法直接 extends Thread,此时,能够实现一个 Runnable 接口ide
public class MyThread extends OtherClass implements Runnable { public void run() { System.out.println("MyThread.run()"); } }
3.实现 Callable 接口经过 FutureTask 包装器来建立 Thread 线程
当须要让一步执行的线程在执行完成之后,提供一个返回值给到当前的主线程,主线程须要依赖这个值进行后续的逻辑处理,那么这个时候,就须要用到 带返回值的线程了。 Java 中提供了这样的实现方式操作系统
public class CallableDemo implements Callable<String> { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService= Executors.newFixedThreadPool(1); CallableDemo callableDemo=new CallableDemo(); Future<String> future=executorService.submit(callableDemo); System.out.println(future.get()); executorService.shutdown(); } @Override public String call() throws Exception { int a=1; int b=2; System.out.println(a+b); return "执行结果:"+(a+b); } }
线程生命周期
线程一共有 6 种状态(NEW、RUNNABLE、BLOCKED、WAITING、TIME_WAITING、TERMINATED)
NEW:初始状态,线程被构建,可是尚未调用 start 方法
RUNNABLED:运行状态,JAVA 线程把操做系统中的就绪(READY)和运行(RUNNING)两种状态统一称为“运行中”
BLOCKED:阻塞状态,表示线程进入等待状态,也就是线程,由于某种缘由放弃了 CPU 使用权,阻塞也分为几种状况
➢ 等待阻塞:运行的线程执行 wait 方法,jvm 会把当前线程放入到等待队列
➢ 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被其余线程锁占用了,那么 jvm 会把当前的线程放入到锁池中
➢ 其余阻塞:运行的线程执行 Thread.sleep 或者 t.join 方法,或者发出了 I/O 请求时,JVM 会把当前线程设置为阻塞状态,当 sleep 结束、join 线程终止、io 处理完毕则线程恢复
TIME_WAITING:超时等待状态,超时之后自动返回
TERMINATED:终止状态,表示当前线程执行完毕
线程
线程的终止
interrupt 方法
当其余线程经过调用当前线程的 interrupt 方法,表示向当前线程打个招呼,告诉他能够中断线程的执行了,至于何时中断,取决于当前线程本身。 这种经过标识位或者中断操做的方式可以使线程在终止时有机会去清理资源,而不是武断地将线程中止,所以这种终止线程的作法显得更加安全和优雅。 线程经过检查资深是否被中断来进行相应,能够经过isInterrupted()来判断是否被中断。
thread.interrupt()方法实际就是设置一个 interrupted 状态标识为 true、而且经过ParkEvent 的 unpark 方法来唤醒线程。指针
- 对于 synchronized 阻塞的线程,被唤醒之后会继续尝试获取锁,若是失败仍然可能被 park
- 在调用 ParkEvent 的 park 方法以前,会先判断线程的中断状态,若是为 true,会清除当前线程的中断标识
- Object.wait 、 Thread.sleep 、 Thread.join 会 抛 出InterruptedException
Thread.interrupted
经过 interrupt,设置了一个标识告诉线程可 以 终 止 了 , 线 程 中 还 提 供 了 静 态 方 法Thread.interrupted()对设置中断标识的线程复位。外面的线程调用 thread.interrupt 来设置中断标识,而在线程里面,又经过 Thread.interrupted 把线程的标识又进行了复位。code
其余的线程复位
除了经过 Thread.interrupted 方法对线程中断标识进行复位之外,还 有 一 种 被 动 复 位 的 场 景,就 是对抛出InterruptedException 异 常 的 方 法,在 InterruptedException抛出以前,JVM 会先把线程的中断标识位清除,而后才会抛出InterruptedException,这个时候若是调用 isInterrupted方法,将会返回 false.对象
为何要复位
Thread.interrupted()是属于当前线程的,是当前线程对外界中断信号的一个响应,表示本身已经获得了中断信号,但不会马上中断本身,具体何时中断由本身决定,让外界知道在自身中断前,他的中断状态仍然是 false,这就是复位的缘由。
InterruptedException
Object.wait、Thread.sleep 和 Thread.join都会抛出InterruptedException。阻塞的方法释放会取决于一些外部的事件,可是阻塞方法可能由于等不到外部的触发事件而致使没法终止,因此它容许一个线程请求本身来中止它正在作的事情。当一个方法抛出 InterruptedException 时,它是在告诉调用者若是执行该方法的线程被中断,它会尝试中止正在作的事情而且经过抛出 InterruptedException表示提早返回。因此,这个异常的意思是表示一个阻塞被其余线程中断了。 而后 ,因为线程调 用了interrupt() 中断方法,那 么Object.wait、Thread.sleep等被阻塞的线程被唤醒之后会经过 is_interrupted 方法判断中断标识的状态变化,若是发现中断标识为 true,则先清除中断标识(中断标识置为false),而后抛出InterruptedException。须要注意的是,InterruptedException异常的抛出并不意味着线程必须终止,而是提醒当前线程有中断的操做发生,至于接下来怎么处理取决于线程自己,好比
- 直接捕获异常不作任何处理
- 将异常往外抛出
- 中止当前线程,并打印异常信息
--------知识浅薄,望君勿喷。