并发编程简介
Jdk 1.5 之前,我们必须自己编写代码实现一些并发编程的逻辑,之后到了Jdk 1.5 ,DougLea解救了广大Java程序员,在Jdk里特意设计并实现了一套JUC的框架,给大家提供了非常好的并发编程体验。Java并发编程涉及的知识点比较多,多线程编程所考虑的场景相对比较复杂,包括线程间的资源共享、竞争、死锁等问题。
涉及到源码的阅读
1、为啥要看源码
源于几个场景:
- 对于刚入职场的新手,不知道如何去设计,来一个需求 需要做概要设计,不知道如何下手,不得不去看当前系统类似的需求是如何设计的,然后模仿去设计
- 即使是职场中工作一段时间的人,设计的时候,考虑问题也会不周全,虽然相对于新手这类人对一个需求依靠自己的经验已经能够拿出一个概要设计,但是设计中经常会遗漏一些异常细节,比如使用多线程有界队列执行任务,遇到机器宕机了,如果队列里面的任务不存盘处理,那么机器下次启动的时候这些任务就会丢失了。
这些问题,说到底主要还是因为经验不够,而经验主要从项目实践中积累,所有招聘单位一般都会限制工作时间大于3年,因为这些人的项目经验相对丰富,在项目中遇到的场景也较多,工作经验的积累来源年限与实践,而通过看源码则可以拓展我们的思路,这个是变相增加我们的经验,虽然不能在短时间内通过时间来积累经验,但是可以通过学习开源框架、开源项目来获取别人的经验。
另外,进入职场后,一般都需要先熟悉现有系统,如果有文档还好,没有文档就得自己去翻阅代码研究。如果之前你对阅读源码有经验,那么在研究新系统的代码逻辑时就不会那么吃力,那么费劲、头大!!!!!
还有一点,当你使用框架或工具做开发时,如果你对它的实现有所了解,就可以最大化地减少出bug的可能,比如:
并发队列ArrayBlockingQueue里面的关于元素添加有2个方法offer方法和put方法,虽然某个时间点 你知道使用offer方法时,当队列满足了就会丢弃要入队的元素,之后offer方法会返回fasle,而不会阻塞当前的线程;而使用put方法时,当队列满了,则会挂起当前线程,直到队列有空闲,元素入队成功后,但是我们常会遗忘,一段时间不使用,就会忘记它们的区别,当你再次去使用时,需要进入offer方法和put方法的内部,看它们的源码实现。进offer方法一看,原来队列满了后直接返回了false,进入put方法一看,原来队列满后,直接使用条件变量的wait方法挂起了当前线程。知道2者的区别,你就可以根据自己的需求来选择。
/**
* Inserts the specified element at the tail of this queue if it is
* possible to do so immediately without exceeding the queue's capacity,
* returning {@code true} upon success and {@code false} if this queue
* is full. This method is generally preferable to method {@link #add},
* which can fail to insert an element only by throwing an exception.
*
* @throws NullPointerException if the specified element is null
*/
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
enqueue(e);
return true;
}
} finally {
lock.unlock();
}
}
/**
* Inserts the specified element at the tail of this queue, waiting
* for space to become available if the queue is full.
*
* @throws InterruptedException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
enqueue(e);
} finally {
lock.unlock();
}
}
看源码最大的好处是可以开阔思维,提升架构设计能力。有些东西仅靠书本和自己的思考是很难学到的,必须通过看源码,看别人如何设计,然后思考为何这样设计才能领悟到,能力的提高不在于你写了多少代码,做了多少项目,而在于给你一个业务场景时,你是否能拿出几个靠谱的解决方案,并且说出各自的优缺点,还有就是面对运维中出现的问题如何快速定位问题并解决,而如何才能具有这些能力,
一来靠经验,二来靠归纳总结,二看源码可以快速增加你的经验。
业务需求设计能力和业务问题的解决能力
2、如何看源码
- 在你看某一个框架的源码前,先去Google查找这个开源框架的官方介绍,通过资料了解该框架有几个模块,各个模块是做什么的,之间有什么联系,每个模块都有哪些核心类,在阅读源码时候可以重点看这些类。
- 然后对哪个模块感兴趣就去写个小demo,先了解一下这个模块的具体使用,然后再debug进去看具体的实现细节,在debug的过程中,第一遍是走马观花,简单的看一下主要调用逻辑,都用到了哪些类,第二遍需要重点的debug,看看这些类担任了架构图中的哪些功能,使用了哪些设计模式。如果第二遍有感觉了,便大致知道了整体代码的功能实现,但是对整体代码结构还不是很清楚,毕竟代码里面多个类来回调用,很容易遗忘当前断点的来处,那么你可以进行第三遍debug,这个时候你最好把主要类的调用时序图以及类图结构画出来,有全局把控,等画好了,再对着时序图分析调用流程,就可以清楚地知道类之间的调用关系,而通过类图可以知道类的功能以及它们之间的依赖关系。
- 另外,开源框架里面的每个功能类或方法一般都是有注释的,这些注释是一手资料,比如JUC包里的一些并发组件的注释,就已经说明了它们的设计原理和使用场景,
- 在阅读源码时,最后画时序图和类图,便于回查,因为人容易遗忘一些东西,如果隔一段时间你再去看之前的看过的源码,虽然有些印象,但当你想去看某个模块的逻辑时,又得需要根据demo再从头debug了,而如果有了时序图和类图,就可以从中直接查找,并且看一眼时序图就知道整个模块的脉络了。
- 查框架使用说明最后去官方网站(这些信息时源头,是没有经过被别人翻译的),虽然是英文,但看久了就好了,毕竟还有类似Google翻译的软件!
- 研究源码时不一定非要debug几遍,上面说的只是对应源码掌握的三种程度,如果你debug一遍或者不debug就能掌握,那完全可以不需要好几遍。
Java并发编程基础
并发编程线程基础
线程与进程
线程创建与运行:
3种方式
package com.allen.base.day01;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class ThreadCreate {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
RunnableTask task = new RunnableTask();
new Thread(task).start();
new Thread(task).start();
FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
new Thread(futureTask).start();
String result = null;
try {
result = futureTask.get();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
public static class MyThread extends Thread {
@Override
public void run() {
System.out.println(this + "I am a Thread.......");
}
}
public static class RunnableTask implements Runnable {
@Override
public void run() {
System.out.println(this + "I am a RunnableTask.......");
}
}
public static class CallerTask implements Callable<String> {
@Override
public String call() throws Exception {
return "hello";
}
}
}
并发编程其他概念与原理解析