}
}
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开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!