Core Java™, Volume I–Fundamentals

(Eleventh Edition) Book Notes

Featured image

第1章 Java程序设计概述

Java程序设计平台                  Java发展简史
Java“白皮书”的关键术语    Java applet与Internet

1.1 程序设计平台

1.2 Java“白皮书”的关键术语

1.2.1 简单性

1.2.2 面向对象

1.2.3 分布式

1.2.4 健壮性

1.2.5 安全性

1.2.6 体系结构中立

1.2.7 可移植性

1.2.8 解释型

1.2.9 高性能

1.2.10 多线程

1.2.11 动态性

1.3 Java applet 与 Internet

1.4 Java发展简史


第2章 Java程序设计环境

术语名 缩写 解释
Java Development Kit(Java开发工具包) JDK 编写Java程序的程序员使用的软件
Java Runtime Environment(Java运行时环境) JRE 运行Java程序的用户使用的软件
Server JRE(服务器JRE) —— 在服务器上运行Java程序的软件
Standard Edition(标准版) SE 用于桌面或简单服务器应用的Java平台
Enterprise Edition(企业版) EE 用于复杂服务器应用的Java平台

第3章 Java的基本程序设计结构

数据类型    变量与常量
运算符       字符串
输入输出    控制流
大数           数组

3.1 数据类型

3.1.1 整型

类型 存储需求 取值范围
int 4字节 -2 147 483 648 ~ 2 147 483 (24*8
short 2字节 -32 768 ~ 32767 (22*8
long 8字节 -9 223 372 036 854 775 808 ~ 9 223 372 036 854 775 807 (28*8
byte 1字节 -128 ~ 127 (21*8

3.1.2 浮点型

类型 存储需求 取值范围
float 4字节 大约±3.402 823 47E+38F(有效位数为6~7位)
double 8字节 大约±1.797 693 134 862 315 70E+308(有效位数为15位)

3.1.3 char类型

3.1.4 boolean类型

3.2 变量与常量

3.2.1 变量声明与初始化

var n = 12; //number is an int
var s = "Lam"; //s is a String

3.2.2 常量

final double PI = 3.14;

3.2.3 枚举类型

enum Size { SMALL, MEDIUM, LARGE, EXTRA_LARGE };
//现在可以声明这种类型的变量
Size s = Size.MEDIUM;

3.3 运算符

3.3.1 算术运算符

3.3.2 数学函数与常量

double y = Math.pow(x,a); //将y的值设置为x的a次幂
double x = Math.PI //将x的值设置为π

3.3.3 强制类型转换

double x = 9.997;
int nx = (int) x; //nx = 9,截取整数部分
int nx = (int) Math.round(x); //nx =10,四舍五入

3.3.4 位运算符

1 << 35 = 1 << 3;

3.4 字符串

3.4.1 子串

String greeting = "Hello";
String s = greeting.substring(0,3); // s = "Hel"

3.4.2 拼接

//join方法
String all = String.join("/","S","M","L","XL"); // all is "S/M/L/XL";
//repeat方法
String repeated = "LAM".repeat(3);// repeated is "LAMLAMLAM";

3.4.3 检测字符串是否相等

s.equals(t); //返回值为 true 或者 false

3.4.4 空串与Null串

if (str != null && str.length() != 0) //先检查是否null,因为对null串调用length()方法会报错

3.4.5 构建字符串

StringBuilder sb = new StringBuilder();
sb.append(ch);// appends a single character
sb.append(str);// appends a string
String completedString = sb.toString(); 

3.5 输入与输出

3.5.1 读取输入

Scanner in = new Scanner(System.in);
String name = in.nextLine();

nextLine() 读一行
next() 读一次(空格结束)
nextInt() 读一整数

3.5.2 格式化输出

System.out.println("LAM"); // print LAM

3.5.3 文件输入与输出

//文件输入

//方法一
Scanner in = new Scanner(Path.of("inputfile.txt"),StandardCharsets.UTF_8);
String s1=in.nextLine();

//方法二(性能高) 
BufferedReader bf = new BufferedReader(new FileReader("inputfile.txt"));
String s2=bf.readLine(); //BufferedReader的read()方法返回int类型。

//...

//文件输出
PrintWriter pw = new PrintWriter("outputfile.txt",StandardCharsets.UTF_8);
pw.print("hello");
pw.close();// 必要

3.5.4 中断控制流程的语句

break; // 终止循环
continue // 终止本次循环,跳到循环首部(进行下一次循环)。

3.6 大数

3.7 数组

3.7.1 声明数组

int[] a = new int[100]; // or var a = new int[100];

3.7.2 for each 循环

for (int e : a){
    System.out.println(e); // 打印数组a中每一个元素
}

3.7.3 数组拷贝

int[] lunckyNumbers = smallPrimes;
luckyNumbers[5] = 12; // now smallPrimes[5] is also 12
//即不会新建数组,本质上是同一个数组。
inr[] copy = Arrays.copyOf(luckyNumbers, luckyNumbers.length); // copy是一个新的数组

3.7.4 数组排序

// int[] a = {3,1,2};
Arrays.sort(a); //  升序,优化的快速排序(QuickSort)算法
// now a = {1,2,3}

3.7.5 多维数组

第4章 对象与类

面对对象程序设计概述
对象构造
使用预定义类

用户自定义类
静态字段与静态方法
文档注释 方法参数
类设计技巧


4.1 面对对象程序设计概述

4.1.1 类

4.1.2 对象

4.1.3 识别类

4.1.4 类之间的关系

4.2 使用预定义类

4.2.1 对象与对象变量

4.3 静态字段与静态方法

4.3.1 静态字段

class Employee{
    private static int nextId = 1;
    private int id;
    ...
}

每一个Employee对象都有一个自己的id字段,但这个类的所有实例将共享一个nextId字段。

4.3.2 静态常量

public class Math{
    ...
    public static final double PI = 3.14159265358979323846;
    ...
}

在程序中我们就可以使用Math.PI来访问这个常量。

4.3.3 静态方法

4.4 方法参数

4.5 对象构造

4.5.1 重载

4.5.2 默认字段初始化

4.5.3 无参数的构造器

4.5.4 参数名

public Employee(String name, double salary){
    this.name = name;
    this.salary = salary;
}

4.6 包

4.7 文档注释

4.7.1 类注释

4.7.2 方法注释

/**
* @param variable description 参数
* @return description 返回值
* @throws class description 可能的异常
*/

4.7.3 通用注释

/**
* @author name 作者
* @version text 版本
*/

4.8 类设计技巧

  1. 一定要保证数据私有。
  2. 一定要对数据进行初始化。
  3. 不要在类中使用过多的基本类型。
  4. 不是所有的字段都需要单独的字段访问器和字段更改器。
  5. 分解有过多职责的类。
  6. 类名和方法名要能够体现它们的职责。
  7. 优先使用不可变的类。

第5章 继承

类、超类和子类
参数数量可变的方法
Object:所有类的超类
枚举类
泛型数组列表
反射
对象包装器与自动装箱
继承的设计技巧

5.1 类、超类和子类

5.1.1 定义子类

/* public class Employee{
    ...
    int getSalary(){
        ...
    }
}
*/
public class Manager extends Employee{
    //子类特有字段
    private double bonus;
    ...
    //调用父类中的方法
    super.getSalary();
}

5.1.2 子类构造器

public Manager(String name, double salary, int year, int month, int day){
    super(name, salary, year, month, day);
    bonus = 0;
}

5.1.3 继承层次

5.1.4 阻止继承:final类和方法

public final class Example{
    ...
}
public class Employee{
    ...
    public final String getName(){
        return name;
    }
    ...
}

5.1.5 抽象类

5.1.6 受保护的访问

作用域 当前类 同一package 子孙类 其他package
public
protected ×
friendly(不修饰) × ×
private × × ×

5.2 Object: 所有类的超类

5.2.1 Object类型的变量

Object obj = new Employee("Lam",35000);

5.2.2 equals方法

5.2.3 相等测试与继承

5.3 泛型数组列表

5.3.1 声明数组列表

ArrayList<Employee> staff = new ArrayList<Employee>();
//or ArrayList<Employee> staff = new ArrayList<>();

// Java 10中,避免重复写类名
var staff = new new ArrayList<Employee>();
//添加
staff.add(new Employee("Lam", ...);

//元素个数
staff.size();

5.3.2 访问数组列表元素

//set 修改第i个元素
staff.set(i,lam); // var lam = new Employee("Lam", ...);

//get 获取第i个元素
staff.get(i);

5.4 对象包装器与自动装箱

var list = new ArrayList<Integer>(); // instead of new ArrayList<int>();

5.5 反射

5.5.1 Class类

Employee e;
...
Class cl = e.getClass();
System.out.println(e.getClass().getName() + " " + e.getName());
// if Eployee e, print "Employee Lam"
// if Manager e, print "Manager Lam"

5.5.2 声明异常入门

public static void doSomethingWithClass(String name)
    throws ReflectiveOperationException{
        Class cl = Class.forName(name); // might throw exception
        ...
    }

5.6 继承的设计技巧

  1. 将公共操作和字段放在超类中。
  2. 不要使用受保护的字段。
  3. 使用继承实现“is-a”关系。
  4. 除非所有继承的方法都有意义,否则不要使用继承。
  5. 在覆盖方法时,不要改变预期的行为。
  6. 使用多态,而不要使用类型信息。
  7. 不要滥用反射。

第6章 接口、lambda表达式与内部类

接口
服务加载器
lambda表达式
代理
内部类

6.1 接口

6.1.1 接口的概念

// Comparable是一个接口
class Employee implements Comparable

6.1.2 接口的属性

x = new Comparable(...); // ERROR
Comparable x = new Employee(...); // OK provided Employee implements Comparable

6.1.3 接口与抽象类

6.1.4 静态和私有方法

6.1.5 接口与回调

import java.awt.*;
import java.awt.event.*;
import java,time.*;
import javax.swing.*;

public class TimerTest{
    public static void main(String[] args){
        var listener = new TimePrinter();
        // construct a timer that calls the listener
        // once every second
        var timer = new Timer(1000, listener);
        timer,start();
        

    }
}

6.1.6 对象克隆

var original = new Employee("Lam", 50000);
Employee copy = original; // 本质上是同一个对象
copy.raiseSalary(10); // oops--also changed original
Employee copy = original.clone();
copy.raiseSalary(10); // OK--just copy changed

6.2 lambda表达式

6.2.1 为什么引入lambda表达式

6.2.2 lambda表达式的语法

(String first, String second) -> 
{
    if(first.length() < second.length()) return -1;
    else if (first.length() > second.length()) return 1;
    else return 0;
}

6.3 内部类


第7章 异常、断言和日志

处理错误
使用断言
捕获异常
日志
使用异常的技巧
调试技巧

7.1 处理错误

7.1.1 异常分类

image

7.1.2 声明检查型异常

7.1.3 如何抛出异常

String readData(Scanner in) throws EOFException{
    ...
    while(...){
        if (!in.hasNext()) // EOF encountered
        {
            if (n < len)
                throw new EOFException();
        }
        ...
    }
    return s;
}

7.1.4 创建异常类

class FileFormatException extends IOException{
    public FileFormatException() {
    
    }
    public FileFormatException(String gripe){
        super(gripe);
    }
}
String readData(BufferedReader in) throws FileFormatException{
    ...
    while(...){
        if (ch == -1) // EOF encountered
        {
            if (n < len)
                throw new FileFormatException();
        }
        ...
    }
    return s;
}

7.2 捕获异常

7.2.1 捕获异常

try {
    code
    more code
    more code
}
catch (ExceptionType e){
    handler for this type
}

7.2.2 捕获多个异常

try {
    code that might throw exceptions
}
catch (FileNotFoundException e){
    emergency action for missing files
}
catch (IOException e){
    emergency action for all other I/O problems
}
...

7.2.3 再次抛出异常与异常链

try {
    access the database
}
catch (SQLException e) {
    throw new ServletException("database error: " + e.getMessage());
}

7.2.4 finally子句

var in = new FileInputStream(...);
try {
    // 1
    code that might throw exceptions
    // 2
}
catch (IOException e){
    // 3
    show error message
    // 4
}
finally {
    // 5
    in.close();
}
// 6
public static int parseInt(String s) {
    try {
        return Integer.parseInt(s);
    }
    finally {
        return 0;
    }
}
// will return 0

7.2.5 分析堆栈轨迹元素

7.3 使用异常的技巧

  1. 异常处理不能代替简单的测试。
  2. 不要过分的细化异常。
  3. 充分利用异常层次结构。
  4. 不要压制异常。
  5. 在检测错误时,“苛刻”要比放任好。
  6. 不要羞于传递异常。

7.4 使用断言

7.4.1 断言的概念

7.4.2 启用和禁用断言

7.4.3 使用断言完成参数检查

7.5 日志

7.5.1 基本日志

Logger.getGlobal().info("File->Open menu item selected");

/* will print:

 May 10,2013 10:12:15 PM LoggingImageViewer fileOpen
 INFO: File->Open menu item selected
 
 */

7.5.2 高级日志

private static final Logger myLogger = Logger.getLogger("com.mycompany.myapp");
logger.setlevel(Level.FINE); // FINE及所有更高级别的日志都会记录。

7.5.3 处理器

7.5.4 日志技巧

  1. 对于一个简单的应用,选择一个日志记录器。
  2. 默认的日志配置会把级别等于或高于INFO的所有消息记录到控制台。

第8章 泛型程序设计

限制与局限性
定义简单泛型类
泛型类型的继承规则
泛型方法
通配符类型

8.1 为什么要使用泛型程序设计

8.1.1 类型参数的好处

var list = new ArrayList<String>();

8.1.2 谁想成为泛型程序员

8.2 定义简单泛型类

public class Pair<T> {
    private T first;
    private T second;
    
    public Pair() { first = null; second = null;}
    public Pair(T first, T second) { this.first = first; this.second = second;}
    
    public T getFirst() { return first;}
    public T getSecond() { return second;}
    
    public void setFirst(T newValue) { first = newValue;}
    public void setSecond(T newValue) { second = newValue;}
}

8.3 泛型方法

class ArrayAlg {
    public static <T> T getMiddle(T...a) {
        return a[a.length / 2];
    }
}

8.4 类型变量的限定

public static <T extends Comparable> T min(T[] a)...
//尖括号中语句表示 限制T只能是实现了Comparable接口的类。

8.5 泛型代码和虚拟机


第9章 集合

Java集合框架
视图与包装器
集合框架中的接口
算法
具体集合
遗留的集合
映射

9.1 Java集合框架

9.1.1 集合接口与实现分离

9.1.2 Collection接口

public interface Collection<E> {
    boolean add(E element);
    Iterator<E> iterator();
    ...
}

9.1.3 迭代器

public interface Iterator<E> {
    E next();
    boolean hasNext();
    void remove();
    default void forEachRemaining(Consumer<? super E> action);
}

9.2 集合框架中的接口

image

9.3 具体集合

9.3.1 链表

9.3.2 数组列表

9.3.3 散列集

9.3.4 树集

9.3.5 队列与双端队列

9.3.6 优先队列

9.4 映射

9.4.1 基本映射操作

9.4.2 更新映射条目

counts.put(word, counts.get(word) + 1);
// 第一次看到word时,get会返回null,会出现NullPointerException异常

/** 补救方法 1 
  * getOrDefault方法 
  * 若未在映射中找到这个键,
  * 则返回defaulValue。
  */
counts.put(word, counts.getOrDefault(word, 0) + 1);

/** 补救方法 2
  * 只有当键原先存在(或者映射到null)时,
  * 才会放入一个值。
  */
counts.putIfAbsent(word, 0);
counts.put(word, counts.get(word) + 1);

/** 补救方法 3
  * merge方法
  * 若键原先不存在,
  * word会与 1 关联,
  * 否则使用Integer::sum将原值与1求和。
  */
counts.merge(word, 1, Integer::sum);

9.4.3 映射视图

Set<K> keySet() // return 键集

Collection<V> values() // return 值集合

Set<Map.Entry<K, V>> entrySet() // return 键/值对集

9.4.4 弱散列映射

9.4.5 链接散列集与映射

9.4.6 枚举集与映射

9.4.7 标识散列映射

9.5 视图与包装器

9.5.1 小集合

List<String> names = List.of("Peter", "Paul", "Mary");
Set<Integer> numbers = Set.of(2, 3, 5);
// 会分别生成包含3个元素的一个列表和一个集

Mao<String, Integer> scores = Map.of("Peter",2, "Paul",3, "Mary",5);
// 生成一个映射

9.5.2 子范围

List<Employee> group2 = staff.subList(10,20);
// subList方法与String类中的substring方法类似

9.5.3 不可修改的视图

9.5.4 同步视图

var map = Collections.synchronizeMap(new HashMap<String, Employee());
// Collections类的静态synchronizedMap方法可以将任何一个映射转换成有同步访问方法的Map

9.5.5 检查型视图

9.6 算法

9.6.1 为什么使用泛型算法

9.6.2 排序与混排

var staff = new LinkedList<String>();
fill collection
Collection.sort(staff);
// Collections类中的sort方法可以对实现了List接口的集合进行排序。

9.6.3 批操作

coll1.removeAlL(coll2); // 从集合1中删除集合2中出现的所有元素

coll2.retainAll(coll2); // 从集合1中删除所有未在集合2中出现的元素

9.7 遗留的集合

9.7.1 属性映射

9.7.2 位集


第10章 图形用户界面程序设计


第11章 Swing用户界面组件


第12章 并发

什么是线程
线程状态
线程属性
同步
线程安全的集合
任务和线程池
异步计算
进程

12.1 什么是线程

12.2 线程状态

12.2.1 新建线程

12.2.2 可运行线程

12.2.3 阻塞和等待线程

  1. 当一个线程试图获取一个内部的对象锁,而这个锁目前被其他线程占有,该线程就会被阻塞。
  2. 当线程等待另一个线程通知调度器出现一个条件时,这个线程就会进入等待状态。
  3. 有几个方法有超时参数,调用这些方法会让线程进入计时等待(timed waiting)状态。

12.2.4 终止进程

12.3 线程属性

12.3.1 中断线程

while(!Thread.currentThread().isInterrupted()) {
    do more work
}

12.3.2 守护线程

12.3.3 线程名

var t = new Thread(runnable);
t.setName("Web crawler");

12.3.4 未捕获异常的处理器

12.3.5 线程优先级

MIN_PRIORITY: 1
MAX_PRIORITY: 10
NORM_PRIORITY: 5

12.4 同步

12.4.1 竞态条件的一个例子

// Bank.java

package threads;

import java.util.Arrays;

public class Bank {
    private final double[] accounts;

    public Bank(int n, double initialBalance) {
        accounts = new double[n];
        Arrays.fill(accounts,initialBalance);
    }

    public void transfer(int from, int to, double amount){
        if(accounts[from]<amount) return;
        System.out.println(Thread.currentThread());
        accounts[from] -= amount;
        System.out.printf(" %10.2f from %d to %d", amount, from, to);
        accounts[to] += amount;
        System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
    }

    public double getTotalBalance() {
        double sum = 0;

        for (double a : accounts)
            sum += a;

        return sum;
    }

    public int size(){
        return accounts.length;
    }
}
// UnsynchBankTest.java
package threads;

public class UnsynchBankTest {
    public static final int NACCOUNTS = 100;
    public static final double INITIAL_BALANCE = 1000;
    public static final double MAX_AMOUNT = 1000;
    public static final int DELAY = 10;

    public static void main(String[] args) {
        var bank = new Bank(NACCOUNTS, INITIAL_BALANCE);
        for (int i = 0; i < NACCOUNTS; i++) {
            int fromAccount = i;
            Runnable r = () -> {
                try {
                    while (true) {
                        int toAccount = (int) (bank.size() * Math.random());
                        double amount = MAX_AMOUNT * Math.random();
                        bank.transfer(fromAccount, toAccount, amount);
                        Thread.sleep((int) (DELAY * Math.random()));
                    }
                } catch (InterruptedException e) {

                }
            };
            var t = new Thread(r);
            t.start();
        }
    }
}

12.4.2 锁对象

myLock.lock(); // a ReentrantLock object
try {
    crirical section
}
finally {
// unlock操作必须,否则,其他线程将永远阻塞
    myLock.unlock(); // make sure the lock is unlocked even if an exception is thrown
}
public class Bank {
    ReentrantLock bankLock = new ReentrantLock();
    ...
    public void transfer(int from, int to, int amount) {
        bankLock.lock();
        try {
            System.out.print(Thread.currentThread());
            accounts[from] -= amount;
            System.out.printf(" %10.2f from %d to %d", amount, from, to);
            accounts[to] += amount;
            System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
        }
        finally {
            bankLock.unlock();
        }
    }
}

12.4.3 条件对象

// 错误方式
if(bank.getBalance(from) >= amount) {
    // thread might be deactivated at this point
    bank.transfer(from, to, amount);
}

// 正确方式
public void transfer(int from, int to, int amount) {
    bankLock.lock();
    try {
        while(accounts[from] < amount) {
            // wait
            ...
        }
        // transfer funds
        ...
    }
    finally {
        bankLock.unlock();
    }
}
package threads;

import java.util.Arrays;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Bank {
    private final double[] accounts;
    // ReentrantLock bankLock = new ReentrantLock();
    private Lock bankLock;
    private Condition sufficientFunds;

    public Bank(int n, double initialBalance) {
        accounts = new double[n];
        Arrays.fill(accounts,initialBalance);
        bankLock = new ReentrantLock();
        sufficientFunds = bankLock.newCondition(); // new一个条件对象
    }


    public void transfer(int from, int to, double amount) throws InterruptedException{
        bankLock.lock();
        try {
        /** 当发现资金不足,调用sufficientFunds.await();
          * 当前线程暂停,并放弃锁。
          * 这就允许另一个线程执行。
          */
            while(accounts[from] < amount)
                sufficientFunds.await();
            System.out.println(Thread.currentThread());
            accounts[from] -= amount;
            System.out.printf(" %10.2f from %d to %d", amount, from, to);
            accounts[to] += amount;
            System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
        /** 当另一个线程完成转账时,调用sufficientFunds.signalAll();
          * signalAll方法通知等待线程,
          * 现在有可能满足条件,值得再次检查。
          */
            sufficientFunds.signalAll();
        }
        finally {
            bankLock.unlock();
        }
    }

    public double getTotalBalance() {
        bankLock.lock();
        try{

        double sum = 0;

        for (double a : accounts)
            sum += a;

        return sum;
        }
        finally {
            bankLock.unlock();
        }

    }

    public int size(){
        return accounts.length;
    }
}

12.4.4 synchronized关键字

public synchronized void method() {
    method body
}
// 等价于
public void method() {
    this.intrinsicLock.lock();
    try {
        method body
    }
    finally {
        this.intrinsicLock.unlock();
    }
}

12.4.5 同步块

synchronized(obj) // this is the syntax for a synchronized block
{
    critical section
}
// 会获得obj的锁

12.4.6 监视器概念

12.4.7 volatile字段

// 1. 使用锁
private boolean done;
public synchronized boolean isDone() { return done; }
public synchronized bollean setDone() { done = true; }

// 2. 使用volatile字段
private volatile boolean done;
public boolean isDone() { return done; }
public void setDone() { done = true; }

12.4.8 final变量

final var accounts = new HashMap<String, Double>();
/** 其他线程会在构造器完成构造之后才看到这个accounts变量。
  * 若不使用final,
  * 就无法保证其他线程看到的是accounts更新后的值,
  * 它们可能都只是看到null,而不是新构造的HashMap。
  */

12.4.9 原子性

12.4.10 死锁

账户1 $200
账户2 $300
线程1 从账户1转$300到账户2
线程2 从账户2转$400到账户1

12.4.11 线程局部变量

12.4.12 为什么废弃stop和suspend方法

12.5 线程安全的集合

12.5.1 阻塞队列

方法 正常动作 特殊情况下动作
add 添加一个元素 如果队列满,则抛出IllegalStateException异常
element 返回队头元素 如果队列空,则抛出NoSuchElementException异常
offer 添加一个元素并返回true 如果队列满,则返回false
peek 返回队头元素 如果队列空,则返回null
poll 移除并返回队头元素 如果队列空,则返回null
put 添加一个元素 如果队列满,则阻塞
remove 移除并返回队头元素 如果队列空,则抛出NoSuchElementException异常
take 移除并返回队头元素 如果队列空,则阻塞

12.5.2 高效的映射、集和队列

12.5.3 映射条目的原子更新

// 1
map.compute(word, (k, v) -> v == null ? 1 v + 1);

// 2 
map.merge(word, 1L, (existingValue, new Value) -> existingValue + newValue);
// or  map.merge(word, 1L, Long::sum);

12.5.4 对并发散列映射的批操作

  1. search(搜索)为每个键或值应用一个函数,直到函数生成一个非null的结果。然后搜索终止,返回这个函数的结果。
  2. reduce(规约)组合所有键或值,这里要使用所提供的一个累加函数。
  3. forEach为所有键或值应用一个函数。

12.5.5 并发集视图

// keySet方法可以生成一个映射的键集
Set<String> words = map.keySet(1L);
words.add("Java");
// 如果“Java”在words中不存在,现在它会有一个值1。

12.5.6 写数组拷贝

12.5.7 并行数组算法

12.5.8 较早的线程安全集合

List<E> synchArrayList = Collections.synchronizedList(new ArrayList<E>())
Map<K, V> synchHashMap = Collections.synchronizedMap(new HashMap<K, V>());

12.6 任务和线程池

12.6.1 Callable与Future

public interface Callable<V> {
    V call() throws Exception;
}

12.6.2 执行器

方法 描述
newCachedThreadPool 必要时创建线程;空闲线程会保留60秒
newFixedThreadPool 池中包含固定数目的线程;空闲线程会一直保留
newWorkStealingPool 一种适合“fork-join”任务的线程池,其中复杂的任务会分解为更简单的任务,空闲线程会“密取”较简单的任务
newSingleThreadExecutor 只有一个线程的“池”,会顺序地执行所提交的任务
newScheduledThreadPool 用于调度执行的固定线程池
newSingleThreadScheduledExecutor 用于调度执行的单线程“池”

12.6.3 控制任务组

12.6.4 fork-join框架

if (problemSize < threshold) {
    solve problem directly
}
else {
    break problem into subproblems
    recursively solve each subproblem
    combine the results
}
class Counter extends RecursiveTask<Integer> {
    ...
    protected Integer compute() {
        if (to - from < THRESHOLD) {
            solve problem directly
        }
        else {
            int mid = (from + to) / 2;
            var first = new Counter(values, from, mid, filter);
            var second = new Counter(values, mid, to, filter);
            invokeAll(first, second);
            return first.join() + second.join();
        }
    }
}
/** invokeAll方法接收到很多任务并阻塞,
  * 直到所有这些任务全部完成。
  * join方法将生成结果。
  * 对每个子任务调用join,
  * 并返回其总合。
  */

12.7 异步计算

12.7.1 可完成Future

CompletableFuture<String> f = ...;
f.thenAccept(s -> Process the result string s);

12.8 进程

12.8.1 建立一个进程

var builder = new ProcessBuilder("gcc", "myapp.c");

12.8.2 运行一个进程

12.8.3 进程句柄

long pid = handle.pid();
Optional<ProcessHandle> parent = handle.parent();
Stream<ProcessHandle> children = handle.children();
Stream<ProcessHandle> descendants = handle.descedants();

12.9 线程补充

/** 方式1 继承于Thread类
  */ 
package threads;
/**
 *
 * 创建三个窗口卖票,总票数为100张,使用继承自Thread方式
 * 用静态变量保证三个线程的数据独一份
 *
 * 存在线程的安全问题,有待解决
 *
 * */

public class ThreadDemo extends Thread{

    public static void main(String[] args){
        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window();

        t1.setName("售票口1");
        t2.setName("售票口2");
        t3.setName("售票口3");

        t1.start();
        t2.start();
        t3.start();
    }

}

class Window extends Thread{
    private static int ticket = 100; //将其加载在类的静态区,所有线程共享该静态变量

    @Override
    public void run() {
        while(true){
            if(ticket>0){
                //                try {
                //                    sleep(100);
                //                } catch (InterruptedException e) {
                //                    e.printStackTrace();
                //                }
                System.out.println(getName()+"当前售出第"+ticket+"张票");
                ticket--;
            }else{
                break;
            }
        }
    }
}
/** 方式2 实现Runable接口方式
  */
package threads;

public class ThreadDemo1 {
    public static  void main(String[] args){
        Window1 w = new Window1();

        //虽然有三个线程,但是只有一个窗口类实现的Runnable方法,由于三个线程共用一个window对象,所以自动共用100张票

        Thread t1=new Thread(w);
        Thread t2=new Thread(w);
        Thread t3=new Thread(w);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

class Window1 implements Runnable{
    private int ticket = 100;

    @Override
    public void run() {
        while(true){
            if(ticket>0){
                //                try {
                //                    sleep(100);
                //                } catch (InterruptedException e) {
                //                    e.printStackTrace();
                //                }
                System.out.println(Thread.currentThread().getName()+"当前售出第"+ticket+"张票");
                ticket--;
            }else{
                break;
            }
        }
    }
}
/** 方式3 实现callable接口方式
  */
package threads;


import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * 创建线程的方式三:实现callable接口。---JDK 5.0新增
 * 是否多线程?否,就一个线程
 *
 * 比runable多一个FutureTask类,用来接收call方法的返回值。
 * 适用于需要从线程中接收返回值的形式
 *
 * //callable实现新建线程的步骤:
 * 1.创建一个实现callable的实现类
 * 2.实现call方法,将此线程需要执行的操作声明在call()中
 * 3.创建callable实现类的对象
 * 4.将callable接口实现类的对象作为传递到FutureTask的构造器中,创建FutureTask的对象
 * 5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start方法启动(通过FutureTask的对象调用方法get获取线程中的call的返回值)
 *
 * */

//实现callable接口的call方法
class NumThread implements Callable{

    private int sum=0;//

    //可以抛出异常
    @Override
    public Object call() throws Exception {
        for(int i = 0;i<=100;i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName()+":"+i);
                sum += i;
            }
        }
        return sum;
    }
}

public class ThreadNew {

    public static void main(String[] args){
        //new一个实现callable接口的对象
        NumThread numThread = new NumThread();

        //通过futureTask对象的get方法来接收futureTask的值
        FutureTask futureTask = new FutureTask(numThread);

        Thread t1 = new Thread(futureTask);
        t1.setName("线程1");
        t1.start();

        try {
            //get返回值即为FutureTask构造器参数callable实现类重写的call的返回值
            Object sum = futureTask.get();
            System.out.println(Thread.currentThread().getName()+":"+sum);
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
/** 方式4 使用线程池的方式
  */
package threads;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * 创建线程的方式四:使用线程池(批量使用线程)
 *1.需要创建实现runnable或者callable接口方式的对象
 * 2.创建executorservice线程池
 * 3.将创建好的实现了runnable接口类的对象放入executorService对象的execute方法中执行。
 * 4.关闭线程池
 *
 * */
class NumberThread implements Runnable{
    @Override
    public void run() {
        for(int i = 0;i<=100;i++){
            if (i % 2 ==0 )
                System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

class NumberThread1 implements Runnable{
    @Override
    public void run() {
        for(int i = 0;i<100; i++){
            if(i%2==1){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}

public class ThreadPool {

    public static void main(String[] args){

        //创建固定线程个数为十个的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        //new一个Runnable接口的对象
        NumberThread number = new NumberThread();
        NumberThread1 number1 = new NumberThread1();

        //执行线程,最多十个
        executorService.execute(number1);
        executorService.execute(number);//适合适用于Runnable

        //executorService.submit();//适合使用于Callable
        //关闭线程池
        executorService.shutdown();
    }

}