线程池(一):阻塞队列

}

}

public static void main(String args[]) {

ThreadCallAble mCall = new ThreadCallAble();

ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();

Future mFuture = singleThreadPool.submit(mCall);

try {

Log.d(“hh”, "mFuture.get() = " + mFuture.get());

} catch (ExecutionException e) {

e.printStackTrace();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

(1)Callable可以在任务接受后提供一个返回值,Runnable无法提供这个功能。

(2)Callable中的call()方法可以抛出异常,而Runnable的run()方法不能抛出异常。

(3)运行Callable可以拿到一个Future对象,Future对象表示异步计算的结果,它提供了检查计算是否 完成的方法。由于线程属于异步计算模型,因此无法从别的线程中得到函数的返回值,在这种情况下就可 以使用 Future 来监视目标线程调用 call()方法的情况。但调用 Future的get()方法以获取结果时,当前 线程就会阻塞,直到call()方法返回结果。

线程的终止

当线程的run方法执行完毕,或者在方法中出现没有捕获的异常时,线程将终止。

在Java早期版本中有 一个stop方法,其他线程可以调用它终止线程,但是这个方法现在已经被弃用了。

interrupt 方法可以用来请 求中断线程。当一个线程调用 interrupt 方法时,线程的中断标识位将被置位(中断标识位为true),线程会 不时地检测这个中断标识位,以判断线程是否应该被中断。要想知道线程是否被置位,可以调用 Thread.currentThread().isInterrupted()

调用**Thread.interrupted()**来对中断标识位进行复位。

如果一个线程被阻塞,就无法检测中 断状态。如果一个线程处于阻塞状态,线程在检查中断标识位时如果发现中断标识位为true,则会在阻塞方 法调用处抛出InterruptedException异常,并且在抛出异常前将线程的中断标识位复位,即重新设置为 false。

需要注意的是被中断的线程不一定会终止,中断线程是为了引起线程的注意,被中断的线程可以决定如何 去响应中断。所以如果要终止进程,就需要退出run方法

线程 同步

在多线程应用中,两个或者两个以上的线程需要 共享对同一个数据的存取。

synchronized

volatile

阻塞队列

阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元 素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素

阻塞队列有两个常见的阻塞场景,它们分别是:

(1)当队列中没有数据的情况下,消费者端的所有线程都会被自动阻塞(挂起),直到有数据放入队列。

(2)当队列中填满数据的情况下,生产者端的所有线程都会被自动阻塞(挂起),直到队列中有空的 位置,线程被自动唤醒。

支持以上两种阻塞场景的队列被称为阻塞队列。

BlockingQueue的核心方法 放入数据:
  • offer(anObject):表示如果可能的话,将anObject加到BlockingQueue里。即如果BlockingQueue可以 容纳,则返回true,否则返回false。(本方法不阻塞当前执行方法的线程。)

  • offer(E o,long timeout,TimeUnit unit):可以设定等待的时间。如果在指定的时间内还不能往队列 中加入BlockingQueue,则返回失败。 * put(anObject):将anObject加到BlockingQueue里。如果BlockQueue没有空间,则调用此方法的线程 被阻断,直到BlockingQueue里面有空间再继续。 获取数据:

  • poll(time):取走 BlockingQueue 里排在首位的对象。若不能立即取出,则可以等 time参数规定的时 间。取不到时返回null。

  • poll(long timeout,TimeUnit unit):从BlockingQueue中取出一个队首的对象。如果在指定时间内, 队列一旦有数据可取,则立即返回队列中的数据;否则直到时间超时还没有数据可取,返回失败。

  • take():取走BlockingQueue里排在首位的对象。若BlockingQueue为空,则阻断进入等待状态,直 到 BlockingQueue有新的数据被加入。

  • drainTo():一次性从BlockingQueue获取所有可用的数据对象(还可以指定获取数据的个数)。通 过该方法,可以提升获取数据的效率;无须多次分批加锁或释放锁。

在Java中提供了7个阻塞队列,它们分别如下所示。

• ArrayBlockingQueue:由数组结构组成的 有界 阻塞队列。

• LinkedBlockingQueue:由链表结构组成的 有界 阻塞队列。

• PriorityBlockingQueue:支持优先级排序的 无界 阻塞队列。

• DelayQueue:使用优先级队列实现的 无界 阻塞队列。

• SynchronousQueue:不存储元素的阻塞队列。

• LinkedTransferQueue:由链表结构组成的 无界 阻塞队列。

• LinkedBlockingDeque:由链表结构组成的 双向 阻塞队列

import java.util.concurrent.ArrayBlockingQueue;

/**

  • @author 付影影

  • @desc 阻塞队列

  • @date 2019/10/15

*/

public class BlockingQueueTest {

private int queueSize = 10;

private ArrayBlockingQueue mArrayBlockingQueue = new ArrayBlockingQueue<>(queueSize);

public static void main(String[] args) {

BlockingQueueTest mBlockingQueue = new BlockingQueueTest();

Consumer consumer = mBlockingQueue.new Consumer();

Producer producer = mBlockingQueue.new Producer();

consumer.start();

producer.start();

}

/**

  • 使用阻塞队列实现 生产者和消费者模式,无需考虑同步和线程间同步的问题

*/

class Consumer extends Thread {

@Override

public void run() {

while (true) {

try {

mArrayBlockingQueue.take();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

class Producer extends Thread {

@Override

public void run() {

while (true) {

try {

mArrayBlockingQueue.put(1);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

}

线程池

在编程中经常会使用线程来异步处理任务,但是每个线程的创建和销毁都需要一定的开销。如果每次 执行一个任务都需要开一个新线程去执行,则这些线程的创建和销毁将消耗大量的资源;并且线程都是“各 自为政”的,很难对其进行控制,更何况有一堆的线程在执行。这时就需要线程池来对线程进行管理

可以通过ThreadPoolExecutor来创建一个线程池,

ThreadPoolExecutor类一共有4个构造方法。其中,拥 有最多参数的构造方法如下所示:

这些参数的作用如下所示。

corePoolSize:核心线程数。默认情况下线程池是空的,只有任务提交时才会创建线程。如果当前运 行的线程数少于corePoolSize,则创建新线程来处理任务;如果等于或者多于corePoolSize,则不再创建。如 果调用线程池的prestartAllcoreThread方法,线程池会提前创建并启动所有的核心线程来等待任务。

maximumPoolSize:线程池允许创建的最大线程数。如果任务队列满了并且线程数小于 maximumPoolSize时,则线程池仍旧会创建新的线程来处理任务。

keepAliveTime:非核心线程闲置的超时时间。超过这个时间则回收。如果任务很多,并且每个任务 的执行事件很短,则可以调大keepAliveTime来提高线程的利用率。另外,如果设置 allowCoreThreadTimeOut属性为true时,keepAliveTime也会应用到核心线程上,

TimeUnit:keepAliveTime参数的时间单位。可选的单位有天(DAYS)、小时(HOURS)、分钟 (MINUTES)、秒(SECONDS)、毫秒(MILLISECONDS)等。

workQueue:任务队列。如果当前线程数大于corePoolSize,则将任务添加到此任务队列中。该任务 队列是BlockingQueue类型的,也就是阻塞队列。

ThreadFactory:线程工厂。可以用线程工厂给每个创建出来的线程设置名字。一般情况下无须设置 该参数。

RejectedExecutionHandler:饱和策略。这是当任务队列和线程池都满了时所采取的应对策略,默认 是AbordPolicy,表示无法处理新任务,并抛出RejectedExecutionException异常。

此外还有3种策略,它们分 别如下。

(1)CallerRunsPolicy:用调用者所在的线程来处理任务。此策略提供简单的反馈控制机制,能够减缓 新任务的提交速度。 (2)DiscardPolicy:不能执行的任务,并将该任务删除。

(3)DiscardOldestPolicy:丢弃队列最近的任务,并执行当前的任务。

线程池执行流程

image

(1)如果线程池中的线程数未达到核心线程数,则创建核心线程处理任务。

(2)如果线程数大于或者等于核心线程数,则将任务加入任务队列,线程池中的空闲线程会不断地从 任务队列中取出任务进行处理。

(3)如果任务队列满了,并且线程数没有达到最大线程数,则创建非核心线程去处理任务。

(4)如果线程数超过了最大线程数,则执行饱和策略

线程池的种类

通过直接或者间接地配置ThreadPoolExecutor的参数可以创建不同类型的ThreadPoolExecutor,其中有 4 种线程池比较常用,它们分别是 FixedThreadPool、CachedThreadPool、SingleThreadExecutor和 ScheduledThreadPool

/**

*线程池

*/

class ThreadPoolActivity : AppCompatActivity() {

lateinit var fixThreadPool: ExecutorService

lateinit var cacheThreadPool: ExecutorService

lateinit var singleThreadPool: ExecutorService

lateinit var scheduledThreadPool: ExecutorService

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_thread_pool)

//固定核心线程和最大线程个数,阻塞队列无界

fixThreadPool = Executors.newFixedThreadPool(5)

//没有核心线程 最大线程个数不限制,阻塞队列不存储

cacheThreadPool = Executors.newCachedThreadPool(object : ThreadFactory {

var count = 0

override fun newThread(r: Runnable?): Thread {

Log.d(“hh”, “新开的线程: newThreadPool_$count”)

val thread = Thread(r, “newThreadPool_${count++}”)

thread.setUncaughtExceptionHandler { t, e ->

//处理非正常的线程中止,多线程中通过trycatch试图捕获线程的异常是不可取的

Log.d(“hh”, t.name)

e.printStackTrace()

}

return thread

}

})

//核心线程和最大线程个数 只有一个,阻塞队列无界

singleThreadPool = Executors.newSingleThreadExecutor()

//定时任务的线程池

scheduledThreadPool = Executors.newScheduledThreadPool(5)

tvFixThreadPool.setOnClickListener {

for (i in 0…10) {

cacheThreadPoolExecutor(Runnable {

Log.d(“hh”, “i = $i”)

})

}

}

ivDownImg.setOnClickListener {

//downLoadImg(“https://y3.cnliveimg.com:8080/image/itv/2016/1112/7e151ac5d7f84f58a4653e879c973192_161409_100.jpg”)

arrayBlockingQueue()

}

}

/**

  • 线程池 调用

*/

private fun cacheThreadPoolExecutor(r: Runnable) {

cacheThreadPool.execute®

}

/**

  • 下载图片

*/

private fun downLoadImg(path: String) {

//它有3个泛型参数,分别为Params、Progress和Result,

// 其中Params为 参数类型,

// Progress为后台任务执行进度的类型,

// Result为返回结果的类型。

// 如果不需要某个参数,可以将 其设置为Void类型

val asynctask =

最后

那我们该怎么做才能做到年薪60万+呢,对于程序员来说,只有不断学习,不断提升自己的实力。我之前有篇文章提到过,感兴趣的可以看看,到底要学习哪些知识才能达到年薪60万+。

通过职友集数据可以查看,以北京 Android 相关岗位为例,其中 【20k-30k】 薪酬的 Android 工程师,占到了整体从业者的 30.8%!

北京 Android 工程师「工资收入水平 」

今天重点内容是怎么去学,怎么提高自己的技术。

1.合理安排时间

2.找对好的系统的学习资料

3.有老师带,可以随时解决问题

4.有明确的学习路线

当然图中有什么需要补充的或者是需要改善的,可以在评论区写下来,一起交流学习。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
过,感兴趣的可以看看,到底要学习哪些知识才能达到年薪60万+。

通过职友集数据可以查看,以北京 Android 相关岗位为例,其中 【20k-30k】 薪酬的 Android 工程师,占到了整体从业者的 30.8%!

北京 Android 工程师「工资收入水平 」

[外链图片转存中…(img-m9kgs8ov-1715354749854)]

今天重点内容是怎么去学,怎么提高自己的技术。

1.合理安排时间

2.找对好的系统的学习资料

3.有老师带,可以随时解决问题

4.有明确的学习路线

当然图中有什么需要补充的或者是需要改善的,可以在评论区写下来,一起交流学习。

[外链图片转存中…(img-YHZpnYsc-1715354749855)]

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 27
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值