进程是系统资源分配的最小单位。由 文本区域
,数据区域
和堆栈
组成。
涉及问题: cpu抢占
,内存分配(虚拟内存/物理内存)
,以及进程间通信
。
线程是操作系统能够进行运算调度的最小单位。
一个进程可以包括多个线程,线程共用
进程所分配到的资源空间
涉及问题: 线程状态
,并发问题
,锁
子例程
: 某个主程序的一部分代码,也就是指某个方法,函数。
维基百科:执行过程类似于 子例程
,有自己的上下文,但是其切换由自己控制。
常用的方式主要由上述3种,需要注意的是 使用
,而不是创建线程,从实现的代码我们可以看到,Java 创建线程只有一种方式, 就是通过 new Thread()
的方式进行创建线程。
Thread()
,Runnable()
与 Callable()
之间的区别Thread
需要继承,重写run()
方法,对拓展不友好,一个类即一个线程任务。
Runnbale
通过接口的方式,可以实现多个接口,继承父类。需要创建一个线程进行装载任务执行。
Callable
JDK1.5 后引入, 解决 Runnable 不能返回结果或抛出异常的问题。需要结合 ThreadPoolExecutor
使用。
Thread.run()
和 Thread.start()
的区别Thread.run()
输出结果:
Thread.start()
输出结果:
线程状态,也称为线程的生命周期, 主要可以分为: 新建
,就绪
,运行
,死亡
,堵塞
等五个阶段。
图片引用 芋道源码
新建状态比较好理解, 就是我们调用 new Thread()
的时候所创建的线程类。
就绪状态指得是:
1、当调用 Thread.start
时,线程可以开始执行, 但是需要等待获取 cpu 资源。区别于 Thread.run
方法,run
方法是直接在当前线程进行执行,沿用其 cpu 资源。
2、运行状态下,cpu 资源
使用完后,重新进入就绪状态,重新等待获取 cpu 资源
. 从图中可以看到,可以直接调用Thread.yield
放弃当前的 cpu资源,进入就绪状态。让其他优先级更高的任务优先执行。
在步骤2
就绪状态中,获取到 cpu资源
后,进入到运行状态, 执行对应的任务,也就是我们实现的 run()
方法。
1、正常任务执行完成,run() 方法执行完毕
2、异常退出,程序抛出异常,没有捕获
阻塞主要分为: io等待,锁等待,线程等待 这几种方式。通过上述图片可以直观的看到。
io等待: 等待用户输入,让出cpu资源,等用户操作完成后(io就绪),重新进入就绪状态。
锁等待:同步代码块需要等待获取锁,才能进入就绪状态
线程等待: sleep()
, join()
和 wait()/notify()
方法都是等待线程状态的阻塞(可以理解成当前线程的状态受别的线程影响)
池化技术,主要是为了减少每次资源的创建,销毁所带来的损耗,通过资源的重复利用提高资源利用率而实现的一种技术方案。常见的例如: 数据库连接池,http连接池以及线程池等。都是通过池同一管理,重复利用,从而提高资源的利用率。
使用线程池的好处:
Executors 可以比较快捷的帮我们创建类似 FixedThreadPool ,CachedThreadPool 等类型的线程池。
存在的弊端:
构造函数:
几个核心的参数:
corePoolSize
: 核心线程数maximumPoolSize
: 最大线程数keepAliveTime
: 线程空闲存活时间unit
: 时间单位workQueue
: 等待队列threadFactory
: 线程工厂handler
: 拒绝策略与上述的 ExecutorService.newSingleThreadExecutor
等多个api
进行对比,可以比较容易的区分出底层的实现是依赖于 BlockingQueue
的不同而定义的线程池。
主要由以下几种的阻塞队列:
ArrayBlockingQueue
,队列是有界的,基于数组实现的阻塞队列LinkedBlockingQueue
,队列可以有界,也可以无界。基于链表实现的阻塞队列 对应了: Executors.newFixedThreadPool()
的实现。SynchronousQueue
,不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作将一直处于阻塞状态。对应了:Executors.newCachedThreadPool()
的实现。PriorityBlockingQueue
,带优先级的无界阻塞队列拒绝策略主要有以下4种:
CallerRunsPolicy
: 在调用者线程执行AbortPolicy
: 直接抛出RejectedExecutionException异常DiscardPolicy
: 任务直接丢弃,不做任何处理DiscardOldestPolicy
: 丢弃队列里最旧的那个任务,再尝试执行当前任务往线程池中提交任务,主要有两种方法,execute()
和submit()
1、 execute()
无返回结果,直接执行任务
2、submit()
submit()
会返回一个 Future
对象,用于获取返回结果,常用的api 有 get()
和 get(timeout,unit)
两种方式,常用于做限时处理
ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多。可能很多朋友都知道ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
Semaphore ,是一种新的同步类,它是一个计数信号. 使用示例代码:
可以理解成是一个栅栏,需要等所有的线程都执行完成后,才能继续往下走。
CountDownLatch
默认的构造方法是 CountDownLatch(int count)
,其参数表示需要减少的计数,主线程调用 #await()
方法告诉 CountDownLatch
阻塞等待指定数量的计数被减少,然后其它线程调用 CountDownLatch
的 #countDown()
方法,减小计数(不会阻塞)。等待计数被减少到零,主线程结束阻塞等待,继续往下执行。
CyclicBarrier
与 CountDownLatch
有点相似, 都是让线程都到达某个点,才能继续往下走, 有所不同的是 CyclicBarrier
是可以多次使用的。 示例代码:
最后贴一个新生的公众号 (Java 补习课
),欢迎各位关注,主要会分享一下面试的内容(参考之前博主的文章),阿里的开源技术之类和阿里生活相关。 想要交流面试经验的,可以添加我的个人微信(Jayce-K
)进群学习~