jdk8入门,包括lambda、juc
说明:写这篇博客的目的是,为了自己查找方便。
很多内容来自于https://www.bilibili.com/video/av90007319
一、Lambda 表达式
作用:让代码简化,流水线式的逻辑。可以简化 匿名内部类的 写法。
首先介绍函数式接口,这个接口只有一个方法。
1.1 简化匿名内部类
@FunctionalInterface
public interface Swimmable {
void swim();
}
public class LambdaExpressDemo {
public void goSwimming(Swimmable swimmable) {
swimmable.swim();
}
}
LambdaExpressDemo lambdaExpressDemo = new LambdaExpressDemo();
// 这是传统的 匿名内部类的写法
lambdaExpressDemo.goSwimming(new Swimmable() {
@Override
public void swim() {
System.out.println("这是传统的 匿名内部类的写法");
}
});
lambdaExpressDemo.goSwimming(() -> System.out.println("lambda表达式的写法"));
1.2 简化线程实现的写法
比如 new runable(){} 用lambda写,就是 ()-> {// TODO 具体的业务} 。
1.3 集合的常用操作:排序。
除了使用 lambda表达式来代替 new Comparator(){},还有更简单的写法。这个写法,叫做方法引用。
给定一个 Person类。
List personArrayList = new ArrayList<>();
personArrayList.add(new Person("Tom", 22, 166));
personArrayList.add(new Person("Jerry", 76, 176));
personArrayList.add(new Person("张三", 55, 188));
personArrayList.add(new Person("李四", 76, 199));
personArrayList.sort(Comparator.comparingInt(Person::getAge).thenComparing(Person::getName));
personArrayList.forEach(System.out::println);
运行结果是
Person{ Tom, 22, 166}
Person{ 张三, 55, 188}
Person{ Jerry, 76, 176}
Person{ 李四, 76, 199}
1.4 lambda表达式 和 匿名内部类的 比较
- 需要实现的类型不一样
- 匿名内部类 需要抽象类 ,接口 都可以。
- 而 lambda表达式 必须实现一个 函数式接口。
- 可以实现的抽象方法 的 数量不一样编译过程不一样
- 匿名内部类 可以 实现 多个抽象方法。
- 而 lambda表达式 只能 实现一个。
- 编译过程不一样
-
- 匿名内部类 在 编译后 ,会形成一个 XXXXX$1.class文件。
- 而 lambda表达式 在 程序运行的时候,不会生成 新的class文件。
1.5 lambda内使用的变量是final类型
lambda内的list是通过值传递的方式,拿到的一个外部变量的一个副本,它们都指向 new ArrayList<>()这个对象。
在lambda的业务处理过程,不允许改变 list变量的内存地址。
1.6 柯理化
柯理化的特征是,把多个参数的函数 转换为 只有一个参数的 函数。 x -> y -> x + y 这种写法,叫做级联表达式。
public static void main(String[] args) {
Function> fun = x -> y -> x + y;
System.out.println(fun.apply(2).apply(6));
}
二、内置的函数式接口
1. 供给型函数式接口 Supplier ,提供一个结果输出。
@FunctionalInterface
public interface Supplier {
T get();
// 省略其它
}
2. 有输入 也有 输出的 function
@FunctionalInterface
public interface Function {
R apply(T t);
// 省略其它
}
3. 消费型, 要求有输入参数。
@FunctionalInterface
public interface Consumer {
void accept(T t);
// 省略其它
}
4. 判断处理,要求有输入。返回 boolean 值。
@FunctionalInterface
public interface Predicate {
boolean test(T t);
// 省略其它
}
三、 juc的锁
3.1 可重入锁 ReentrantLock 。
解释 公平锁和非公平锁的感念,公平锁 有先来后到的顺序,非公平锁则插队执行,不按顺序。非公平锁的效率更高。
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Lock 和 synchronized 的区别
* 1、synchronized 是java的关键字;Lock 是java类。
* 2、synchronized 自动释放锁,Lock需要手动。
* 3、synchronized 适合锁少量的代码。Lock适合大量的代码,非常灵活。
*
*/
@SuppressWarnings("all")
public class SaleTicketDemo02 {
public static void main(String[] args) {
Ticket2 ticket = new Ticket2();
new Thread(() -> { for (int i = 0; i < 60; i++) ticket.sale(); }, "a").start();
new Thread(() -> { for (int i = 0; i < 60; i++) ticket.sale(); }, "b").start();
}
}
class Ticket2 {
private int num = 20;
Lock lock = new ReentrantLock(false); // 非公平锁, 为了工作效率。
public void sale() {
lock.lock();
try {
lock.tryLock(5, TimeUnit.SECONDS);
if (num > 0) {
System.out.println("还剩下 " + (--num) + " 张票 " + " ++++++ " + Thread.currentThread().getName());
TimeUnit.MILLISECONDS.sleep(500);
}else {
System.exit(1);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
3.2 传统的生产者和消费者
这里使用的是 wait 和 notify 。也叫做线程间通信 。
package com.kuang.productAndComsumer;
/**
* jdk的官方建议,在while内使用wait() 。
* 留意这个虚假唤醒的问题。
* synchronized (obj) {
* while ()
* obj.wait();
* ... // Perform action appropriate to condition
* }
*/
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "a").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "b").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "c").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "d").start();
}
}
class Data {
private int num = 0;
public synchronized void increment() throws InterruptedException {
while (num != 0) {
wait();
}
num++;
System.out.println(Thread.currentThread().getName() + " => " + num);
notify();
}
public synchronized void decrement() throws InterruptedException {
while (num == 0) {
wait();
}
num--;
System.out.println(Thread.currentThread().getName() + " => " + num);
notify();
}
}
3.3 JUC 版本的 await 和 signal
好处是 做到 精准唤醒。
package com.kuang.productAndComsumer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 这个是 JUC 版本的 await 和 signal。交替唤醒。
* 而且 可以做到 精准地唤醒。
*/
public class B {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(() -> { for (int i = 0; i < 10; i++) data.increment(); }, "a").start();
new Thread(() -> { for (int i = 0; i < 10; i++) data.decrement(); }, "b").start();
}
}
class Data2 {
private int num = 0;
Lock lock = new ReentrantLock();
Condition inCondition = lock.newCondition();
Condition deCondition = lock.newCondition();
public void increment() {
lock.lock();
try {
while (num != 0) {
inCondition.await();
}
num++;
System.out.println(Thread.currentThread().getName() + " => " + num);
deCondition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() {
lock.lock();
try {
while (num == 0) {
deCondition.await();
}
num--;
System.out.println(Thread.currentThread().getName() + " => " + num);
inCondition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
3.4 8个锁的问题
情况1: a和b两个线程,是共用同一把锁 。 a线程 是 sendMsg, 它先拿到锁,先执行。等它执行完毕后,再执行b线程的 makeCall() 。
import java.util.concurrent.TimeUnit;
public class Test1 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()-> {
phone.sendMsg();
}, "a").start();
TimeUnit.SECONDS.sleep(3);
new Thread(()-> {
phone.makeCall();
}, "b").start();
}
}
class Phone {
public synchronized void sendMsg(){
System.out.println("发送短信。");
}
public synchronized void makeCall(){
System.out.println("打电话");
}
}
情况2:哪怕 a线程跑得慢,也是它先执行。
import java.util.concurrent.TimeUnit;
public class Test1 {
public static void main(String[] args){
Phone phone = new Phone();
new Thread(()-> {
phone.sendMsg();
}, "a").start();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()-> {
phone.makeCall();
}, "b").start();
}
}
class Phone {
public synchronized void sendMsg() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发送短信。");
}
public synchronized void makeCall(){
System.out.println("打电话");
}
}
情况3: 如果和一个普通方法竞争,普通方法会先执行。
import java.util.concurrent.TimeUnit;
public class Test2 {
public static void main(String[] args){
Phone2 phone = new Phone2();
new Thread(()-> {
phone.sendMsg();
}, "a").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()-> {
phone.hello();
}, "b").start();
}
}
class Phone2 {
public synchronized void sendMsg() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发送短信。");
}
public synchronized void makeCall(){
System.out.println("打电话");
}
public void hello(){
System.out.println("hello");
}
}
情况4: 如果是两把不同的锁,锁之间就不存在竞争了。
import java.util.concurrent.TimeUnit;
public class Test2 {
public static void main(String[] args){
Phone2 phone = new Phone2();
Phone2 phone2 = new Phone2();
new Thread(()-> {
phone.sendMsg();
}, "a").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()-> {
phone2.makeCall();
}, "b").start();
}
}
class Phone2 {
public synchronized void sendMsg() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发送短信。");
}
public synchronized void makeCall(){
System.out.println("打电话");
}
public void hello(){
System.out.println("hello");
}
}
结果是:
打电话
发送短信。
情况5: 如果同一把锁,但方法是static,就存在竞争关系。因为,锁的是 这个类class,类一加载,就拿到了这把锁。
import java.util.concurrent.TimeUnit;
public class Test2 {
public static void main(String[] args){
Phone2 phone = new Phone2();
// Phone2 phone2 = new Phone2();
new Thread(()-> {
phone.sendMsg();
}, "a").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()-> {
phone.makeCall();
}, "b").start();
}
}
class Phone2 {
public static synchronized void sendMsg() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发送短信。");
}
public synchronized void makeCall(){
System.out.println("打电话");
}
public void hello(){
System.out.println("hello");
}
}
情况6: 静态方法 加上 两个对象Phone。结果是先 发送短信 。原因还是因为 static, 锁的是 这个class 类,同一把锁
import java.util.concurrent.TimeUnit;
public class Test2 {
public static void main(String[] args){
Phone2 phone = new Phone2();
Phone2 phone2 = new Phone2();
new Thread(()-> {
phone.sendMsg();
}, "a").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()-> {
phone2.makeCall();
}, "b").start();
}
}
class Phone2 {
public static synchronized void sendMsg() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发送短信。");
}
public static synchronized void makeCall(){
System.out.println("打电话");
}
public void hello(){
System.out.println("hello");
}
}
情况7: 如果一个线程调用的是 static, 另一个线程调用的不是 static方法。那就是2把锁。结果是先打电话。
import java.util.concurrent.TimeUnit;
public class Test2 {
public static void main(String[] args){
Phone2 phone = new Phone2();
// Phone2 phone2 = new Phone2();
new Thread(()-> {
phone.sendMsg();
}, "a").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()-> {
phone.makeCall();
}, "b").start();
}
}
class Phone2 {
public static synchronized void sendMsg() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发送短信。");
}
public synchronized void makeCall(){
System.out.println("打电话");
}
public void hello(){
System.out.println("hello");
}
}
情况8: 在情况7的基础上,如果是两个Phone对象调用,结果也是 先打电话。
import java.util.concurrent.TimeUnit;
public class Test2 {
public static void main(String[] args){
Phone2 phone = new Phone2();
Phone2 phone2 = new Phone2();
new Thread(()-> {
phone.sendMsg();
}, "a").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()-> {
phone2.makeCall();
}, "b").start();
}
}
class Phone2 {
public static synchronized void sendMsg() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发送短信。");
}
public synchronized void makeCall(){
System.out.println("打电话");
}
public void hello(){
System.out.println("hello");
}
}
4、 并发包下的集合类
4.1 ArrayList不安全
Vector使用 syncchronized , 所以效率低。CopyOnWriteArrayList效率更好。和 CopyOnWriteArrayList 类似的有 CopyOnWriteArraySet 。
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
*
* 1、Vector 是线程安全的。ArrayList 就不是。
* 2、但可以通过 Collections.synchronizedList(new ArrayList<>()) 方法变得线程安全。
* 3、Vector使用 syncchronized , 所以效率低。CopyOnWriteArrayList效率更好。
*
*/
public class ListTest {
public static void main(String[] args) {
// Listlist = new ArrayList<>();
// ListlistSync = Collections.synchronizedList(new ArrayList<>());
// Vectorvector = new Vector<>();
CopyOnWriteArrayListlist = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread(()-> {
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
}, i+"").start();
}
}
}
引出一个小知识点,HashSet 的本质就是HashMap,官方的源码就是这么写的。
public HashSet() {
map = new HashMap<>();
}
4.2 ConcurrentHashMap
ConcurrentHashMap 添加进 null值,会报出异常。而 HashMap 就可以存null值。
public static void main(String[] args) throws InterruptedException {
Map map = new HashMap<>();
ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap<>();
for (int i = 0; i < 30; i++) {
final int temp = i;
new Thread(() -> concurrentHashMap.put(Thread.currentThread().getName(), temp + ""), i + "").start();
}
TimeUnit.SECONDS.sleep(5);
concurrentHashMap.forEach((k, v) -> System.out.println(k + " : " + v));
map.put(null, null);
System.out.println(map);
concurrentHashMap.put(null, null);
System.out.println(concurrentHashMap);
}
五、Callable简单使用
特点是:有返回值,可以抛出异常。Callable 需要 FutureTask<> 做适配。
public class CallableTest {
public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
FutureTask futureTask = new FutureTask<>(new MyThread());
new Thread(futureTask, "a").start();
String res = (String) futureTask.get(2, TimeUnit.SECONDS); // 等待2秒,没有返回值,就算了,不等了。
System.out.println(res);
}
}
class MyThread implements Callable {
@Override
public String call() {
return UUID.randomUUID().toString();
}
}
六、juc并发
6.1 常用的辅助类
6.1.1 CountDownLatch 类似倒计时的功能
/**
* 使用场景: 有前提任务要先做
*/
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(3);
for (int i = 1; i <= 4; i++) {
new Thread(() -> {
System.out.println("Thread name: " + Thread.currentThread().getName());
countDownLatch.countDown();
}, i + "").start();
}
countDownLatch.await(); // 这里的 await 是必须要有的
System.out.println("Thread name: " + Thread.currentThread().getName() + " 倒计时结束");
}
6.1.2 还有 类似累加器CyclicBarrier 的使用
它的作用和 CountDownLatch 非常类似。都有任务先后执行的功能。
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(4, ()-> System.out.println("加法器累加结束,接下来执行新的任务。"));
for (int i = 1; i <= 6; i++) {
final int temp = i;
new Thread(()-> {
System.out.println(temp);
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
6.1.3 信号量Semaphore
起到 并发限流的作用。
// 线程数 10 并发 Semaphore semaphore = new Semaphore(10); for (int i = 0; i < 20; i++) { new Thread(()->{ try { semaphore.acquire(); System.out.println(Thread.currentThread().getName()+"进来"); TimeUnit.SECONDS.sleep(1); System.out.println(Thread.currentThread().getName()+"出去"); } catch (InterruptedException e) { e.printStackTrace(); }finally { semaphore.release(); } }).start(); } }
6.2 读写锁
源码接口
public interface ReadWriteLock { /** * Returns the lock used for reading. * * @return the lock used for reading */ Lock readLock(); /** * Returns the lock used for writing. * * @return the lock used for writing */ Lock writeLock(); }
写锁是独占的,只允许一个线程写。读锁是线程共享的,多个线程一起读。
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadAndWrite {
public static void main(String[] args) {
MyCache myCache = new MyCache();
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
for (int i = 1; i <= 10; i++) {
final int temp = i;
new Thread(() -> {
readWriteLock.writeLock().lock();
try {
myCache.put(temp + "", temp + "");
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
readWriteLock.writeLock().unlock();
}
}).start();
}
for (int i = 1; i <= 10; i++) {
final int temp = i;
new Thread(() -> {
readWriteLock.readLock().lock();
myCache.get(temp + "");
readWriteLock.readLock().unlock();
}).start();
}
}
}
class MyCache {
volatile Map map = new HashMap<>();
void get(String key) {
System.out.println(Thread.currentThread().getName() + "开始读");
map.get(key);
System.out.println(Thread.currentThread().getName() + "读完毕");
}
void put(String key, Object value) {
System.out.println(Thread.currentThread().getName() + "开始写");
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写完毕");
}
}
6.3 阻塞队列
会抛出异常 | 不会抛出异常 | 阻塞等待 | 超时等待 | |
添加 | add() | offer(e) | put(e) | offer(e,t,u) |
移除 | remove() | poll() | take() | poll(t,u) |
队首元素 | element() | peek() | - | - |
public static void test1() { ArrayBlockingQueue
public static void test2() { ArrayBlockingQueue
public static void test3() throws InterruptedException { ArrayBlockingQueue
public static void test4() throws InterruptedException { ArrayBlockingQueue
6.4 同步队列 SynchronousQueue
这个队列只存一个元素。它的使用方法和阻塞队列很相似,区别就是容量。
6.5 线程池
6.5.1 使用 Executors 来创建线程池。
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(5); // 最多5个线程并发
// ExecutorService threadPool = Executors.newCachedThreadPool(); // 根据电脑的cpu线程数而定,遇强则强。
try {
for (int i = 0; i < 20; i++) {
threadPool.execute(()-> System.out.println(Thread.currentThread().getName()));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
除了上面两个方法外, 还有 Executors.newSingleThreadExecutor() , 创建单线程。
6.5.2 线程池的7个参数
public ThreadPoolExecutor(int corePoolSize, 核心线程池数 int maximumPoolSize, 最大线程池数 long keepAliveTime, 存活时间 TimeUnit unit, 时间单位 BlockingQueueworkQueue, 阻塞队列 ThreadFactory threadFactory, 创建线程的工程,默认即可。 RejectedExecutionHandler handler) 拒绝策略
/** * Creates a new {@code ThreadPoolExecutor} with the given initial * parameters. * * @param corePoolSize the number of threads to keep in the pool, even * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @param maximumPoolSize the maximum number of threads to allow in the * pool * @param keepAliveTime when the number of threads is greater than * the core, this is the maximum time that excess idle threads * will wait for new tasks before terminating. * @param unit the time unit for the {@code keepAliveTime} argument * @param workQueue the queue to use for holding tasks before they are * executed. This queue will hold only the {@code Runnable} * tasks submitted by the {@code execute} method. * @param threadFactory the factory to use when the executor * creates a new thread * @param handler the handler to use when execution is blocked * because the thread bounds and queue capacities are reached * @throws IllegalArgumentException if one of the following holds:
* {@code corePoolSize < 0}
* {@code keepAliveTime < 0}
* {@code maximumPoolSize <= 0}
* {@code maximumPoolSize < corePoolSize} * @throws NullPointerException if {@code workQueue} * or {@code threadFactory} or {@code handler} is null */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueworkQueue, 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.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
提出线程吧资源耗尽的问题。 Integer.MAX_VALUE, 是 231-1=2147483647 ,21亿多。应该避免使用这个方法。
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue()); }
建议使用 ThreadPoolExecutor 来创建线程。
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 5, 20, 5, TimeUnit.SECONDS, new LinkedBlockingDeque<>(5), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy() );
拒绝策略有默认的方法。拒绝的意思是,最大线程满了,阻塞队列也满了, 就做拒绝。
/** * The default rejected execution handler */ private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
有四个拒绝策略。ctrl+h打开。
6.5.3 关于最大线程数怎么确定
- IO密集型,比如频繁文件io磁盘落地和读取。
- cpu密集型,比如大量统计,累加,计算分析的任务。
如果电脑是2核4线程,通常核心线程数=2,最大线程数=4。如果IO密集型,最大线程数=8。
Runtime.getRuntime().availableProcessors() // 获得电脑的线程数
七、流 stream
根据一个例子来初始stream的魅力。通常的业务都会包括,给数据做筛选,排序,统计等等的操作。
public static void main(String[] args) {
List list = new ArrayList<>();
list.add(new Person(1, "Tom", 23));
list.add(new Person(2, "Tomson", 32));
list.add(new Person(3, "Jack", 12));
list.add(new Person(4, "Jerry", 56));
list.add(new Person(5, "Betty", 48));
list.add(new Person(6, "James", 22));
System.out.println("筛选");
List list1 = list.stream()
.filter(person -> person.getId() % 2 == 0)
.filter(person -> person.getAge() > 22)
.sorted(Comparator.comparing(Person::getName).reversed()) // 按照name逆序
.collect(Collectors.toList());
list1.forEach(person -> System.out.println(person));
System.out.println("转小写字母");
list1.stream()
.map(person -> {
person.setName(person.getName().toLowerCase());
return person;
})
.forEach(System.out::println);
}
7.1 针对数值类型 IntStream
int[] arr = new int[]{2, 1, 34, 7, 99};
System.out.println(IntStream.of(arr).sum());
7.2 中间操作和终止操作
- 比如 map , filter 就是典型的 中间操作, 返回的仍是 stream 。
- collect,foreach就是终止操作,产生了结果,哪怕打印,也是个结果。
- 如果一串流处理,最后没有 终止操作,这些中间操作是惰性求值,实际上跟没做一样。
7.3 创建流
举例
Stream.generate(() -> {return new Random().nextInt(100)+50;})
.limit(10).forEach(System.out::println);
ArrayList arrayList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
arrayList.add(i);
}
arrayList.stream().filter(num -> num % 2 == 0)
.sorted((i,j) -> i-j).sorted(Comparator.reverseOrder())
.forEach(System.out::println);
7.4 中间操作
无状态操作,对于操作顺序没要求。有转态,就要等之前的操作做完,再做。
7.5 终止操作
短路操作,对于 无限流是非常有必要的。
7.6 并行流, 使用多线程 提供效率。
stream().parallel()