1. 读写锁 ReentrantReadWriteLock
- java.util.concurrent
- Condition
- Lock
- ReadWriteLock
- ReentrantReadWriteLock
A ReadWriteLock维护一对关联的locks ,一个用于只读操作,一个用于写入。 read lock可以由多个阅读器线程同时进行,只要没有作者。 write lock是独家的。
/**
* 独占锁(写锁) 一次只能被一个线程占有
* 共享锁(读锁) 多个线程可以同时占有
* ReadWriteLock
* 读-读 可以共存!
* 读-写 不能共存!
* 写-写 不能共存!
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
// 写入
for (int i = 1; i <= 5 ; i++) {
final int temp = i;
new Thread(()->{
myCache.put(temp+"",temp+"");
},String.valueOf(i)).start();
}
// 读取
for (int i = 1; i <= 5 ; i++) {
final int temp = i;
new Thread(()->{
myCache.get(temp+"");
},String.valueOf(i)).start();
}
}
}
// 加锁的
class MyCacheLock{
private volatile Map<String,Object> map = new HashMap<>();
// 读写锁: 更加细粒度的控制
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//private Lock lock = new ReentrantLock();
// 存,写入的时候,只希望同时只有一个线程写
public void put(String key,Object value){
//写锁 上锁
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"写入"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入OK");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
// 取,读,所有人都可以读!
public void get(String key){
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"读取"+key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName()+"读取OK");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
Reentrant
英
/riːˈentrənt/
adj.
再进去的;凹角的
n.
凹角;再进入
2. 阻塞队列 BlockingDeque
- 写入:如果队列满了,就必须阻塞等待
- 读取:如果队列是空的,必须阻塞等待生产
结构
Collection
- Queue
- Abstract Queue 非阻塞队列
- Concurrent Linked Queue
- Blocking Queue 阻塞队列
- Array Blocking Queue
- Linked Blocking Queue
- Synchron ous Queue
- Deque 双端队列
- Blocking Deque,继承上面两个
- Abstract Queue 非阻塞队列
- Set
- List
- ArrayList
- LinkedList
四组API
- 为何使用
A —> B,如果B未执行完,A要等待。
多线程并发处理,线程池!
方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer() | put() | offer(,) |
移除 | remove | poll() | take() | poll(,) |
检测队首元素 | element | peek | - | - |
抛出异常
// 队列的大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(1);
System.out.println(blockingQueue.add("a"));
// IllegalStateException: Queue full 抛出异常!
// System.out.println(blockingQueue.add("d"));
System.out.println("=-===========");
System.out.println(blockingQueue.element()); // 查看队首元素是谁,如果已经移出去 第一个,那就是第二个
System.out.println(blockingQueue.remove());
// java.util.NoSuchElementException 抛出异常!
// System.out.println(blockingQueue.remove());
不抛出异常
// 队列的大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.peek()); //获取首元素
// System.out.println(blockingQueue.offer("d")); // false 不抛出异常!
System.out.println("============================");
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll()); //没了,返回 null 不抛出异常!
peek
v.
偷看,窥视;微露出,探出
n.
一瞥,偷偷地一看;(计算机)读取数据
offer
v.
提供,给予;提议,表示愿意(做某事);出(价),开(价);提出,作出;表示(爱、友谊等);(向上帝或神)奉献(祈祷、赞美),祭献(牺牲); 试图(使用暴力,进行抵抗);<古>发动,挑起(战争);把……摆好(或放到位)(以便检测或评估其外观或状况);<旧>(机会)出现;求婚
poll
英
/pəʊl/
n.
民意调查,民意测验;选举投票,计票;投票数;投票点(the polls);头顶,头皮;无角动物(尤指无角牛)
v.
对……进行民意测验(调查);获得(票数);(电信,计算机)轮询,探询;截掉(动物,尤指小牛)的角;<古>修枝
* 阻塞等待
/**
* 等待,阻塞(一直阻塞)
*/
public static void test3() throws InterruptedException {
// 队列的大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(1);
// 一直阻塞
blockingQueue.put("a");
// blockingQueue.put("d"); // 队列没有位置了,一直阻塞
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take()); // 没有这个元素,一直阻塞
}
- 使用案例
public class MyTest {
public static void main(String[] args) {
LinkedBlockingDeque<Integer> queue = new LinkedBlockingDeque<>(1);
new Thread(new Producer(queue)).start();
new Thread(new Consumer(queue)).start();
System.out.println("主线程执行顺序不确定");
}
}
class Producer implements Runnable {
protected LinkedBlockingDeque<Integer> queue = null;
public Producer(LinkedBlockingDeque queue) {
this.queue = queue;
}
@Override
public void run() {
try {
Thread.sleep(3000);
System.out.println("2. 进行添加了");
//这里对应错误了,应该是 put 对应take
queue.add(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Consumer implements Runnable {
protected LinkedBlockingDeque<Integer> queue = null;
public Consumer(LinkedBlockingDeque queue) {
this.queue = queue;
}
@Override
public void run() {
try {
//队列为空,必须阻塞 等着添加
System.out.println("3. 我一定是在 添加后面执行,添加睡几秒,我就等待它 " + queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
超时等待
/**
* 等待,阻塞(等待超时)
*/
public static void test4() throws InterruptedException {
// 队列的大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.offer("a");
blockingQueue.offer("b");
blockingQueue.offer("c");
// blockingQueue.offer("d",2,TimeUnit.SECONDS); // 等待超过2秒就退出,不报错
System.out.println("===============");
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
blockingQueue.poll(2, TimeUnit.SECONDS); // 等待超过2秒就退出
}
SynchronousQueue 同步队列
-
阻塞队列下的
-
没有容量,
-
进去一个元素,必须等待取出来之后,才能再往里面放一个元素!
-
put、take
/**
* 同步队列
* 和其他的BlockingQueue 不一样, SynchronousQueue 不存储元素
* put了一个元素,必须从里面先take取出来,否则不能在put进去值!
*/
public class SynchronousQueueDemo {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new SynchronousQueue<>(); // 同步队列
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(6);
System.out.println(Thread.currentThread().getName() + " put 1");
blockingQueue.put("1");
System.out.println(Thread.currentThread().getName() + " put 2");
blockingQueue.put("2");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "T1").start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + "=>" + blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "T2").start();
}
}
queue
英
/kjuː/
n.
<英>(人、汽车等的)队,行列;<英>(为得到某机会而等待的)长列,长队;(计算机)队列;呼叫队列;<古>辫子
v.
<英>排队(等候);竞相,抢着(做某事);(计算机)排成队列,排队
synchronous
英
/ˈsɪŋkrənəs/
同步的
3. 线程池
线程池:三大方法、7大参数、4种拒绝策略
池化技术
程序的运行,本质:占用系统的资源!
优化资源的使用!=>池化技术
线程池、连接池、内存池、对象池,常量池///… 创建、销毁。十分浪费资源
池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我。
线程池的好处**😗*
1、降低资源的消耗
2、提高响应的速度
3、方便管理。
线程复用、可以控制最大并发数、管理线程
线程池 不允许使用 Executors 创建,而是通过 ThreadPoolExecutor
- 更贱明确 线程的运行规则,避免资源耗尽的风险
- 允许 Integer.MAX_VALUE个线程,可能报 OOM 内存溢出
3大方法
//单一
Executors.new Single Thread Executor();
//固定的值
Executors.new Fixed ThreadPool(5);
//缓存,可伸缩的,遇强则强
Executors.new Cached ThreadPool();
//还有 调度行 创建,参数为 核心线程数
Executors.newScheduledThreadPool(5);
ExecutorService threadPool = Executors.newSingleThreadExecutor();
try {
for (int i = 0; i < 100; i++) { // 使用了线程池之后,使用线程池来创建线程
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally { // 线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
scheduled
英
/ˈʃedjuːld/
adj.
预先安排的,按时刻表的;(尤指航班)定期的;<英>(建筑,历史遗迹)列入文物保护单位的
v.
安排;将……列入计划表(或时间表等);<英>把(建筑物)列为文物保护单位(schedule 的过去式和过去分词形式)
7大参数
- 正常 1,2 号口,可办业务。 核心线程池大小
- 其他人,去 候客区等。 1 2 3。 候客区就是 阻塞队列。
- 人特别多,候客区满了,在开 3,4,5窗口。最大线程数
- 假如 再开的 这三个窗口,也满了。又来了人,就要拒绝策略了。
- 3,4,5 窗口,1个小时都没有业务。关闭释放。超时不候
- 队列 ,候客区,这里只能放3个人。 候客区
- 线程工厂,可以用默认的
- 核心线程数
- 最大线程数
- 多少单位 超时没有业务,关闭释放 非核心的线程。如上 3,4,5 。1个小时都没任务,就释放。
- 超时的单位
- 队列。候客区
- 线程工厂
- 拒绝策略
源码 Executors
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
//所以 不要用这个创建
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, //最大为21亿,会oom
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
//本质 ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize, // 核心线程池大小
int maximumPoolSize, // 最大核心线程池大小
long keepAliveTime,// 超时了没有人调用就会释放
TimeUnit unit, // 超时单位
BlockingQueue<Runnable> workQueue, // 阻塞队列
ThreadFactory threadFactory,// 线程工厂:创建线程的,一般 不用动
RejectedExecutionHandler handler) { // 拒绝策略
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
4种拒绝策略
/**
* new ThreadPoolExecutor.AbortPolicy() // 银行满了,还有人进来,不处理这个人的,抛出异常
* new ThreadPoolExecutor.CallerRunsPolicy() // 哪来的去哪里!调用的线程执行
* new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常!
* new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试去和最早的竞争,也不会抛出异常! 一般都会竞争成功
*/
Abort
英
/əˈbɔːt/
v.
(使)流产,堕(胎);(由于问题或故障)中止,使夭折;(胚胎器官,有机体)发育不全,败育
n.
<非正式>(飞行、航天任务或其他事业的)中断,取消;夭折的计划(或事业)
Policy
英
/ˈpɒləsi/
n.
政策,方针;(处事) 原则,策略;保险单
discard
v.
扔掉,弃置;打出(无用的牌),垫(牌)
n.
被抛弃物;(纸牌游戏中)垫出的牌
案例 ThreadPoolExecutor
public class Demo01 {
public static void main(String[] args) {
// 自定义线程池!工作 ThreadPoolExecutor
// 最大线程到底该如何定义:
// 1、CPU 密集型,几核,就是几,可以保持CPu的效率最高!
// 2、IO 密集型 > 判断你程序中十分耗IO的线程。比如设置2倍
// 程序 15个大型任务 io十分占用资源!
// 获取CPU的核数,比如6核心12线程,即是12
//System.out.println(Runtime.getRuntime().availableProcessors());
List list = new ArrayList();
//核心 2个,上个例子 最大5个,3秒超时,就不等了。
//阻塞Linked(等候区有三个,最多最多5+3=8个,超过8个就走拒绝策略),
//和 默认的工厂。拒绝策略
ExecutorService threadPool = new ThreadPoolExecutor(
2,
Runtime.getRuntime().availableProcessors(), //我的12核心,这参数大概能执行24个,不确定,最好能执行29个
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了,尝试去和最早的竞争,也不会抛出异常!
try {
// 最大承载:Deque + max
// 超过 RejectedExecutionException
for (int i = 1; i <= 9; i++) {
// 使用了线程池之后,使用线程池来创建线程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
4. 四大函数式接口
新时代的程序员:lambda表达式、链式编程、函数式接口、Stream流式计算
函数式接口:
只有一个方法的接口(但 可以有默认方法 和 静态方法)
- 在包:java.util.function
- Consumer
- 两个参数 没有返回值 的是:BiConsumer
- IntConsumer 是有一个int的参数
- Funtion
- Predicate
- Supplier 无参数,有返回值。
- 如果 无参数,无返回值的 那就是 Runnable
- Consumer
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
// 泛型、枚举、反射
// lambda表达式、链式编程、函数式接口、Stream流式计算
// 超级多FunctionalInterface
// 简化编程模型,在新版本的框架底层大量应用!
// foreach(消费者类的函数式接口)
集合的 forEach
Iterable
//消费者 类型的 函数式 接口
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
Function
@FunctionalInterface
public interface Function<T, R> {
R apply(T t); //参数T,返回R
}
Function<String,String> function1 = new Function<String,String>() {
@Override
public String apply(String str) {
return str;
}
};
//简化1
Function<String, String> function2 = (String str) -> {
return str;
};
//简化2
Function<String,String> function = str->{return str;};
//简化3
Function<String, String> function2 = str -> str;
System.out.println(function.apply("asd"));
Predicate
predicate
n.
(语法)谓语;(逻)谓项
v.
使基于,使取决于;表明,断言
adj.
(用作)表语的
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
断定型接口:有一个输入参数,返回值只能是 布尔值!
String str=" ";
System.out.println(str.isEmpty()); // 空格都是为false。但是:null报空指针
// 判断字符串是否为空
Predicate<String> predicate1 = new Predicate<String>() {
@Override
public boolean test(String str) {
return str.isEmpty();
}
};
//最简化
Predicate<String> predicate2 = str -> str.isEmpty();
Predicate<String> predicate = (str) -> {
return str.isEmpty();
};
System.out.println(predicate2.test(""));
Consumer
Consumer 消费型接口: 只有输入,没有返回值
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
//这个默认方法不算
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
// Consumer<String> consumer = new Consumer<String>() {
// @Override
// public void accept(String str) {
// System.out.println(str);
// }
// };
Consumer<String> consumer = (str)->{System.out.println(str);};
consumer.accept("sdadasd");
//在简化
Consumer<String> consumer2 = str -> System.out.println(str);
new ArrayList<>().forEach(System.out::println);
Supplier
- 供给者,提供者
英
/səˈplaɪə(r)/
美
/səˈplaɪər/
n.
供应商,供应者
@FunctionalInterface
public interface Supplier<T> {
T get();
}
Supplier 供给型接口 没有参数,只有返回值
public static void main(String[] args) {
// Supplier supplier = new Supplier<Integer>() {
// @Override
// public Integer get() {
// System.out.println("get()");
// return 1024;
// }
// };
Supplier supplier = ()->{ return 1024; };
System.out.println(supplier.get());
Supplier supplier = () -> 1024;
}