16.ReadWriteLock(读写锁)
读可以由多个线程去读,写只能允许一个
package com.lsh.rw;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 独占锁(写锁)只能允许一个线程占有
* 共享锁(独锁)允许多个线程共同占有
* 读-读(可以共存)
* 读-写(不能共存)
* 写-写(不能共存)
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
//MyCache myCache = new MyCache();
MyCacheLock myCacheLock = new MyCacheLock();
//写入
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(()->{
myCacheLock.put(temp+"",temp+"");
},String.valueOf(i)).start();
}
//读取
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(()->{
myCacheLock.get(temp+"");
},String.valueOf(i)).start();
}
}
}
class MyCacheLock{
private volatile Map<String,Object> map = new HashMap<>();
//加锁,更加细粒度的控制写操作
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//存,写(只希望有一个线程去写入)
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()+"写入完毕");
} 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);
map.get(key);
System.out.println(Thread.currentThread().getName()+"读取完毕");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
class MyCache{
private volatile Map<String,Object> map = new HashMap<>();
//存,写
public void put(String key,Object value){
System.out.println(Thread.currentThread().getName()+"写入"+ key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入完毕");
}
//读,取
public void get(String key){
System.out.println(Thread.currentThread().getName()+"读取"+ key);
map.get(key);
System.out.println(Thread.currentThread().getName()+"读取完毕");
}
}
17.阻塞队列BlockingQueue、四组API
写入:如果队列满了,就必须阻塞等待
读取:如果队列是空的,必须阻塞等待生产
阻塞队列:
什么情况下我们会使用阻塞队列:多线程并发处理,线程池!
学会使用队列
添加、删除
方式 | 抛出异常 | 不会抛出异常,有返回值 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add() | offer(异常时返回false) | put() | offer(,) |
移除 | remove() | poll(异常时返回null) | take() | poll(,) |
检测队首元素 | element | peek() |
package com.lsh.bq;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) throws InterruptedException {
//test1();
//test2();
//test3();
test4();
}
public static void test1(){
ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(arrayBlockingQueue.add("A"));
System.out.println(arrayBlockingQueue.add("B"));
System.out.println(arrayBlockingQueue.add("C"));
//Exception in thread "main" java.lang.IllegalStateException: Queue full
//System.out.println(arrayBlockingQueue.add("C"));
System.out.println("======================================");
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
//Exception in thread "main" java.util.NoSuchElementException
//System.out.println(arrayBlockingQueue.remove());
}
public static void test2(){
ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(arrayBlockingQueue.offer("a"));
System.out.println(arrayBlockingQueue.element());//队首元素
System.out.println(arrayBlockingQueue.offer("b"));
System.out.println(arrayBlockingQueue.offer("c"));
System.out.println(arrayBlockingQueue.offer("d"));//不抛出异常,返回false
System.out.println(arrayBlockingQueue.peek());
System.out.println("=================");
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());//不抛出异常,返回null
}
public static void test3() throws InterruptedException {
ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
arrayBlockingQueue.put("a");
arrayBlockingQueue.put("b");
arrayBlockingQueue.put("c");
// arrayBlockingQueue.put("d");队列没有位置了,一直阻塞
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());//没有这个元素,一直阻塞
}
public static void test4() throws InterruptedException {
ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
arrayBlockingQueue.offer("a");
arrayBlockingQueue.offer("b");
arrayBlockingQueue.offer("c");
arrayBlockingQueue.offer("a",2, TimeUnit.SECONDS);//等待超时退出
System.out.println("---------------");
arrayBlockingQueue.poll();
arrayBlockingQueue.poll();
arrayBlockingQueue.poll();
arrayBlockingQueue.poll(2,TimeUnit.SECONDS);
}
}
19.同步队列SynchronousQueue
1.没有容量,进去一个元素,必须等待取出来之后,才能再往里面放入一个元素
pull、take
package com.lsh.bq;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/**
* 同步队列跟其他队列不一样,不存储元素,只要put了一个元素,就必须取出来(take)
* 否则不让继续put
*/
public class SynchronousQueueDemo {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new SynchronousQueue<>();//同步队列
new Thread(()->{
try {
System.out.println((Thread.currentThread().getName() + " put 1"));
blockingQueue.put("1");
System.out.println((Thread.currentThread().getName() + " put 2"));
blockingQueue.put("2");
System.out.println((Thread.currentThread().getName() + " put 3"));
blockingQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"T1").start();
new Thread(()->{
try {
System.out.println((Thread.currentThread().getName() + "===>" + blockingQueue.take()));
TimeUnit.SECONDS.sleep(1);
System.out.println((Thread.currentThread().getName() + "===>" + blockingQueue.take()));
TimeUnit.SECONDS.sleep(1);
System.out.println((Thread.currentThread().getName() + "===>" + blockingQueue.take()));
} catch (InterruptedException e) {
e.printStackTrace();
}
},"T2").start();
}
}
20.池化技术及线程池使用
池化技术
程序的运行,占用系统资源,优化资源使用:池化技术
eg:线程池、内存池、对象池。。。
池化技术:事先准备好一些资源,有人要用就来取,用完之后须归还
线程池的好处
1.降低资源消耗
2.提高响应速度
3.方便管理。
线程复用、可以控制最大并发数、管理线程
线程池:三大方法、七个参数、四大拒绝策略
package com.lsh.pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo01 {
public static void main(String[] args) {
//ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
//ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建固定大小的线程池
ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩性的
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();
}
}
}
/**
*七大参数
*/
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,
newLinkedBlockingQueue<Runnable());
}
=========================================================================
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,//21亿
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
//以上3种方式本质都是调用: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;
}
阿里巴巴开发手册:
package com.lsh.pool;
import java.util.concurrent.*;
//Executors 工具类,3大方法
/**
* 四大拒绝策略:
* 1.new ThreadPoolExecutor.AbortPolicy()//超出队列最大值,不处理,抛出异常
* 2.new ThreadPoolExecutor.CallerRunsPolicy()//哪里来的回哪里去
* 3.new ThreadPoolExecutor.DiscardPolicy()//队列满了,丢掉任务,不会抛出异常
* 4.new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,尝试和最早的竞争,不会抛出异常
*/
public class Demo01 {
public static void main(String[] args) {
//ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
/*ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建固定大小的线程池
ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩性的*/
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3), Executors.defaultThreadFactory(),
//new ThreadPoolExecutor.AbortPolicy()
//new ThreadPoolExecutor.CallerRunsPolicy()//哪里来的去哪里
//new ThreadPoolExecutor.DiscardPolicy()//队列满了,丢掉任务,不会抛出异常
new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,尝试和最早的竞争,不会抛出异常
);
try {
//最大承载:Deque + MAX
//RejectedExecutionException(抛出异常)
for (int i = 0; i < 9; i++) {
//使用线程池创建线程
threadPool.execute(()->{
System.out.println((Thread.currentThread().getName() + " OK"));
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
21.CPU密集型和IO密集型
池的大小如何去设置?
IO密集型,CPU密集型(调优)
package com.lsh.pool;
import java.util.concurrent.*;
//Executors 工具类,3大方法
/**
* 四大拒绝策略:
* 1.new ThreadPoolExecutor.AbortPolicy()//超出队列最大值,不处理,抛出异常
* 2.new ThreadPoolExecutor.CallerRunsPolicy()//哪里来的回哪里去
* 3.new ThreadPoolExecutor.DiscardPolicy()//队列满了,丢掉任务,不会抛出异常
* 4.new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,尝试和最早的竞争,不会抛出异常
*/
public class Demo01 {
public static void main(String[] args) {
//ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
/*ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建固定大小的线程池
ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩性的*/
/**
* 最大线程数该如何定义:
* 1.CPU密集型:几核,就是几,可以保持CPU的效率最高
* 2.IO密集型:>你程序中十分耗IO的线程
*/
//获取CPU核数
System.out.println(Runtime.getRuntime().availableProcessors());
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3), Executors.defaultThreadFactory(),
//new ThreadPoolExecutor.AbortPolicy()
//new ThreadPoolExecutor.CallerRunsPolicy()//哪里来的去哪里
//new ThreadPoolExecutor.DiscardPolicy()//队列满了,丢掉任务,不会抛出异常
new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,尝试和最早的竞争,不会抛出异常
);
try {
//最大承载:Deque + MAX
//RejectedExecutionException(抛出异常)
for (int i = 0; i < 9; i++) {
//使用线程池创建线程
threadPool.execute(()->{
System.out.println((Thread.currentThread().getName() + " OK"));
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
22.函数型接口和断定型接口
新时代程序员:lambda表达式、链式编程、函数式接口、Stream流式计算
函数式接口:只有一个方法的接口,带有注解:@FunctionalInterface
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
//新版本的框架底层大量应用
//foreach(消费者类型的函数式接口)
package com.lsh.fuction;
import java.util.function.Function;
/**
* function:函数式接口
* 只要是函数式接口,就可以用lambda表达式简化
*/
public class Demo01 {
public static void main(String[] args) {
/*Function function =new Function<String,String>() {
@Override
public String apply(String o) {
return null;
}
};*/
//lambda表达式简化
Function function = (str)->{return str;};
System.out.println(function.apply("abc"));
}
}
断定型接口
package com.lsh.fuction;
import java.util.function.Predicate;
/**
*断定型接口
*
/
public class Demo02 {
public static void main(String[] args) {
/*Predicate<String> predicate = new Predicate<String>() {
@Override
public boolean test(String str) {
return str.isEmpty();
}
};*/
Predicate<String> predicate = str->{return str.isEmpty();};
System.out.println(predicate.test(""));
}
}
23.消费型接口和供给型接口
消费型接口:Consumer()
package com.lsh.fuction;
import java.util.function.Consumer;
public class Demo03 {
public static void main(String[] args) {
/* Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};*/
Consumer<String> consumer = s -> {System.out.println(s);};
consumer.accept("超级英雄LBJ");
}
}
supplier 供给型接口
package com.lsh.fuction;
import java.util.function.Supplier;
public class Demo04 {
public static void main(String[] args) {
/*Supplier supplier = new Supplier() {
@Override
public Object get() {
return "天生我材必有用";
}
};*/
Supplier supplier = ()->{return "偏执又坚韧";};
System.out.println(supplier.get());
}
}
24.Stream流式计算
大数据:存储+计算
集合、mysql本质就是存储东西的
计算都应该交给流来操作!
package com.lsh.stream;
import java.sql.SQLOutput;
import java.util.Arrays;
import java.util.List;
/**
* 一分钟内完成此题,只能用一行代码完成
* 现在有5个用户,筛选:
* 1.id必须是偶数
* 2.年龄必须大于23岁
* 3.用户名转为大写字母
* 4.用户名字母倒着排序
* 5.只输出一个用户
*/
public class Test {
public static void main(String[] args) {
User u1 = new User(1,"a",21);
User u2 = new User(2,"b",22);
User u3 = new User(3,"c",23);
User u4 = new User(4,"d",24);
User u5 = new User(6,"e",25);
List<User> list = Arrays.asList(u1,u2,u3,u4,u5);
//计算交给stream流
//filter过滤,调用Predicate函数式接口
//lambda表达式,链式编程,函数式接口,Stream流式计算
list.stream().filter(u->{return u.getId()%2==0;})
.filter(u->{return u.getAge()>23;})
.map(user -> {return user.getName().toUpperCase();})
.sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
.limit(1)
.forEach(System.out::println);
}
}
25.ForkJoin详解
分支合并
ForkJoin在JDKL1.7,并行执行任务,提高效率,大数据量
大数据:Map Reduce(把大任务拆分为小任务)
ForkJoin特点:工作窃取。
维护了双端队列
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
import java.util.stream.LongStream;
/**
* 求和计算的任务!
* // 如何使用 forkjoin
* // 1、forkjoinPool 通过它来执行
* // 2、计算任务 forkjoinPool.execute(ForkJoinTask task)
* // 3. 计算类要继承 ForkJoinTask
*/
class test1{
public static void main(String[] args) throws ExecutionException, InterruptedException {
test1(); // 6911
test2(); // 7249
test3(); // 156
}
// 普通程序员
public static void test1(){
Long sum = 0L;
long start = System.currentTimeMillis();
for (Long i = 1L; i <= 10_0000_0000; i++) {
sum += i;
}
long end = System.currentTimeMillis();
System.out.println("sum="+sum+" 时间:"+(end-start));
}
// 会使用ForkJoin
public static void test2() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
Forkjion task = new Forkjion(0L, 10_0000_0000L);
Forkjion submit = (Forkjion) forkJoinPool.submit(task);// 提交任务
Long sum = submit.get();
long end = System.currentTimeMillis();
System.out.println("sum="+sum+" 时间:"+(end-start));
}
// 使用Stream并行流!效率最高
public static void test3(){
long start = System.currentTimeMillis();
// Stream并行流 ()(]
long sum = LongStream.rangeClosed(0L,
10_0000_0000L).parallel().reduce(0, Long::sum);
long end = System.currentTimeMillis();System.out.println("sum="+sum+"时间:"+(end-start));
}
}
class Forkjion extends RecursiveTask<Long> {
private Long start; // 1
private Long end; // 1990900000
// 临界值
private Long temp = 10000L;
public Forkjion(Long start, Long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if ((end-start)<temp){
Long sum = 0L;
for (Long i = start; i <= end; i++) {
sum += i;
}
return sum;
}else { // forkjoin 递归
long middle = (start + end) / 2; // 中间值
Forkjion task1 = new Forkjion(start, middle);
task1.fork(); // 拆分任务,把任务压入线程队列
Forkjion task2 = new Forkjion(middle+1, end);
task2.fork(); // 拆分任务,把任务压入线程队列return task1.join() + task2.join();
return task1.join() + task2.join();
}
}
}