Java学习笔记——初步了解线程池

等待唤醒机制

在⼀个线程进⾏了规定操作后,就进⼊等待状态( wait() ),等待其他线程执⾏完他们的指定
代码过后再将其唤醒( notify() );在有多个线程进⾏等待时,如果需要,可以使⽤ notifyAll() 来
唤醒所有的等待线程。
wait/notify 就是线程间的⼀种协作机制。

等待唤醒中的⽅法

等待唤醒机制就是⽤于解决线程间通信的问题的,使⽤到的3个⽅法的含义如下:

  1. wait:线程不再活动,不再参与调度,进⼊ wait set 中,因此不会浪费 CPU 资源,也不会
    去竞争锁了,这时的线程状态即是 WAITING。它还要等着别的线程执⾏⼀个特别的动作,
    也即是“通知( notify )”在这个对象上等待的线程从 wait set 中释放出来,重新进⼊到调度
    队列( ready queue )中。
  2. notify:则选取所通知对象的 wait set 中的⼀个线程释放;例如,餐馆有空位置后,等候就
    餐最久的顾客最先⼊座。
  3. notifyAll:则释放所通知对象的 wait set 上的全部线程。

注意:

哪怕只通知了⼀个等待的线程,被通知线程也不能⽴即恢复执⾏,因为它当初中断的地⽅
是在同步块内,⽽此刻它已经不持有锁,所以它需要再次尝试去获取锁(很可能⾯临其它
线程的竞争),成功后才能在当初调⽤ wait ⽅法之后的地⽅恢复执⾏。

总结如下:

如果能获取锁,线程就从 WAITING 状态变成 RUNNABLE 状态;
否则,从 wait set 出来,⼜进⼊ entry set,线程就从 WAITING 状态⼜变成
BLOCKED 状态。
调⽤wait和notify⽅法需要注意的细节

  1. wait⽅法与notify⽅法必须要由同⼀个锁对象调⽤。因为:对应的锁对象可以通过notify唤
    醒使⽤同⼀个锁对象调⽤的wait⽅法后的线程。
  2. wait⽅法与notify⽅法是属于Object类的⽅法的。因为:锁对象可以是任意对象,⽽任意对
    象的所属类都是继承了Object类的。
  3. wait⽅法与notify⽅法必须要在同步代码块或者是同步函数中使⽤。因为:必须要通过锁对
    象调⽤这2个⽅法。

线程池

线程池概念
线程池:其实就是⼀个容纳多个线程的容器,其中的线程可以反复使⽤,省去了频繁创建线
程对象的操作,⽆需反复创建线程⽽消耗过多资源。
由于线程池中有很多操作都是与优化资源相关的,我们在这⾥就不多赘述。我们通过⼀张图来了
解线程池的⼯作原理:

在这里插入图片描述

合理利⽤线程池能够带来三个好处:

  1. 降低资源消耗。减少了创建和销毁线程的次数,每个⼯作线程都可以被重复利⽤,可执⾏多
    个任务。
  2. 提⾼响应速度。当任务到达时,任务可以不需要的等到线程创建就能⽴即执⾏。
  3. 提⾼线程的可管理性。可以根据系统的承受能⼒,调整线程池中⼯作线线程的数⽬,防⽌因
    为消耗过多的内存,⽽把服务器累趴下(每个线程需要⼤约1MB内存,线程开的越多,消
    耗的内存也就越⼤,最后死机)。

线程池的使⽤
Java⾥⾯线程池的顶级接⼝是 java.util.concurrent.Executor ,但是严格意义上讲 Executor并不是⼀个线程池,⽽只是⼀个执⾏线程的⼯具。真正的线程池接⼝是 java.util.concurrent.ExecutorService 。要配置⼀个线程池是⽐较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优 的,因此在 java.util.concurrent.Executors 线程⼯⼚类⾥⾯提供了⼀些静态⼯⼚,⽣成⼀些常⽤的线程池。官 ⽅建议使⽤ Executors ⼯程类来创建线程池对象。

Executors 类中有个创建线程池的⽅法如下:

public static ExecutorService newFixedThreadPool(int nThreads) :返回线程池对象。(创建的是有界线程池,也就是池中的线程个数可以指定最⼤数量)

获取到了⼀个线程池 ExecutorService 对象,那么怎么使⽤呢,在这⾥定义了⼀个使⽤线程池对象的⽅法如下:

public Future<?> submit(Runnable task) :获取线程池中的某⼀个线程对象,并执⾏

Future接⼝:⽤来记录线程任务执⾏完毕后产⽣的结果。线程池创建与使⽤

使⽤线程池中线程对象的步骤:

  1. 创建线程池对象。
  2. 创建Runnable接⼝⼦类对象。(task)
  3. 提交Runnable接⼝⼦类对象。(take task)
  4. 关闭线程池(⼀般不做)。

Runnable实现类代码:

public class MyRunnable implements Runnable {
 @Override
 public void run() {
 System.out.println("我要⼀个教练");
 try {
 Thread.sleep(2000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 System.out.println("教练来了:" + Thread.currentThread().getName());
 System.out.println("教我游泳,交完后,教练回到了游泳池");
 }
}

线程池测试类:

public class ThreadPoolDemo {
 public static void main(String[] args) {
 // 创建线程池对象
 ExecutorService service = Executors.newFixedThreadPool(2); // 包含2个线
程对象
 // 创建Runnable实例对象
 MyRunnable r = new MyRunnable();

 // ⾃⼰创建线程对象的⽅式
 // Thread t = new Thread(r);
 // t.start(); ---> 调⽤MyRunnable中的run()

 // 从线程池中获取线程对象,然后调⽤MyRunnable中的run()
 service.submit(r);
 // 再获取个线程对象,调⽤MyRunnable中的run()
 service.submit(r);
 service.submit(r);
 // 注意:submit⽅法调⽤结束后,程序并不终⽌,是因为线程池控制了线程的关闭。
 // 将使⽤完的线程⼜归还到了线程池中
 // 关闭线程池
 // service.shutdown();
 }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值