Java并发编程之美 - (EPUB全文下载)
文件大小:0.16 mb。
文件格式:epub 格式。
书籍内容:
Java并发编程之美
第一部分 Java并发编程基础篇
第1章 并发编程线程基础
第2章 并发编程的其他基础知识
第二部分 Java并发编程高级篇
第3章 Java并发包中ThreadLocalRandom类原理剖析
第4章 Java并发包中原子操作类原理剖析
第5章 Java并发包中并发List源码剖析
第6章 Java并发包中锁原理剖析
第7章 Java并发包中并发队列原理剖析
第8章 Java并发包中线程池ThreadPoolExecutor原理探究
第9章 Java并发包中ScheduledThreadPoolExecutor原理探究
第10章 Java并发包中线程同步器原理剖析
第三部分 Java并发编程实践篇
第11章 并发编程实践
封底
第一部分 Java并发编程基础篇
本篇主要介绍并发编程的基础知识,包含两章内容,分别为并发编程线程基础以及并发编程的其他概念与原理解析。
第1章 并发编程线程基础
1.1 什么是线程
在讨论什么是线程前有必要先说下什么是进程,因为线程是进程中的一个实体,线程本身是不会独立存在的。进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程的一个执行路径,一个进程中至少有一个线程,进程中的多个线程共享进程的资源。
操作系统在分配资源时是把资源分配给进程的,但是CPU资源比较特殊,它是被分配到线程的,因为真正要占用CPU运行的是线程,所以也说线程是CPU分配的基本单位。
在Java中,当我们启动main函数时其实就启动了一个JVM的进程,而main函数所在的线程就是这个进程中的一个线程,也称主线程。
进程和线程的关系如图1-1所示。
图1-1
由图1-1可以看到,一个进程中有多个线程,多个线程共享进程的堆和方法区资源,但是每个线程有自己的程序计数器和栈区域。
程序计数器是一块内存区域,用来记录线程当前要执行的指令地址。那么为何要将程序计数器设计为线程私有的呢?前面说了线程是占用CPU执行的基本单位,而CPU一般是使用时间片轮转方式让线程轮询占用的,所以当前线程CPU时间片用完后,要让出CPU,等下次轮到自己的时候再执行。那么如何知道之前程序执行到哪里了呢?其实程序计数器就是为了记录该线程让出CPU时的执行地址的,待再次分配到时间片时线程就可以从自己私有的计数器指定地址继续执行。另外需要注意的是,如果执行的是native方法,那么pc计数器记录的是undefined地址,只有执行的是Java代码时pc计数器记录的才是下一条指令的地址。
另外每个线程都有自己的栈资源,用于存储该线程的局部变量,这些局部变量是该线程私有的,其他线程是访问不了的,除此之外栈还用来存放线程的调用栈帧。
堆是一个进程中最大的一块内存,堆是被进程中的所有线程共享的,是进程创建时分配的,堆里面主要存放使用new操作创建的对象实例。
方法区则用来存放JVM加载的类、常量及静态变量等信息,也是线程共享的。
1.2 线程创建与运行
Java中有三种线程创建方式,分别为实现Runnable接口的run方法,继承Thread类并重写run的方法,使用FutureTask方式。
首先看继承Thread类方式的实现。
如上代码中的MyThread类继承了Thread类,并重写了run()方法。在main函数里面创建了一个MyThread的实例,然后调用该实例的start方法启动了线程。需要注意的是,当创建完thread对象后该线程并没有被启动执行,直到调用了start方法后才真正启动了线程。
其实调用start方法后线程并没有马上执行而是处于就绪状态,这个就绪状态是指该线程已经获取了除CPU资源外的其他资源,等待获取CPU资源后才会真正处于运行状态。一旦run方法执行完毕,该线程就处于终止状态。
使用继承方式的好处是,在run()方法内获取当前线程直接使用this就可以了,无须使用Thread.currentThread()方法;不好的地方是Java不支持多继承,如果继承了Thread类,那么就不能再继承其他类。另外任务与代码没有分离,当多个线程执行一样的任务时需要多份任务代码,而Runable则没有这个限制。下面看实现Runnable接口的run方法方式。
如上面代码所示,两个线程共用一个task代码逻辑,如果需要,可以给RunableTask添加参数进行任务区分。另外,RunableTask可以继承其他类。但是上面介绍的两种方式都有一个缺点,就是任务没有返回值。下面看最后一种,即使用FutureTask的方式。
如上代码中的CallerTask类实现了Callable接口的call()方法。在main函数内首先创建了一个FutrueTask对象(构造函数为CallerTask的实例),然后使用创建的FutrueTask对象作为任务创建了一个线程并且启动它,最后通过 futureTask.get()等待任务执行完毕并返回结果。
小结:使用继承方式的好处是方便传参,你可以在子类里面添加成员变量,通过set方法设置参数或者通过构造函数进行传递,而如果使用Runnable方式,则只能使用主线程里面被声明为final的变量。不好的地方是Java不支持多继承,如果继承了Thread类,那么子类不能再继承其他类,而Runable则没有这个限制。前两种方式都没办法拿到任务的返回结果,但是Futuretask方式可以。
1.3 线程通知与等待
Java中的Object类是所有类的父类,鉴于继承机制,Java把所有类都需要的方法放到了Object类里面,其中就包含本节要讲的通知与等待系列函数。
1.wait()函数
当一个线程调用一个共享变量的wait()方法时,该调用线程会被阻塞挂起,直到发生下面几件事情之一才返回: (1)其他线程调用了该共享对象的notify()或者notifyAll()方法;(2)其他线程调用了该线程的interrupt()方法,该线程抛出InterruptedExceptio ............
以上为书籍内容预览,如需阅读全文内容请下载EPUB源文件,祝您阅读愉快。
书云 Open E-Library » Java并发编程之美 - (EPUB全文下载)