先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
正文
this.state = NEW; // ensure visibility of callable
}
所以在后续的执行中就调用的是 callbale
对象的 call
方法执行,并将结果存储在运行的 FutureTask
中进行返回,正常获取。
如果传入的类型是 Runnable
,同样调用 newTaskFor
方法生成 FutureTask
对象。
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
public static Callable callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter(task, result);
}
static final class RunnableAdapter implements Callable {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
可以看到,经过一系列的转折,最终是转换成一个 RunnableAdapter
,这个 RunnableAdapter
的值就是传入的 result
,没传入就是 null
。
针对 Runnable
类型参数概括一下,这段可能比较绕,所以多结合源码理解下过程:
submit
方法中传入Runnable
类型,一般为了获取结果,会将Callable
对象构建成FutureTask
类型在传入,(此处记作FutureA
);- 调用
newTaskFor
方法生成FutureTask
对象(记作FutureB
),这个对象就是我们submit
方法返回的Future
对象; - 在
FutureTask
的构造方法中调用Executors.callable(runnable, result)
方法构建一个Callable
对象存储在FutureTask
(即FutureB
) 的成员变量callable
中。其中result
默认为null
,由于传入的是Runnable
类型,所以在构建的时候是通过新建一个Callable
的子类RunnableAdapter
进行封装。 - 当
task
任务经过入队成功开始执行的时候,就是执行的callable
的call
方法。由于当前的Callable
对象是RunnableAdapter
类型,所以最终是调用传入的Runnable
(FutureTask
类型)的run
方法,并且返回值是result
。 - 经过这样的一波三折,最终回到构建原始的
FutureTask
的Callable
中调用call
方法,计算结果就被存储在传入作为参数的FutureTask
中,而返回值的Future
结果就是result
。
所以在 FutureTask + Callable
结合使用时,如果通过 submit
返回值来获取计算结果就会出现为 null
的情况。
在 ThreadPoolExecutor
的介绍中,我们针对 execute
进行了大致的流程介绍,并没有涉及到实际的执行流程,所以在这里我们针对 submit
方法的执行捋一遍流程。
2. addWorker
private boolean addWorker(Runnable firstTask, boolean core) {
/**
- 此处省略 n 行代码,它们的主要作用是判断线程池的状态是否是运行状态,以及线程数是否超标。
- 如果线程池是运行状态,并且线程没超标,则往下执行创建线程。
*/
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 将 firstTask 封装成 Worker对象。
w = new Worker(firstTask);
// 获取封装的任务线程
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 重新检查线程池的状态
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
// 线程池是运行状态 或 是关闭状态但是没任务,则添加 work 到集合
workers.add(w);
// 获取添加后的集合大小,修改 poolSize 的值
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
// 成功添加,开启线程执行
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
addWorkder
的逻辑大致可以分为以下步骤:
- 它们的主要作用是判断线程池的状态是否是运行状态,以及线程数是否超标。如果线程池是运行状态,并且线程没超标,则往下执行创建线程。
- 创建
Worker
对象。 - 添加
Worker
对象到集合。 - 获取
Worker
线程并执行。
我们想要获得最终的执行转换,如何转到我们定义的接口,就需要扒下 Worker
的外衣来看看。
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
private static final long serialVersionUID = 6138294804551838833L;
/** Thread this worker is running in. Null if factory fails. /
final Thread thread;
/* Initial task to run. Possibly null. /
Runnable firstTask;
/* Per-thread task counter */
volatile long completedTasks;
/**
- Creates with given first task and thread from ThreadFactory.
- @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker */
public void run() {
runWorker(this);
}
// 省略 n 行代码
}
首先需要明确 Worker
类是 ThreadPoolExecutor
的内部类。Worker
类是集成 AbstractQueuedSynchronizer
的子类,AbstractQueuedSynchronizer
应该都熟悉了(前面我们在并发编程锁的系列介绍过),同时实现了 Runnable
接口。它的内部包含一个 Runnable
和 Thread
对象,而这个 Thread
对象是通过创建。
this.thread = getThreadFactory().newThread(this);
将自身作为一个参数进行创建。getThreadFactory()
方法 ThreadPoolExecutor
提供的获取 ThreadFactory
方法,最终的实现是在 Executor
的内部类 DefaultThreadFactory
中进行实现。
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = “pool-” +
poolNumber.getAndIncrement() +
“-thread-”;
}
public Thread newThread(Runnable r) {
// 将我们的任务 Runnable 对象作为参数常见 Thread
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
// 判断是否是守护线程,如果是守护线程,设为为 false,让它不是守护线程
if (t.isDaemon())
t.setDaemon(false);
// 设置线程的优先级为普通
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
这样 Worker
以自身为参数创建一个线程,当线程启动的时候就会执行 worker
的 run
方法。最终执行到 runWorker(this)
。
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
// 获取 Worker 封装的 task 任务
Runnable task = w.firstTask;
// 销毁 workder 对象的 Runnable 引用,并释放锁
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
// 在任务执行前,先获取锁,确保线程执行的时候线程池不被打断
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
// 在 task 执行前,调用 beforeExecute 方法抛出异常,task 不会执行。该方法是空。
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
这里的关键方法 task.run()
,由于 task
是 FutureTask
类型,所以程序运行到 FutureTask
的 run
方法中。
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 调用自定义的 callable 对象的 call 方法,获取计算结果
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
// 设置计算结果返回值
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
最终,所有的执行和结果存储都回归到 FutureTask
中。
至此,整个的流程逻辑分析完毕。
3. 简单应用
下面通过一些简单的实例模拟下如何使用,主要有:
Future + Callable
FutureTask + Callable
3.1 Future + Callable
public class HelloWorld {
Future feature;
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Callables callable = new Callables();
Future future = executorService.submit(callable);
executorService.shutdown();
try {
System.out.println(“主线程获取结果:” + future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
static class Callables implements Callable{
@Override
public Integer call() throws Exception {
Thread.sleep(1000);
int sum =0;
for(int i=0;i<100;i++) {
sum += i;
}
System.out.println(“子线程计算结果:” + sum);
return sum;
}
}
}
执行结果:
子线程计算结果:4950
主线程获取结果:4950
3.2 FutureTask + Callable
public class HelloWorld {
Future feature;
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Callables callable = new Callables();
FutureTask futureTask = new FutureTask<>(callable);
Future<?> future = executorService.submit(futureTask);
executorService.shutdown();
try {
System.out.println(“主线程获取结果:” + future.get() + “==” + futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
static class Callables implements Callable{
@Override
public Integer call() throws Exception {
Thread.sleep(1000);
int sum =0;
for(int i=0;i<100;i++) {
sum += i;
}
System.out.println(“子线程计算结果:” + sum);
return sum;
}
}
}
执行结果:
子线程计算结果:4950
主线程获取结果:null==4950
可以看到,我们通过 submit 返回的 Future 获取的结果是 null。
这篇文章之后,并发编程的文章也算告一段落,当然还有很多没有涉及到,后面有时间在继续吧。
最后
即使是面试跳槽,那也是一个学习的过程。只有全面的复习,才能让我们更好的充实自己,武装自己,为自己的面试之路不再坎坷!今天就给大家分享一个Github上全面的Java面试题大全,就是这份面试大全助我拿下大厂Offer,月薪提至30K!
我也是第一时间分享出来给大家,希望可以帮助大家都能去往自己心仪的大厂!为金三银四做准备!
一共有20个知识点专题,分别是:
Dubbo面试专题
JVM面试专题
Java并发面试专题
Kafka面试专题
MongDB面试专题
MyBatis面试专题
MySQL面试专题
Netty面试专题
RabbitMQ面试专题
Redis面试专题
Spring Cloud面试专题
SpringBoot面试专题
zookeeper面试专题
常见面试算法题汇总专题
计算机网络基础专题
设计模式专题
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
转存中…(img-mvym6Yzq-1713607907230)]
常见面试算法题汇总专题
[外链图片转存中…(img-U1OBs1tu-1713607907231)]
计算机网络基础专题
[外链图片转存中…(img-bh1fcZwr-1713607907231)]
设计模式专题
[外链图片转存中…(img-6FaD8ekO-1713607907232)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-Z9RlT8tm-1713607907232)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!