【JUC 二】Callable Runnable 同步辅助类 读写锁 阻塞队列 线程池

}

}

8.3、Semaphore


信号量、信号灯

原理:

semaphore.acquire(); //获取信号量,假设已经满了,等待信号量可用时被唤醒

semaphore.release();//释放信号量

作用:

多个共享资源互斥的使用!

并发限流,控制最大的线程数

  • 示例

停车场

10个位,30辆车

package auxiliaryClass;

import java.util.Random;

import java.util.concurrent.Semaphore;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.ReentrantLock;

/**

  • @author ajun

  • Date 2021/7/5

  • @version 1.0

  • 信号灯:抢车位

*/

public class SemaphoreTest {

private static final int MAX_PARKING = 10;//最大停车位

private static int cars = 0;//当前停车数量

public static void main(String[] args) throws InterruptedException {

final Semaphore semaphore = new Semaphore(MAX_PARKING);//停车场

Random random = new Random();//计算随机停车时间

for (int i = 1; i <= 30; i++) {//30辆车

TimeUnit.SECONDS.sleep(1);//车辆进场间隔

int t = 10 + random.nextInt(5);//随机停车时间 10 - 15

new Thread(() -> {

try {

semaphore.acquire();//获得车位。如果没有车位,就阻塞等待。

System.out.println(Thread.currentThread().getName() + “号车进场!”);

carsNum(1);//车辆数加1

TimeUnit.SECONDS.sleep(t);//停车时间

System.out.println(Thread.currentThread().getName() + “号车离场了!停了 " + t + " 秒钟”);

carsNum(-1);//车辆数减1

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

semaphore.release();//释放车位

}

}, String.valueOf(i)).start();

}

}

//统计当前停车数量和剩余车位

public static void carsNum(int n) {

cars = cars + n;//当前车辆数

int pN = MAX_PARKING - cars;//剩余车位

System.out.println("\n【当前有 " + cars + " 辆车!剩余车位 " + pN + “个】\n”);

}

}

9、读写锁

=====

9.1、简介


ReadWriteLock

包括 读锁 和 写锁

  • 读锁
*   也叫 共享锁;一次可以有多个线程占有
  • 写锁
*   也叫 独占锁;一次只能由一个线程占有

9.2、用法


类似于 Lock 锁

private ReadWriteLock lock = new ReentrantReadWriteLock();//定义读写锁

lock.readLock().lock();//添加读锁

try {

//业务代码

} finally {

lock.readLock().unlock();//释放读锁

}

lock.writeLock().lock();//添加写锁

try {

//业务代码

} finally {

lock.writeLock().unlock();//释放写锁

}

9.3、示例


package readWriteLock;

import java.util.HashMap;

import java.util.Map;

import java.util.concurrent.locks.ReadWriteLock;

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**

  • @author ajun

  • Date 2021/7/5

  • @version 1.0

  • 读写锁

*/

public class Demo01 {

public static void main(String[] args) {

MyCacheLock myCache = new MyCacheLock();//缓存类

//循环写

for (int i = 1; i <= 10; i++) {

final int temp = i;

new Thread(() -> {

myCache.push(“k-”+temp,“v-”+temp);

},String.valueOf(i)).start();

}

//循环读

for (int i = 1; i <= 10; i++) {

final int temp = i;

new Thread(() -> {

myCache.get(“k-”+temp);

},String.valueOf(i)).start();

}

}

}

//自定义加锁缓存类

class MyCacheLock {

private volatile Map<String, String> map = new HashMap<>();

private ReadWriteLock lock = new ReentrantReadWriteLock();//读写锁

//读

public String get(String key) {

lock.readLock().lock();

String s=“”;

try {

System.out.println(Thread.currentThread().getName() + " 读取 " + key);

s = map.get(key);

System.out.println(Thread.currentThread().getName() + " 读取成功");

} finally {

lock.readLock().unlock();

}

return s;

}

//写

public void push(String key,String value){

lock.writeLock().lock();

try {

System.out.println(Thread.currentThread().getName() + " 写入 " + key);

map.put(key,value);

System.out.println(Thread.currentThread().getName() + " 写入成功");

} finally {

lock.writeLock().unlock();

}

}

}

10、阻塞队列

=======

10.1、简介


写入:如果队列满了,就阻塞,等待消费

读取:如果队列空了,就阻塞,等待生产

10.2、队列关系图


10.3、使用场景


  • 多线程并发处理
  • 线程池

10.4、四组API


ArrayBlockingQueue

LinkedBlockingQueue

| 方式 | 抛出异常 | 不会抛出异常 | 阻塞等待 | 超时不等待 |

| — | — | — | — | — |

| 添加操作 | add() | offer() 供应 | put() 无返回值 | offer(obj,int,timeunit.status) |

| 移除/获取 | remove() 移除 | poll() 获得 | take() | poll(int,timeunit.status) |

| 判断队列首部 | element() | peek() 偷看,偷窥 | | |

  • remove() :移除,返回元素 public E remove() {}
  • remove(Object o) :移除,返回boolean public boolean remove(Object o) {}
  • offer(obj,int,timeunit.status) :添加、供应
*   obj :需要添加的元素
*   int :等待的时间。如果超出时间添加不成功就退出
*   timeunit.status : 时间单位。如秒
  • poll(int,timeunit.status) :获取
*   int :等待的时间。如果超出时间获取不成功就退出
*   timeunit.status : 时间单位。如秒

1、第一组

add()、remove()、element()

操作不成功时会抛出异常

package blockingQueue;

import java.util.concurrent.ArrayBlockingQueue;

/**

  • @author ajun

  • Date 2021/7/6

  • @version 1.0

  • 阻塞队列四组API

*/

public class demo01 {

public static void main(String[] args) {

api1();

}

/*

add()、remove()、element()

操作不成功时会抛出异常

*/

public static void api1() {

//队列大小

ArrayBlockingQueue queue = new ArrayBlockingQueue<>(3);

//添加

System.out.println(queue.add(“a”));

System.out.println(queue.add(“b”));

System.out.println(queue.add(“c”));

//超出队列大小,第四个元素添加不成功,抛出异常

//java.lang.IllegalStateException

//System.out.println(queue.add(“d”));

System.out.println(“-------------”);

System.out.println(“队首” + queue.element());

System.out.println(queue.remove());

System.out.println(“队首” + queue.element());

System.out.println(queue.remove());

System.out.println(“队首” + queue.element());

System.out.println(queue.remove());

//超出队列大小,第四个元素清除不成功,抛出异常

//java.util.NoSuchElementException

//System.out.println(queue.remove());

}

}

2、第二组

offer()、poll()、peek()

操作不成功时不抛出异常,有返回值

/*

offer()、poll()、peek()

操作不成功时不抛出异常,有返回值

*/

public static void api2() {

//队列大小

ArrayBlockingQueue queue = new ArrayBlockingQueue<>(3);

//添加

System.out.println(queue.offer(“a”));

System.out.println(queue.offer(“b”));

System.out.println(queue.offer(“c”));

//超出队列大小,添加不成功,返回false,不抛异常

System.out.println(queue.offer(“d”));

System.out.println(“-------------”);

System.out.println(“队首” + queue.peek());

System.out.println(queue.poll());

System.out.println(queue.poll());

System.out.println(“队首” + queue.peek());

System.out.println(queue.poll());

//超出队列大小,获取不成功,返回null,不抛异常

System.out.println(“队首” + queue.peek());

System.out.println(queue.poll());

}

3、第三组

put()、take()

操作不成功时阻塞等待

/*

put()、take()

操作不成功时阻塞等待

*/

public static void api3() throws InterruptedException {

//队列大小

ArrayBlockingQueue queue = new ArrayBlockingQueue<>(3);

//添加

queue.put(“a”);//无返回值,不能打印输出

queue.put(“b”);

queue.put(“c”);

//queue.put(“d”); //队列没有位置就会阻塞

System.out.println(queue.take());

System.out.println(“队首” + queue.peek());

System.out.println(queue.take());

System.out.println(“队首” + queue.element());

System.out.println(queue.take());

//System.out.println(queue.take());//超出队列中现在元素数量,阻塞等待

}

4、第四组

offer(obj,int,timeunit.status)、poll(int,timeunit.status)

操作不成功时,阻塞等待,等待超时就退出;操作成功时,不需等待,直接退出;

/*

offer(obj,int,timeunit.status)、poll(int,timeunit.status)

操作不成功时超时等待

*/

public static void api4() throws InterruptedException {

//队列大小

ArrayBlockingQueue queue = new ArrayBlockingQueue<>(3);

//添加

System.out.println(queue.offer(“a”, 2, TimeUnit.SECONDS));

System.out.println(queue.offer(“b”, 2, TimeUnit.SECONDS));

System.out.println(queue.offer(“c”, 2, TimeUnit.SECONDS));

//超出队列大小,添加不成功。等待2秒后退出,返回false,不阻塞

System.out.println(queue.offer(“d”, 2, TimeUnit.SECONDS));

System.out.println(“---------------”);

System.out.println(queue.poll(1, TimeUnit.SECONDS));

System.out.println(queue.poll(1, TimeUnit.SECONDS));

System.out.println(“队首” + queue.peek());

System.out.println(queue.poll(1, TimeUnit.SECONDS));

//获取数量超出队列中现有数量,获取不成功。等待1秒后退出,返回null,不阻塞

System.out.println(queue.poll(1, TimeUnit.SECONDS));

}

10.5、同步队列


SynchronousQueue

最多只存一个元素

进去一个元素,必须等待取出来之后,才能再往里面放一个元素

存:put()、取:take();用其它存取方法,达不到同步存取目的

package blockingQueue;

import java.util.concurrent.BlockingQueue;

import java.util.concurrent.SynchronousQueue;

import java.util.concurrent.TimeUnit;

/**

  • @author ajun

  • Date 2021/7/6

  • @version 1.0

  • 同步队列

*/

public class SynchronousQueueDemo {

public static void main(String[] args) {

SynchronousQueue queus = new SynchronousQueue<>();//同步队列

//存入

new Thread(() -> {

try {

System.out.println(Thread.currentThread().getName() + " 存入:1");

queus.put(“1”);

System.out.println(Thread.currentThread().getName() + " 存入:2");

queus.put(“2”);

System.out.println(Thread.currentThread().getName() + " 存入:3");

queus.put(“3”);

} catch (InterruptedException e) {

e.printStackTrace();

}

//queus.offer();

//queus.add();

//queus.offer(,2, TimeUnit.SECONDS);

},“存入线程”).start();

//取出

new Thread(() -> {

try {

TimeUnit.SECONDS.sleep(3);

System.out.println(Thread.currentThread().getName() + " 取出:" + queus.take());

TimeUnit.SECONDS.sleep(3);

System.out.println(Thread.currentThread().getName() + " 取出:" + queus.take());

TimeUnit.SECONDS.sleep(3);

System.out.println(Thread.currentThread().getName() + " 取出:" + queus.take());

} catch (InterruptedException e) {

e.printStackTrace();

}

//queus.remove();

//queus.poll();

//queus.poll(2,TimeUnit.SECONDS);

},“取出线程”).start();

}

}

11、线程池(重点)

==========

三大方法、七大参数、4种拒绝策略

一、池化技术


  • 背景:程序运行: 占用系统的资源 ! 优化CPU资源的使用 ===>池化技术

  • 线程池,连接池,内存池,对象池 …

  • 原理:提前准备好一些资源,如果要用,就来拿;用完之后返还

  • 好处

  • 降低资源消耗

  • 提高响应速度

  • 方便管理

线程复用,可以控制最大并发数,管理线程

二、三大方法


Executors 工具类创建线程池有三种方法

阿里巴巴开发手册禁止用 Executors 创建线程池,而用 ThreadPoolExecutor 的方式

1、单一线程

Executors.newSingleThreadExecutor();//单个线程

2、固定数量线程

Executors.newFixedThreadPool(5);//固定数量线程

3、弹性数量线程

Executors.newCachedThreadPool();//弹性数量线程

package threadPool;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

/**

  • @author ajun

  • Date 2021/7/6

  • @version 1.0

  • 用 Executors 工具类的三种方法创建线程

*/

public class ExecutorsDemo {

public static void main(String[] args) {

//ExecutorService service = Executors.newSingleThreadExecutor();//单个线程

ExecutorService service = Executors.newFixedThreadPool(5);//固定数量线程

//ExecutorService service = Executors.newCachedThreadPool();//弹性数量线程

try {

for (int i = 1; i <= 10; i++) {

service.submit(() -> {

System.out.println(Thread.currentThread().getName() + " ok!");

});

}

} finally {

service.shutdown();//关闭线程池

}

}

}

三、七大参数


ThreadPoolExecutor

1、三大方法底层分析

用 Executors 创建线程池的三种方法,底层都是调用 ThreadPoolExecutor

public static ExecutorService newSingleThreadExecutor() {

return new FinalizableDelegatedExecutorService

(new ThreadPoolExecutor(1, 1,

0L, TimeUnit.MILLISECONDS,

new LinkedBlockingQueue()));

}

public static ExecutorService newFixedThreadPool(int nThreads) {

return new ThreadPoolExecutor(nThreads, nThreads,

0L, TimeUnit.MILLISECONDS,

new LinkedBlockingQueue());

}

public static ExecutorService newCachedThreadPool() {

return new ThreadPoolExecutor(0, Integer.MAX_VALUE,

60L, TimeUnit.SECONDS,

new SynchronousQueue());

}

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
ISECONDS,

new LinkedBlockingQueue()));

}

public static ExecutorService newFixedThreadPool(int nThreads) {

return new ThreadPoolExecutor(nThreads, nThreads,

0L, TimeUnit.MILLISECONDS,

new LinkedBlockingQueue());

}

public static ExecutorService newCachedThreadPool() {

return new ThreadPoolExecutor(0, Integer.MAX_VALUE,

60L, TimeUnit.SECONDS,

new SynchronousQueue());

}

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-5TrTFsXS-1715144597933)]

[外链图片转存中…(img-ITUngTej-1715144597933)]

[外链图片转存中…(img-K94Nifmd-1715144597934)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值