当前位置: 首页 > news >正文

1 Studying《Java编程思想》

目录

1 github仓

3 赋值、关系操作符

3.4 赋值

3.7 关系操作符

5 数组初始化

8 多态和构造器

8.2 多态

8.3 构造器和多态

9 接口(策略模式/工厂方法模式)

9.3 完全解耦

9.9 接口与工厂

10 内部类

10.6 匿名内部类

10.8 闭包与回调

11 容器1

11.4 容器的打印

11.11 Queue

11.13 Foreach与迭代器

11.14 总结

13 字符串

13.1 不可变String

13.2 StringBuilder

14 类型信息

14.1 为什么需要RTTI

14.6 反射

16 数组

16.2 对象数组与基本类型数组

16.3 返回数组

16.4 多维数组

16.7 Arrays实用功能

17 容器2

17.3 Collection的功能方法

17.4 可选操作

17.4.1 未获支持的操作

17.5 List

17.6 Set

17.7 Queue

17.8 Map

17.9 散列与散列码

17.11 实用方法

17.12 持有引用

18 Java I/O系统

18.1 File类

18.6 I/O流的典型使用方式

18.6.1 缓冲输入文件

18.6.2 从内存输入

18.6.4 基本的文件输出

18.6.5 存储和恢复数据

18.7 文件读写的实用工具

18.8 标准I/O

18.10 新I/O nio

18.10.1 转换数据

18.10.2 获取基本类型

18.10.3 视图缓冲器

18.10.5 缓冲器的细节

18.10.7 文件加锁

19 枚举类型

19.1 基本enum特性

19.4 values()的神秘之处

19.10 ConstantSpecificMethod

21 并发

21.2 基本的线程机制

21.2.1 定义任务

21.2.2 Thread类

21.2.3 使用Executor

21.2.4 从任务中产生返回值

21.2.9 编码的变体

21.2.11 加入一个线程

21.2.14 捕获异常

21.3 共享受限资源(互斥)

21.3.2 解决共享资源竞争

21.3.3 原子性与可视性

21.3.5 临界区

21.3.6 在其他对象上同步

21.4 终结任务(中断)

21.4.1 装饰性花园

21.4.2 在阻塞时终结

21.4.3 中断

21.4.4 检查中断

21.5 线程之间的协作(同步)

21.5.1 wait()与notifyAll()

21.5.4 生产者与消费者队列

21.5.5 任务间使用管道进行输入/输出

21.6 死锁

21.7 新类库中的构件

21.7.1 CountDownLatch 锁存器

21.7.2 CyclicBarrier 循环栅栏

21.7.3 DelayQueue 延迟同步队列

21.7.4 PriorityBlockingQueue 优先级同步队列

21.7.5 使用ScheduledExecutor的温室控制器

21.7.6 Semaphore 信号量

21.9 性能调优

21.9.1 比较Lock和synchronized

21.9.2 免锁容器

21.10 Future 活动对象


1 github仓

以下所有测试用例托管在如下github仓:
https://github.com/hanyuhang-hz/thinking-in-java.git
IntelliJ:

需要在main.java下Run!!!

3 赋值、关系操作符

3.4 赋值

PassObject

class PassObject {static class Letter {char c;};static void f(Letter y) {y.c = 'z';}public static void chapter3_4() {System.out.println("PassObject chapter3_4>>>");Letter x = new Letter();x.c = 'y';f(x);System.out.println(x.c);}
};输出:
PassObject chapter3_4>>>
z

类对象作为参数传递时是引用传递

3.7 关系操作符

Equivalence

class Equivalence {public static void chapter3_7() {Integer integer1 = new Integer(7);Integer integer2 = new Integer(7);System.out.println("integer1==integer2:" + (integer1==integer2));System.out.println("integer1.equals(integer2):" + integer1.equals(integer2));}
};输出:
integer1==integer2:false
integer1.equals(integer2):true

==:比较对象引用地址

equals:比较对象内容

5 数组初始化

 ArrayNew:初始化int数组和Integer数组

class ArrayNew {public static void chapter5_8() {int[] a = new int[10];Integer[] b = new Integer[10];Random random = new Random(7);for(int i=0;i<10;i++) {a[i] = random.nextInt(10);b[i] = random.nextInt(20);}System.out.println("a.length:" + a.length);System.out.println(Arrays.toString(a));System.out.println("b.length:" + b.length);System.out.println(Arrays.toString(b));}
};
输出:
a.length:10
[6, 5, 0, 8, 0, 0, 8, 5, 1, 7]
b.length:10
[4, 4, 14, 9, 14, 12, 11, 2, 18, 19]

NewVarArgs:可变参数列表

class NewVarArgs {static class A {}static void printObject(Object... args) {for(Object object:args) {System.out.println("object:" + object);}}public static void chapter5_8() {printObject(new Integer(7),3.14F,new A());}
};
输出:
object:7
object:3.14
object:thinkinjava.hyh.NewVarArgs$A@7a5d012c

8 多态和构造器

8.2 多态

Music:多态

class Instrument {void play() {System.out.println("Instrument!");}
};
class Wind extends Instrument {void play() {System.out.println("Wind!");}
};
class Percussion extends Instrument {void play() {System.out.println("Percussion!");}
};
class Music {static void tune(Instrument i) {i.play();}public static void chapter8_2() {Wind wind = new Wind();Percussion percussion = new Percussion();tune(wind);tune(percussion);}
};
输出:
Wind!
Percussion!

PrivateOverride:私有函数不支持多态,privateOverride.f()实际运行的是基类的f()。

class PrivateOverride {/*public void f() {System.out.println("PrivateOverride!");}*/private void f() {System.out.println("PrivateOverride!");}public static void chapter8_2() {PrivateOverride privateOverride = new Derived();privateOverride.f();((Derived)privateOverride).f();}
};
class Derived extends PrivateOverride {public void f() {System.out.println("Derived PrivateOverride!");}
}
输出:
PrivateOverride!
Derived PrivateOverride!
8.3 构造器和多态

构造器并不具有多态性,它们实际上是static方法,只不过声明是隐式的!!!

PolyConstructors:如果一个构造器的内部调用正在构造的对象的某个动态绑定方法,会发生灾难性的结果。

class Glyph {Glyph() {System.out.println("Glyph before draw:");draw();System.out.println("Glyph after draw:");}void draw() {System.out.println("Glyph draw()!");}
};
class RoundDraw extends Glyph {RoundDraw(int r) {radius = r;System.out.println("RoundDraw draw()!!! radius:" + radius);}private int radius = 2;void draw() {System.out.println("RoundDraw draw()! radius:" + radius);}
};
class PolyConstructors {public static void chapter8_3() {new RoundDraw(5);}
};
输出:
Glyph before draw:
RoundDraw draw()! radius:0
Glyph after draw:
RoundDraw draw()!!! radius:5

draw()前后radius打印不同,也就是说对于Glyph类来说打印draw看到的radius是个错误的值!!!

编写构造器时有一条有效的准则:"用尽可能简单的方法使对象进入正常状态,如果可以的话避免调用其他方法"

9 接口(策略模式/工厂方法模式)

9.3 完全解耦

Apply

interface Processor {String name();String process(String input);
};
class Upcase implements Processor {@Overridepublic String name() {return getClass().getSimpleName();}@Overridepublic String process(String input) {return input.toUpperCase();}
};
class Downcase implements Processor {@Overridepublic String name() {return getClass().getSimpleName();}@Overridepublic String process(String input) {return input.toLowerCase();}
};
class Apply {static void process(Processor processor, String input) {System.out.println(processor.process(input));}public static void chapter9_3() {String input = "xxx";process(new Upcase(), input);process(new Downcase(), input);}
};
输出:
hyh
HYH

策略模式:接口定义公共方法,具体子类定义具体实现,其实就是接口的使用。

9.9 接口与工厂

Factories

interface Service {void method();
};
class Implementation1 implements Service {@Overridepublic void method() {System.out.println("Implementation1 Service!");}
};
class Implementation2 implements Service {@Overridepublic void method() {System.out.println("Implementation2 Service!");}
};interface ServiceFactory {Service getService();
};
class ImplementationFactory1 implements ServiceFactory {@Overridepublic Service getService() {return new Implementation1();}
};
class ImplementationFactory2 implements ServiceFactory {@Overridepublic Service getService() {return new Implementation2();}
};
class Factories {public static void serviceConsumer(ServiceFactory serviceFactory) {Service service = serviceFactory.getService();service.method();}public static void chapter9_9() {serviceConsumer(new ImplementationFactory1());serviceConsumer(new ImplementationFactory2());};
};
输出:
Implementation1 Service!
Implementation2 Service!

工厂方法模式:定义一个用于创建对象的接口(ServiceFactory),让子类(Implementation1Factory)决定实例化哪一个类(new Implementation1())。

工厂方法使一个类(ServiceFactory)的实例化延迟到子类(Implementation1Fatory)。之后可以执行子类(Implementation1)的方法(Implementation1.method())。
 

10 内部类

10.6 匿名内部类

Parcel7

interface Contents {int value();
}
class Parcel7 {public Contents contents() {//(1)实现接口Contents的匿名内部类return new Contents() {@Overridepublic int value() {System.out.println("Contents value!");return 0;}};}public static void chapter10_6() {Parcel7 parcel7 = new Parcel7();//(2)子类向上转型为Contents接口Contents contents = parcel7.contents();//(3)多态,运行时执行匿名内部类的value函数contents.value();}
};
输出:
Contents value!

实现接口的匿名内部类

Parcel8

class Wrapping {private int i;public Wrapping(int x) {i = x;}public int value() {System.out.println("Wrapping i:" + i);return i;}
}
class Parcel8 {public Wrapping wrapping(int x) {// 继承基类的匿名内部类return new Wrapping(x) {public int value() {return super.value();}};}public static void chapter10_6() {Parcel8 parcel8 = new Parcel8();Wrapping wrapping = parcel8.wrapping(7);wrapping.value();}
}
输出:
Wrapping i:7

继承基类的匿名内部类

10.8 闭包与回调

闭包(closure)是一个可调用对象。 

内部类是面向对象的闭包,它自动拥有一个指向外围类对象的引用(.this)。在此作用域内,内部类有权操作所有成员,包括private成员。

人们认为Java应该包含某种类似指针的机制,以允许回调,通过内部类提供闭包的功能是优良的解决方案。

Callbacks

// 回调接口
interface Incrementable {void increment();
}// (1)Callee1使用接口实现回调
class Callee1 implements Incrementable {private int i = 0;@Overridepublic void increment() {i++;System.out.println("Callee1 i:" + i);}
}// (2)Callee2使用匿名内部类实现回调
class MyIncrementable {public void increment() {System.out.println("MyIncrementable");}
}
class Callee2 extends MyIncrementable {private int i = 7;public void increment() {super.increment();i+=3;System.out.println("Callee2 i:" + i);}// 匿名内部类// 内部类是面向对象的闭包,它自动拥有一个指向外围类对象的引用(.this)Incrementable callback() {return new Incrementable() {@Overridepublic void increment() {Callee2.this.increment();}};}
}// 回调类
class Caller {private Incrementable callback;Caller(Incrementable call) {callback = call;}void run() {callback.increment();}
}class Callbacks {public static void chapter10_8() {Callee1 callee1 = new Callee1();// (1)注册接口回调Caller caller1 = new Caller(callee1);// (1)调用回调caller1.run();Callee2 callee2 = new Callee2();// (2)注册内部类回调Caller caller2 = new Caller(callee2.callback());// (2)调用回调caller2.run();}
}
输出:
Callee1 i:1
MyIncrementable
Callee2 i:10

(1)使用接口实现回调

(2)使用内部类实现回调

11 容器1

11.4 容器的打印

PrintingContainers

class PrintingContainers {static Collection fill(Collection<String> collection) {collection.add("rat");collection.add("cat");collection.add("dog");collection.add("dog");return collection;}static Map fill(Map<String,String> map) {map.put("rat","1");map.put("cat","2");map.put("dog","3");map.put("dog","4");return map;}public static void chapter11_4() {//CollectionSystem.out.println(fill(new ArrayList<>()));System.out.println(fill(new LinkedList<>()));System.out.println(fill(new HashSet<>()));              //fastestSystem.out.println(fill(new TreeSet<>()));              //比较的顺序System.out.println(fill(new LinkedHashSet<>()));        //被添加的顺序//MapSystem.out.println(fill(new HashMap<>()));              //fastestSystem.out.println(fill(new TreeMap<>()));              //比较的顺序System.out.println(fill(new LinkedHashMap<>()));        //被添加的顺序}
}
输出:
[rat, cat, dog, dog]
[rat, cat, dog, dog]
[rat, cat, dog]
[cat, dog, rat]
[rat, cat, dog]
{rat=1, cat=2, dog=4}
{cat=2, dog=4, rat=1}
{rat=1, cat=2, dog=4}

(1)Collection:

List:ArrayList,LinkedList

Set:HashSet,TreeSet,LinkedHashSet

Queue:LinkedList

(2)Map:

HashMap,TreeMap,LinkedHashMap

11.11 Queue

QueueDemo

class QueueDemo {static void printQ(Queue queue) {while(queue.peek() != null) {System.out.print( " " + queue.remove());}System.out.println();}static void chapter11_11() {Queue<Integer> queue = new LinkedList<>();Random random = new Random(7);for(int i=0;i<10;i++) {queue.offer(random.nextInt(10));}printQ(queue);}
}
输出:
6 4 5 4 0 4 8 9 0 4

Queue是个接口,LinkedList实现了这个接口

PriorityQueueDemo

class PriorityQueueDemo {static void chapter11_11() {List<Integer> list = Arrays.asList(7,17,27,37,40);PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(list);QueueDemo.printQ(priorityQueue);priorityQueue = new PriorityQueue<>(list.size(),Collections.reverseOrder());priorityQueue.addAll(list);QueueDemo.printQ(priorityQueue);}
}
输出:
7 17 27 37 40
40 37 27 17 7

默认排序将使用对象在队列中的自然排序,可以通过自己的Comparator来修改这个顺序

11.13 Foreach与迭代器

ModifyingArraysAsList

class ModifyingArraysAsList {static void chapter11_13() {Random random = new Random(7);Integer[] ia = {1, 2, 3, 4, 5, 6, 7};List<Integer> list1 = new ArrayList<>(Arrays.asList(ia));System.out.println("before:" + list1);Collections.shuffle(list1,random);System.out.println("after:" + list1);System.out.println("Arrays.toString(ia):" + Arrays.toString(ia));System.out.println("ia:" + ia);List<Integer> list2 = Arrays.asList(ia);System.out.println("before:" + list2);Collections.shuffle(list2,random);System.out.println("after:" + list2);System.out.println("Arrays.toString(ia):" + Arrays.toString(ia));System.out.println("ia:" + ia);}
}
输出:
before:[1, 2, 3, 4, 5, 6, 7]
after:[6, 7, 2, 5, 1, 3, 4]
Arrays.toString(ia):[1, 2, 3, 4, 5, 6, 7]
ia:[Ljava.lang.Integer;@36f6e879
before:[1, 2, 3, 4, 5, 6, 7]
after:[4, 2, 5, 3, 1, 7, 6]
Arrays.toString(ia):[4, 2, 5, 3, 1, 7, 6]
ia:[Ljava.lang.Integer;@36f6e879

(1)Arrays.asList()被传递给了我ArrayList()构造函数,则新创建引用list1

(2)Arrays.asList()产生的list2对象会使用底层数组作为物理实现

11.14 总结

简单的容器分类

13 字符串

13.1 不可变String

Immutable

class Immutable {public static String upcase(String s) {return s.toUpperCase();}public static void test(String s) {s = "test";System.out.println("test:" + s);}public static void chapter13_1() {String q = "how";String qq = upcase(q);System.out.println(q);System.out.println(qq);test(q);System.out.println(q);}
}
输出:
how
HOW
test:test
how

String对象是不可变的。String类中的看起来会修改String值的方法(upcase/test),实际上都是创建了一个全新的String对象。

13.2 StringBuilder

UsingStringBuffer

class UsingStringBuilder {public String toString() {Random random = new Random(7);StringBuilder res = new StringBuilder("[");for (int i=0;i<5;i++) {res.append(random.nextInt(10));res.append(", ");}//[...),即删除res.length()-2和res.length()-1位置的字符res.delete(res.length()-2,res.length());res.append("]");return res.toString();}public static void chapter13_2() {UsingStringBuilder usingStringBuilder = new UsingStringBuilder();System.out.println(usingStringBuilder.toString());}
}
输出:
[6, 4, 5, 4, 0]

常用方法:append(),toString(),delete(),reverse(),substring()

14 类型信息

Java是如何让我们在运行时识别对象和类的信息的。主要有两种方式:

(1)RTTI,Runtime Type Information,运行时类型信息,它假定在编译时已经知道了所有的类型

(2)反射,它允许我们在运行时发现和使用类的信息

14.1 为什么需要RTTI

Shapes

abstract class Shape {void draw() {System.out.println(this + ".draw()!");}abstract public String toString();
}
class Circle extends Shape{@Overridepublic String toString() {return "Circle";}
}
class Square extends Shape{@Overridepublic String toString() {return "Square";}
};
class Shapes {public static void chapter14_1() {List<Shape> shapeList = Arrays.asList(new Circle(),new Square());   //(1)upcastfor(Shape s:shapeList) {        //(2)RTTI Object->Shapes.draw();                   //(3)polymorphic 多态}}
}
输出:
Circle.draw()!
Square.draw()!

(1)向上转型

(2)容器会自动将Object转型为Shape,这里是RTTI

(3)多态机制

14.6 反射

RTTI和反射的真正区别是,对于RTTI来说,编译器在编译时打开和检查.class文件;对于反射来说,.class文件在编译时是不可获取的,在运行时打开和检查.class文件。

ShowMethods

class ShowMethods {public ShowMethods() {System.out.println("test");}public static void chapter14_6() {try {Class<?> c = Class.forName("thinkinjava.hyh.ShowMethods");Method[] methods = c.getMethods();Constructor[] ctors = c.getConstructors();System.out.println("methods:");for(Method method:methods) {System.out.println(method.toString());}System.out.println("ctors:");for(Constructor constructor:ctors) {System.out.println(constructor.toString());}} catch (ClassNotFoundException e) {e.printStackTrace();}}
}
输出:
methods:
public static void thinkinjava.hyh.ShowMethods.chapter14_6()
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
ctors:
public thinkinjava.hyh.ShowMethods()

类方法提取器

16 数组

16.2 对象数组与基本类型数组

ArrayOptions

class BerylliumSphere {private static long counter;private final long id = counter++;public String toString() {return "Sphere:" + id;}
}
class ArrayOptions {public static void chapter16_2() {//(1)对象数组//(i)默认初始化BerylliumSphere[] a = new BerylliumSphere[3];System.out.println(Arrays.toString(a));//(ii)列表初始化BerylliumSphere[] b = new BerylliumSphere[] {new BerylliumSphere(), new BerylliumSphere()};System.out.println(Arrays.toString(b));//(2)基本类型数组//(i)默认初始化int[] c = new int[5];System.out.println(Arrays.toString(c));//(ii)静态初始化int[] d = {1,2,3,4,5};                // 同下,也是堆内存new出来的int[] d1 = new int[] {1,2,3,4,5};//(iii)动态初始化int[] e = new int[5];for(int i=0;i<5;i++){e[i] = i;}System.out.println(Arrays.toString(e));}
}
输出:
[null, null, null]
[Sphere:0, Sphere:1]
[0, 0, 0, 0, 0]
[0, 1, 2, 3, 4]

对象数组保存的是引用,基本类型数组直接保存基本类型的值。Java数组必须初始化后才能使用;Java数组是静态的,一但初始化指定长度后,这个长度就不能变了。

数组(无论对象数组还是基本类型数组)在java中属于引用数据类型,这些引用变量存储在一块名叫栈内存的区域,所谓“引用”就是Java对象在堆内存的地址赋给了栈内存的变量。

Java数组传递是引用传递。

Java基础数据类型与数组 - 浪客行翰 - 博客园

Java之路:数组引用传递_java数组是引用传递_星云999的博客-CSDN博客

证明int[] d = {1,2,3,4,5};分配的内存在堆上,不是栈上:

public class test {public static void main(String[] args) {// int[] a = new int[]{1,2,3,4,5};int [] b;{int[] a = {1, 2, 3, 4, 5};b = a;}test(b);print(b);}public static void test(int[] temp){for(int i=0;i<temp.length;i++){temp[i]*=3;}}public static void print(int[] temp1){for(int i=0;i<temp1.length;i++)System.out.println(temp1[i]);}
}
16.3 返回数组

返回一个数组与返回其他对象(实际上返回引用)没什么区别。垃圾回收器会自动清理数组,不必操心。

16.4 多维数组

MultiArray

class MultiArray {public static void chapter16_4() {Integer[][] a = new Integer[3][];for(int i=0;i<a.length;i++) {a[i] = new Integer[5];for(int j=0;j<a[i].length;j++) {a[i][j] = i+j;}}System.out.println(Arrays.deepToString(a));}
}
输出:
[[0, 1, 2, 3, 4], [1, 2, 3, 4, 5], [2, 3, 4, 5, 6]]

Arrays.deepToString()可以将多维数组转换为多个String

16.7 Arrays实用功能

Arrays类一套用于数组的static实用方法:

equals 数组的比较

deepEquals 多维数组的比较

fill 填充数组

sort 数组排序

binarySearch 在已经排序的数组中查找元素

hashCode 产生数组散列码

Arrays.asList 接受任意的序列或数组作为其参数,并将其转变为List容器

CopyingArrays

class CopyingArrays {public static void chapter16_7() {int[] src = new int[] {1,2,3,4,5};int[] dst = new int[10];System.arraycopy(src,0,dst,0,src.length);System.out.println(Arrays.toString(dst));Integer[] a = new Integer[5];Arrays.fill(a,7);Integer[] b = new Integer[10];System.arraycopy(a,0,b,5,a.length);System.out.println(Arrays.toString(b));a[4] = 11;System.out.println(Arrays.toString(a));System.out.println(Arrays.toString(b));}
}
输出:
[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
[null, null, null, null, null, 7, 7, 7, 7, 7]
[7, 7, 7, 7, 11]
[null, null, null, null, null, 7, 7, 7, 7, 7]

System.arraycopy(),用它复制数组比用for循环要快很多。

源数组,从源数组什么位置开始复制的偏移量,目标数组,从目标数组什么位置开始复制的偏移量,需要复制的元素个数

StringSorting

class StringSorting {public static void chapter16_7() {String[] str = new String[]{"Abcde","AAAAb","HxxHv","aaaaa"};System.out.println(Arrays.toString(str));Arrays.sort(str);System.out.println(Arrays.toString(str));int location = Arrays.binarySearch(str,"AAAAb");System.out.println("location:" + location);Arrays.sort(str,Collections.reverseOrder());System.out.println(Arrays.toString(str));//wrong//location = Arrays.binarySearch(str,"AAAAb");location = Arrays.binarySearch(str,"AAAAb", Collections.reverseOrder());System.out.println("location:" + location);Arrays.sort(str,String.CASE_INSENSITIVE_ORDER);System.out.println(Arrays.toString(str));location = Arrays.binarySearch(str,"AAAAb", String.CASE_INSENSITIVE_ORDER);System.out.println("location:" + location);}
}
输出:
location:0
[aaaaa, HxxHv, Abcde, AAAAb]
location:3
[aaaaa, AAAAb, Abcde, HxxHv]
location:1
[ten, tens]

数组排序:词典序,反词典序,忽略大小写词典序

17 容器2

17.3 Collection的功能方法

CollectionMethods

class CollectionMethods {public static void chapter17_3() {Collection<String> c = new ArrayList<>();c.add("ten");c.add("tens");System.out.println(c.toString());Object[] arr = c.toArray();System.out.println(arr.toString());for(int i=0;i< arr.length;i++){System.out.println(arr[i].toString());}String[] str = c.toArray(new String[0]);for(int i=0;i< arr.length;i++){System.out.println(str[i]);}c.clear();System.out.println(c.toString());}
}
17.4 可选操作

执行各种不同的添加和移除的方法在Collection接口中都是可选操作。这意味着实现类并不需要为这些方法提供功能定义。
将方法定义为可选的,是为了防止在设计类时出现接口爆炸的情况。
如果一个操作是未或支持的,那么调用接口的时候可能会导致UnsupportedOperationException异常。注意:未获支持的操作只有在运行时才能探测到,即动态类查。

17.4.1 未获支持的操作

最常见的未获支持的操作,都来源于背后由固定尺寸的数据结构支持的容器。
Unsupported

class Unsupported {static void test(String msg, List<String> list) {Collection<String> c = list;Collection<String> subList = list.subList(1,8); //[1,8)Collection<String> c2 = new ArrayList<>(subList);System.out.println("msg:" + msg);System.out.println(c.toString());try {c.retainAll(c2);System.out.println(c.toString());}catch (Exception e) {System.out.println("retainAll:" + e);}try {c.removeAll(c2);System.out.println(c.toString());}catch (Exception e) {System.out.println("removeAll:" + e);}try {c.add("A");c.add("C");System.out.println(c.toString());}catch (Exception e) {System.out.println("add:" + e);}try {c.remove("A");System.out.println(c.toString());}catch (Exception e) {System.out.println("remove:" + e);}try {list.set(0,"Z");System.out.println(list.toString());}catch (Exception e) {System.out.println("list.set:" + e);}}public static void chapter17_4() {List<String> list = Arrays.asList("A B C D E F G H I J".split(" "));test("Modifiable", new ArrayList<>(list));test("Arrays.asList", list);test("Unmodifiable", Collections.unmodifiableList(new ArrayList<>(list)));}
}

Arrays.asList()返回固定尺寸的List,仅支持那些不会改变数组大小的操作;

unmodifiableList()结果在任何情况都不可修改。

17.5 List

Lists基本用法

class Lists {static void basicTest(List<String> list) {System.out.println(list);list.add(1,"a");list.add("hyh");List<String> ls = Arrays.asList("ab cd".split(" "));list.addAll(ls);System.out.println(list);String s = list.get(2);int index = s.indexOf("hyh");System.out.println("s:" + s + " index:" + index);}static void iterMotion(List<String> list) {ListIterator<String> iter = list.listIterator();Boolean b = iter.hasNext();String s = iter.next();Integer i = iter.nextIndex();System.out.println("b:" + b + " s:" + s + " i:" + i);}static void iterManipulation(List<String> list) {ListIterator<String> iter = list.listIterator();iter.add("7");System.out.println(list);//删除"7"下一个元素iter.next();iter.remove();System.out.println(list);//替换"7"下一个元素iter.next();iter.set("xjy");System.out.println(list);}public static void chapter17_5() {List<String> list = Arrays.asList("a b c d e f".split(" "));//修改容器大小的操作不可直接传入Arrays.asList固定容器basicTest(new LinkedList<>(list));iterMotion(list);//修改容器大小的操作不可直接传入Arrays.asList固定容器iterManipulation(new LinkedList<>(list));}
}
17.6 Set

HashSet:快速查找,存入HashSet的元素必须定义hashCode(),equals()

TreeSet:保持次序的Set,元素必须实现Comparable接口

LinkedHashSet:保持插入次序的Set,存入HashSet的元素必须定义hashCode(),equals()

SortedSetDemo

class SortedSetDemo {public static void chapter17_6() {SortedSet<String> sortedSet = new TreeSet<>();Collections.addAll(sortedSet, "one two three four five".split(" "));System.out.println(sortedSet);System.out.println(sortedSet.first());System.out.println(sortedSet.last());String low = null,high = null;Iterator<String> iter = sortedSet.iterator();for(int i=0;i<sortedSet.size();i++) {String str = iter.next();if(i==1) {low = str;}if(i==sortedSet.size()-1) {high = str;}}System.out.println("low:" + low + " high:" + high);System.out.println(sortedSet.subSet(low,high));System.out.println(sortedSet.headSet(high));System.out.println(sortedSet.tailSet(low));}
}

first():返回容器中第一个元素
last():返回容器中最后一个元素
subSet(from,to): [from,to)的子集
headSet(to):小于to的子集
tailSet(from):大于等于from的子集

17.7 Queue

QueueBehavior

interface Generator<T> {T next();
}
class QueueBehavior {static void test(Queue queue, Gen gen) {for(int i=0;i<gen.size();i++) {queue.offer(gen.next());}while(queue.peek()!=null) {System.out.print(" " + queue.remove());}System.out.println("");}static class Gen implements Generator<String> {String[] str = "one two three four five".split(" ");int i = 0;public int size() {return str.length;}@Overridepublic String next() {return str[i++];}}public static void chapter17_7() {test(new LinkedList(), new Gen());test(new PriorityQueue(), new Gen());}
}

LinkedList:按照插入排序

PriorityQueue的排序方式分为两种,一种是自然排序,这是按照加入元素的大小从小到大排序的。第二种是定制排序,是使用comparator类来重写compare(Object o1,Object o2)方法来实现定制排序的。

PriorityQueue:默认自然数排序

PriorityQueue:实现Comparable排序

ToDoList:

class ToDoItem implements Comparable<ToDoItem> {private char primary;private int secondary;private String item;public ToDoItem(String td, char pri, int sec) {primary = pri;secondary = sec;item = td;}@Overridepublic int compareTo(ToDoItem o) {if(primary > o.primary) {return 1;}else if(primary == o.primary) {if(secondary > o.secondary) {return 1;}else if(secondary == o.secondary) {return 0;}}return -1;}public String  toString() {return Character.toString(primary) + secondary + " :" + item;}
}
class ToDoList extends PriorityQueue<ToDoItem> {void add(String td, char pri, int sec) {super.add(new ToDoItem(td,pri,sec));}public static void chapter17_7() {ToDoList toDoList = new ToDoList();toDoList.add("han1",'C',4);toDoList.add("han2",'A',1);toDoList.add("han3",'A',2);toDoList.add("han4",'B',3);toDoList.add("han5",'B',4);while(!toDoList.isEmpty()){System.out.println("" + toDoList.remove());}}
}
17.8 Map

HashMap:基于散列表的实现
LinkedHashMap:插入次序
TreeMap:基于红黑树的实现,次序由Comparable或Comparator决定
WeakHashMap:弱键映射,允许释放映射所指向的对象
ConcurrentHashMap:线程安全Map

class Maps {static void printKeys(Map<Integer,String> map) {System.out.println("map.size():" + map.size());System.out.println("map.keySet():" + map.keySet());System.out.println("map.values():" + map.values());}static void test(Map<Integer,String> map) {map.put(3,"han3");map.put(4,"han4");map.put(1,"han1");map.put(2,"han2");printKeys(map);Integer first = map.keySet().iterator().next();map.remove(first);printKeys(map);map.clear();printKeys(map);}public static void chapter17_8() {System.out.println("HashMap:");test(new HashMap<>());System.out.println("LinkedHashMap:");test(new LinkedHashMap<>());System.out.println("TreeMap:");test(new TreeMap<>());}
}public static void main(String[] args) {// 循环遍历Map的4中方法Map<Integer, Integer> map = new HashMap<Integer, Integer>();map.put(1, 2);// 1. entrySet遍历,在键和值都需要时使用(最常用)for (Map.Entry<Integer, Integer> entry : map.entrySet()) {System.out.println("key = " + entry.getKey() + ", value = " + entry.getValue());}// 2. 通过keySet或values来实现遍历,性能略低于第一种方式// 遍历map中的键for (Integer key : map.keySet()) {System.out.println("key = " + key);}// 遍历map中的值for (Integer value : map.values()) {System.out.println("key = " + value);}// 3. 使用Iterator遍历Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();while (it.hasNext()) {Map.Entry<Integer, Integer> entry = it.next();System.out.println("key = " + entry.getKey() + ", value = " + entry.getValue());}// 4. java8 Lambda// java8提供了Lambda表达式支持,语法看起来更简洁,可以同时拿到key和value,// 不过,经测试,性能低于entrySet,所以更推荐用entrySet的方式map.forEach((key, value) -> {System.out.println(key + ":" + value);});}
17.9 散列与散列码

注意:如果使用自己的类作为HashMap的键,必须同时重载hashCode()和equals()。否认使用散列的数据结构(HashSet,HashMap,LinkedHashSet或LInkedHashMap)无法正确处理你的键。
使用散列的目的:想要使用一个对象来查找另一个对象。
散列的价值在于速度,使得查询得以快速进行。
我们希望在Map中保存数量不确定的值,如果键的数量别数组的容量限制了,怎么办?
答案是:数组不保存键本身,而是通过键对象生成一个数字,将其作为数组的下标。这个数字就是散列码,由定义在Object中,可能由你的类覆盖的hashCode()方法生成。为解决数组容量固定的问题,不同的键可以产生相同的下标。也就是说,可能会有冲突。如果能够保证没有冲突,那就是一个完美的散列函数。通常,冲突由外部链接处理,数组保存值的list,然后对list里的值使用equals()方法进行线性查询。
如果散列函数好的话,数组的每个位置只有较少的值。因此,不是查询整个list,而是快速跳到数组的某个位置,只对很少的元素进行比较。这便是HashMap快的原因。
设计hashCode:无论何时,对同一个对象调用hashCode()都应该生成同样的值。
Joshua Bloch recipe:
(1)result = 17;
(2)计算int散列码c;
(3)合并计算散列码:
result = 37*result + c;
(4)返回result;
(5)检查hashCode(),确保相同的对象有相同的散列码;
CountedString

class CountedString {private static List<String> created = new ArrayList<>();private String s;private int id = 0;public CountedString(String str) {s = str;created.add(s);for(String t:created) {if(t.equals(s)) {id++;}}}public String toString() {return "String:" + s + " id:" + id + " hashCode():" + hashCode();}public int hashCode() {int result = 17;result = 37*result + s.hashCode();result = 37*result + id;return result;}public boolean equals(Object o) {return o instanceof CountedString && s.equals(((CountedString)o).s) && id == ((CountedString)o).id;}public static void chapter17_9() {Map<CountedString,Integer> map = new HashMap<>();CountedString[] cs = new CountedString[5];for(int i=0;i<cs.length;i++) {cs[i] = new CountedString("ha");map.put(cs[i],i);}System.out.println(map);for(CountedString c:cs) {System.out.println("key:" + c + " value:" + map.get(c));}}
}
17.11 实用方法

Utilities

class Utilities {public static void chapter17_11() {List<String> list = Arrays.asList("one Two three Four five".split(" "));System.out.println("list:" + list);System.out.println("disjoint:" + Collections.disjoint(list,Collections.singletonList("Four")));System.out.println("max:" + Collections.max(list));System.out.println("max_insensitive:" + Collections.max(list,String.CASE_INSENSITIVE_ORDER));System.out.println("min:" + Collections.min(list));System.out.println("min_insensitive:" + Collections.min(list,String.CASE_INSENSITIVE_ORDER));List<String> sublist = Arrays.asList("Four five".split(" "));System.out.println("indexOfSubList:" + Collections.indexOfSubList(list,sublist));Collections.reverse(list);System.out.println("reverse:" + list);Collections.rotate(list,2);System.out.println("rotate:" + list);List<String> src = Arrays.asList("xxx yyy zzz".split(" "));Collections.copy(list,src);System.out.println("copy:" + list);Collections.swap(list,0,list.size()-1);System.out.println("swap:" + list);Collections.shuffle(list,new Random(7));System.out.println("shuffle:" + list);}
}

java.util.Collections类内部的静态方法

disjoint:当两个集合没有任何相同元素时,返回true

shuffle:随机改变列表顺序

FailFast

class FailFast {public static void chapter17_11() {Collection<String> c = new ArrayList<>();Iterator<String> it = c.iterator();c.add("ha");try {it.next();}catch (Exception e){e.printStackTrace();System.out.println("e:" + e.getMessage() + " " + e.toString());}}
}

容器保护机制,快速报错。在迭代遍历容器时,容器大小被(其他线程或自己)改变,会触发快速报错。此例中,应该在添加完所有的元素后,再获取迭代器。

17.12 持有引用

java.lang.ref类库包含了一组类,这些类为垃圾回收提供了更大的灵活性。三个继承自抽象类Reference的类:SoftReference,WeakRefenrence,PhantomReference。

对象是可获得的,是指对象在程序中的某处能找到。意味着在栈中的一个引用,或有更多的中间链接。如果一个对象是"可获得的",垃圾回收器就不能回收它。如果想继续持有对某个对象的引用,又希望垃圾回收器能自动回收它,则考虑使用Reference对象。

18 Java I/O系统

18.1 File类

DirList

class DirFilter implements FilenameFilter {private Pattern pattern;public DirFilter(String regex) {pattern = Pattern.compile(regex);}@Overridepublic boolean accept(File dir, String name) {return pattern.matcher(name).matches();}
}
class DirList {public static void chapter18_1() {File path = new File("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh");System.out.println("path:" + path.toString());String[] list = path.list(new DirFilter(".*\\.java"));System.out.println("list.length:" + list.length);Arrays.sort(list);for(String s:list) {System.out.println("s:" + s);}}
}

目录列表器

".*\\.java"

.*: .表示任意字符,*表示0个或多个

\\.:转义字符.

18.6 I/O流的典型使用方式
18.6.1 缓冲输入文件

BufferedInputFile

class BufferedInputFile {static String read(String filename) throws IOException {BufferedReader in = new BufferedReader(new FileReader(filename));String s;StringBuilder sb = new StringBuilder();while((s=in.readLine())!=null) {sb.append(s + "\n");}return sb.toString();}public static void chapter18_6() {try {System.out.println(read("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\Main.java"));} catch (IOException e) {e.printStackTrace();}}
}
18.6.2 从内存输入

MemoryInput

class MemoryInput {public static void chapter18_6() throws IOException {StringReader in = new StringReader("xxxyyyzzz");int c;while((c=in.read())!=-1) {System.out.print((char)c);}System.out.println();}
}

read()是以int形式返回下一字节,因此必须类型转换为char才能正确打印。

18.6.4 基本的文件输出

BasicFileOutput

class BasicFileOutput {public static void chapter18_6() throws IOException {BufferedReader in = new BufferedReader(new StringReader("xxx xxx yyy yyy zzz zzz"));String file = "D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\BasicFile";PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));int line = 1;String s;while( (s=in.readLine())!=null ) {out.println( (line++) + ":" + s );}out.close();System.out.println(BufferedInputFile.read(file));}
}
18.6.5 存储和恢复数据

StoringAndRecoveringData

class StoringAndRecoveringData {public static void chapter18_6() throws IOException {String file = "D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\Data.txt";DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));out.writeDouble(3.14);out.writeUTF("this is pi");out.writeDouble(1.414);out.writeUTF("square root of 2");out.close();DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));System.out.println(in.readDouble());System.out.println(in.readUTF());System.out.println(in.readDouble());System.out.println(in.readUTF());}
}
18.7 文件读写的实用工具

TextFile

class TextFile {// Read a file as a single stringstatic String read(String filename) {StringBuilder sb = new StringBuilder();try {BufferedReader in = new BufferedReader(new FileReader(new File(filename).getAbsoluteFile()));// try...finallytry {String s;while( (s=in.readLine())!=null ) {sb.append(s);sb.append("\n");}}finally {in.close();}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return sb.toString();}// Write a single filestatic void write(String filename, String text) {try {PrintWriter out = new PrintWriter(new File(filename).getAbsoluteFile());try {out.print(text);}finally {out.close();}} catch (FileNotFoundException e) {e.printStackTrace();}}public static void chapter18_7() {String str = read("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\BasicFile");System.out.println("TextFile:" + str);write("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\TextFile", str);}
}

Static functions for reading and writing text files as a single string

18.8 标准I/O

System.out:PrintStream对象

System.err:PrintStream对象

System.in:InputStream对象

setIn(InputStream)

setOut(PrintStream)

setErr(PrintStream)

Redirecting

class Redirecting {public static void chapter18_8() throws IOException {// defaultBufferedReader br = new BufferedReader(new InputStreamReader(System.in));String s;while( (s=br.readLine())!=null ) {System.out.println(s);if(s.equals("quit")) {// "quit" to quitbreak;}}// redirectSystem.out.println("redirect:");PrintStream console = System.out;BufferedInputStream in = new BufferedInputStream(new FileInputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\TextFile"));PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\Redirecting")));System.setIn(in);System.setOut(out);BufferedReader br1 = new BufferedReader(new InputStreamReader(System.in));String s1;while ( (s1=br1.readLine())!=null ) {// redirect to out fileSystem.out.println(s1);}out.close();System.setOut(console);}
}

此例子将标准输入附接到文件上,并将标准输出和标准错误重定向到另一个文件。

I/O重定向操作的是字节流,不是字符流。所以使用的是InputStream和OutputStream,而不是Reader和Writer。

18.10 新I/O nio

提高速度,更接近于操作系统的I/O方式:通道和缓冲器。唯一直接与通道交互的缓冲器是ByteBuffer。

用以产生FileChannel:三个类FileInputStream FileOutputStream和用于既读又写的RandomAccessFile。

GetChannel

class GetChannel {public static void chapter18_10() throws IOException {FileChannel fc = new FileOutputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\GetChannel").getChannel();fc.write(ByteBuffer.wrap("Some text".getBytes(StandardCharsets.UTF_8)));fc.close();fc = new RandomAccessFile("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\GetChannel","rw").getChannel();// Move to the endfc.position(fc.size());fc.write(ByteBuffer.wrap("\nSome more".getBytes(StandardCharsets.UTF_8)));fc.close();fc = new FileInputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\GetChannel").getChannel();ByteBuffer buf = ByteBuffer.allocate(1024);fc.read(buf);// read(buf)后需要调用buf.flip,让buf做好让别人读取字节的准备buf.flip();while(buf.hasRemaining()) {System.out.print((char)buf.get());}System.out.println();}
}

FileChannnel通道,可以向它传送用于读写的ByteBuffer。可以使用wrap()方法将已存在的字节数组包装到ByteBuffer。

一旦调用read()来告知FileChannel向ByteBuffer存储字节,就必须调用缓冲器上的flip(),让它做好让别人读取字节的准备。

ChannelCopy

class ChannelCopy {public static void chapter18_10() throws IOException {FileChannel in = new FileInputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\GetChannel").getChannel();FileChannel out = new FileOutputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\ChannelCopy").getChannel();ByteBuffer buf = ByteBuffer.allocate(1024);while(in.read(buf) != -1) {buf.flip();out.write(buf);buf.clear();}}
}

read(buffer)之后需要flip(),让它做好让别人读取字节的准备;read(buffer)之前,需要clear()来为read()做好准备。

18.10.1 转换数据

ByteBuffer转换为字符串

BufferToText

class BufferToText {public static void chapter18_10() throws IOException {//(1)write[getBytes()] and readFileChannel fc = new FileOutputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\BufferToText").getChannel();fc.write(ByteBuffer.wrap("Some text".getBytes()));fc.close();fc = new FileInputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\BufferToText").getChannel();ByteBuffer buf = ByteBuffer.allocate(1024);fc.read(buf);buf.flip();//(i)System.out.println(buf.asCharBuffer());//(ii)String encoding = System.getProperty("file.encoding");System.out.println("Decoded using " + encoding + ":" + Charset.forName(encoding).decode(buf));//(2)write[getBytes("UTF-16BE")] and readfc = new FileOutputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\BufferToText").getChannel();fc.write(ByteBuffer.wrap("Some text2".getBytes("UTF-16BE")));fc.close();fc = new FileInputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\BufferToText").getChannel();buf.clear();fc.read(buf);buf.flip();System.out.println(buf.asCharBuffer());//(3)write[buf.asCharBuffer().put()] and readfc = new FileOutputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\BufferToText").getChannel();buf.asCharBuffer().put("Some text3");fc.write(buf);fc.close();fc = new FileInputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\BufferToText").getChannel();buf.clear();fc.read(buf);buf.flip();System.out.println(buf.asCharBuffer());}
}

(1)write[getBytes()],read[asCharBuffer()]不起作用,read[decode]起作用

(2)write[getBytes("UTF-16BE")]

(3)write[buf.asCharBuffer().put()]

18.10.2 获取基本类型

尽管ByteBuffer只能保存字节类型的数据,但是它具有可以从其所容纳的字节中产生出各种不同基本类型值的方法。

GetData

class GetData {public static void chapter18_10() {// allocate default value 0ByteBuffer bb = ByteBuffer.allocate(1024);//(1)char arraybb.rewind();    //bytebuffer回到0位置bb.asCharBuffer().put("xxxxxxx");System.out.println(bb.asCharBuffer());char c;while((c = bb.getChar()) != 0) {System.out.print(c);}System.out.println();//(2)shortbb.rewind();bb.asShortBuffer().put((short) 2423);System.out.println(bb.getShort());//(3)intbb.rewind();bb.asIntBuffer().put(12314);System.out.println(bb.getInt());//(4)longbb.rewind();bb.asLongBuffer().put(123123139);System.out.println(bb.getLong());//(5)doublebb.rewind();bb.asDoubleBuffer().put(77.777);System.out.println(bb.getDouble());}
}

只有short需要进行类型转换

clear,flip,rewind区别:

Buffer(ByteBuffer)以及flip,clear及rewind区别_Joson_cyz的博客-CSDN博客_bytebuffer rewind

18.10.3 视图缓冲器

可以让我们通过某个特定的基本数据类型的视窗查看其底层的ByteBuffer。

IntBufferDemo

class IntBufferDemo {public static void chapter18_10() {ByteBuffer bb = ByteBuffer.allocate(1024);IntBuffer ib = bb.asIntBuffer();ib.put(new int[]{11,42,47,99,143,811});System.out.println(ib.get(3));ib.put(3,777);ib.flip();while(ib.hasRemaining()) {int i = ib.get();System.out.println(i);}}
}

在channel_read和put后调用flip,是为了channel_write和get。

通过在同一个ByteBuffer上建立不同的视图缓冲器,将同一字节序列翻译成了short,int,float,long和double类型的数据。

ViewBuffers

class ViewBuffers {public static void chapter18_10() {ByteBuffer bb = ByteBuffer.wrap(new byte[]{0,0,0,0,0,0,0,97});bb.rewind();System.out.println("ByteBuffer:");while(bb.hasRemaining()) {System.out.print(bb.position() + "->" + bb.get() + " ");}System.out.println();bb.rewind();CharBuffer cb = bb.asCharBuffer();System.out.println("CharBuffer:");while(cb.hasRemaining()) {System.out.print(cb.position() + "->" + cb.get() + " ");}System.out.println();bb.rewind();ShortBuffer sb = bb.asShortBuffer();System.out.println("ShortBuffer:");while(sb.hasRemaining()) {System.out.print(sb.position() + "->" + sb.get() + " ");}System.out.println();bb.rewind();IntBuffer ib = bb.asIntBuffer();System.out.println("IntBuffer:");while(ib.hasRemaining()) {System.out.print(ib.position() + "->" + ib.get() + " ");}System.out.println();bb.rewind();FloatBuffer fb = bb.asFloatBuffer();System.out.println("FloatBuffer:");while(fb.hasRemaining()) {System.out.print(fb.position() + "->" + fb.get() + " ");}System.out.println();bb.rewind();LongBuffer lb = bb.asLongBuffer();System.out.println("LongBuffer:");while(lb.hasRemaining()) {System.out.print(lb.position() + "->" + lb.get() + " ");}System.out.println();bb.rewind();DoubleBuffer db = bb.asDoubleBuffer();System.out.println("DoubleBuffer:");while(db.hasRemaining()) {System.out.print(db.position() + "->" + db.get() + " ");}System.out.println();}
}

ByteBuffer默认大端字节序

new byte[]{0, 97}

高字节存储在内存低地址:大端字节序

0x0002(高地址) 0x0001(低地址)

byte[0](低字节) byte[1](高字节)

00000000         01100001

==>97

低字节存储在内存低地址:小端字节序

0x0002(高地址) 0x0001(低地址)

byte[1](高字节) byte[0](低字节)

01100001         00000000         

==>24832

18.10.5 缓冲器的细节

mark标记,position位置,limit界限,capacity容量

交换相邻字符以对Charbuffer中的字符进行编码和译码

UsingBuffers

class UsingBuffers {static void symmetricSramble(CharBuffer buf) {while(buf.hasRemaining()) {buf.mark();char c1 = buf.get();char c2 = buf.get();buf.reset();buf.put(c2).put(c1);}}public static void chapter18_10() {char[] data = "UsingBuffers".toCharArray();// char = 2 * byteByteBuffer bb = ByteBuffer.allocate(data.length*2);CharBuffer cb = bb.asCharBuffer();cb.put(data);cb.rewind();System.out.println(cb);// encodesymmetricSramble(cb);cb.rewind();System.out.println(cb);// decodesymmetricSramble(cb);cb.rewind();System.out.println(cb);}
}

尽管可以对char数组调用wrap()方法生成一个CharBuffer,但本例是分配一个底层的ByteBuffer,产生的CharBuffer只是ByteBuffer的一个视图。

mark()将mark设置为position

get()将postion位置+1

put()将postion位置+1

reset()将position的值设为mark的值

rewind()将position设置为缓冲器的开始位置

18.10.7 文件加锁

JDK1.4引入了文件加锁机制,允许我们同步访问某个作为共享资源的文件。

FileLocking

class FileLocking {public static void chapter18_10() throws IOException, InterruptedException {FileOutputStream fos = new FileOutputStream("D:\\Users\\xxx\\IdeaProjects\\test\\src\\thinkinjava\\hyh\\FileLocking.txt");FileLock fl = fos.getChannel().tryLock();if(fl != null) {System.out.println("Lock file!");TimeUnit.MILLISECONDS.sleep(2000);// false:独占锁System.out.println("fl.isShared():" + fl.isShared());fl.release();System.out.println("Lock release!");}fos.close();}
}

tryLock非阻塞,lock阻塞

fl.isShared():java并发包提供的加锁模式分为独占锁和共享锁,独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock就是以独占方式实现的互斥锁。共享锁,则允许多个线程同时获取锁,并发访问共享资源,如:ReadWriteLock。很显然,独占锁是一种悲观保守的加锁策略,它避免了读/读冲突,如果某个只读线程获取锁,则其他读线程都只能等待,这种情况下就限制了不必要的并发性,因为读操作并不会影响数据的一致性。共享锁则是一种乐观锁,它放宽了加锁策略,允许多个执行读操作的线程同时访问共享资源。

19 枚举类型

19.1 基本enum特性

EnumClass

enum Shrubbery {GROUND,CRAWLING,HANGING
}
class EnumClass {public static void chapter19_1() {for(Shrubbery shrubbery:Shrubbery.values()) {System.out.println("========>" + shrubbery);System.out.println(shrubbery.name());System.out.println(shrubbery.ordinal());System.out.println(shrubbery.compareTo(Shrubbery.CRAWLING));System.out.println(shrubbery.getDeclaringClass());}}
}
19.4 values()的神秘之处

编译器创建的enum类都继承自Enum类。如果研究Enum类会发现,它并没有values()方法。可用反射机制看下其中究竟:

Reflection

enum Explore {HERE,THERE
}
class Reflection {public static Set<String> analyze(Class<?> enumClass) {System.out.println("--------- Analyzing " + enumClass + "----------");System.out.println("Interfaces:");for(Type t:enumClass.getGenericInterfaces()) {System.out.println(t);}System.out.println("Base:" + enumClass.getSuperclass());System.out.println("Methods:");Set<String> methods = new TreeSet<>();for(Method m:enumClass.getMethods()) {methods.add(m.getName());}System.out.println(methods);return methods;}public static void chapter19_4() {Set<String> exploreMethods = analyze(Explore.class);Set<String> enumMethods = analyze(Enum.class);exploreMethods.removeAll(enumMethods);System.out.println("exploreMethods:" + exploreMethods);}
}

结论是values()是由编译器添加的static方法。实际上,在创建Explore的过程中,编译器还为其添加了valueOf()方法,Enum类的valueOf()方法需要两个参数,新增的方法只需一个参数。

19.10 ConstantSpecificMethod

Java允许为enum实例编写方法,你需要为enum定义一个或多个abstract方法,然后为每个enum实例实现该抽象方法。

ConstantSpecificMethod

enum ConstantSpecificMethod {DATETIME {@OverrideString getInfo() {return DateFormat.getDateInstance().format(new Date());}},CLASSPATH {@OverrideString getInfo() {return System.getenv("CLASSPATH");}},VERSION {@OverrideString getInfo() {return System.getProperty("java.version");}};abstract String getInfo();public static void chapter19_10() {for(ConstantSpecificMethod constantSpecificMethod:ConstantSpecificMethod.values()) {System.out.println(constantSpecificMethod.name() + " " + constantSpecificMethod.getInfo());}}
}

21 并发

21.2 基本的线程机制
21.2.1 定义任务

线程可以驱动任务,任务由Runnable接口提供。定义任务,只需实现Runnable接口并实现run()方法。

MainThread

class LiftOff implements Runnable {protected int countDown = 10;private static int taskCount = 0;private final int id = taskCount++;public LiftOff() {}public LiftOff(int countDown) {this.countDown = countDown;}public String status() {return "#" + id + "(" + countDown + ")" + " ";}@Overridepublic void run() {System.out.println("Thread:" + Thread.currentThread());while (countDown-- > 0) {System.out.println(status());}}
}
class MainThread {public static void chapter21_2() {System.out.println("MainThread:" + Thread.currentThread());LiftOff lauch = new LiftOff();lauch.run();}
}

id用来区分任务的多个实例,它是final的,一旦被初始化之后就不能被修改。

21.2.2 Thread类

Thread构造器需要一个Runnable对象。调用Thread对象的start()方法,为该线程执行初始化操作,然后自动调用Runnable的run()方法。

BasicThreads

class BasicThreads {public static void chapter21_2() {System.out.println("BasicThreads:" + Thread.currentThread());Thread t = new Thread(new LiftOff());t.start();        }
}
21.2.3 使用Executor

java.util.concurrent包的Executor管理Thread对象,简化了并发编程。 

CachedThreadPool

class CachedThreadPool {public static void chapter21_2() {System.out.println("CachedThreadPool:" + Thread.currentThread());ExecutorService executorService = Executors.newCachedThreadPool();for(int i=0;i<3;i++) {executorService.execute(new LiftOff());}executorService.shutdown();}
}

CachedThreadPool将为每个任务都创建一个线程。对shutdown()的调用可防止新任务被提交给这个Executor。

FixedThreadPool使用有限的线程集来执行任务。

SingleThreadExecutor是线程数为1的FixedThreadPool。

21.2.4 从任务中产生返回值

Runnable是执行工作的独立任务,它不返回任何值。如果你希望任务在完成后能够返回值,可以实现Callable接口。

Callable是一个具有类型参数的泛型,它的类型参数表示从call()中的返回值。即Callable返回值。

CallableDemo

class TaskWithResult implements Callable<String> {private int id;public TaskWithResult(int id) {this.id = id;}@Overridepublic String call() throws Exception {System.out.println("TaskWithResult:" + Thread.currentThread());return "result id: " + id;}
}
class CallableDemo {public static void chapter21_2() {System.out.println("CallableDemo:" + Thread.currentThread());ExecutorService executorService = Executors.newCachedThreadPool();ArrayList<Future<String>> results = new ArrayList<>();for(int i = 0; i < 3; i++) {results.add(executorService.submit(new TaskWithResult(i)));}for(Future<String> fs:results) {try {System.out.println(fs.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}}
}

submit()方法会产生Future对象。可以用isDone()判断Future是否已经完成。任务完成时,isDone返回,可调用get()获取该结果;也可以不用isDone判断就直接调用get(),get()将阻塞直到结果准备就绪。

21.2.9 编码的变体

目前为止,任务类都实现了Runnable。你可能会希望使用直接从Thread继承的方式:

SimpleThread

class SimpleThread extends Thread {private int countDown = 5;private static int threadCount = 0;public SimpleThread() {super(Integer.toString(++threadCount));     //store the thread namestart();}public String toString() {return "#" + getName() + "(" + countDown + ")";}public void run() {System.out.println("SimpleThread:" + Thread.currentThread());while(true) {System.out.println(this);if(--countDown==0) {break;}}}public static void chapter21_2() {for(int i = 0; i < 3; i++) {new SimpleThread();}}
}

问题是构造器里启动了线程,意味这另一个线程会在构造器结束之前开始执行,存在风险。

21.2.11 加入一个线程

某线程在另一个线程t上调用t.join(),此线程会阻塞,直到目标线程结束。也可以在调用join()时带上一个超时参数。

线程可以被中断,做法是在调用线程上调用interupt方法。

Joining

class Sleeper extends Thread {private int duration;public Sleeper(String name, int sleepTime) {super(name);duration = sleepTime;start();}public void run() {try {sleep(duration);} catch (InterruptedException e) {e.printStackTrace();System.out.println(getName() + " was interrupted.");System.out.println("isInterrupted(): " + isInterrupted());return;}System.out.println(getName() + " has awakened");}
}
class Joiner extends Thread {private Sleeper sleeper;public Joiner(String name, Sleeper sleeper) {super(name);this.sleeper = sleeper;start();}public void run() {try {sleeper.join();} catch (InterruptedException e) {e.printStackTrace();System.out.println("isInterrupted(): " + isInterrupted());}System.out.println(getName() + " join completed");}
}
class Joining {public static void chapter21_2() {Sleeper sleepy = new Sleeper("Sleepy",1500);Sleeper grumpy = new Sleeper("Grumpy",1500);Joiner sleepy_join = new Joiner("Sleepy_join",sleepy);Joiner grumpy_join = new Joiner("Grumpy_join",grumpy);grumpy.interrupt();}
}

Joiner线程将通过Sleeper对象上调用join()方法来等待Sleeper结束。

当另一个线程在该线程上调用interrupt()时,会给该线程设定一个标志,表明该线程已经被中断。然而,异常被捕获时将清理这个标志,所以在catch中异常被捕获时这个标志总是假的。

21.2.14 捕获异常

一旦异常逃出线程任务的run()方法,它就会向外传播。

NaiveExceptionHandling

class ExceptionThread implements Runnable {@Overridepublic void run() {throw new RuntimeException();}
}
class NativeExceptionHandling {public static void chapter21_2() {try {ExecutorService executorService = Executors.newCachedThreadPool();executorService.execute(new ExceptionThread());} catch(RuntimeException ue) {System.out.println("Exception has been handled!");}}
}

try-catch不会捕获该异常,因为是另一个线程抛出的异常。

Thread.UncaughtExceptionHandler允许在每个Thread对象上附着一个异常处理器。Thread.UncaughtExceptionHandler.uncaughtException()会在线程因未捕获的异常而临近死亡时被调用。Executors.newCachedThreadPool()可以接收ThreadFactory参数。

CaptureUncaughtException

class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {@Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println("!!! caught " + e);}
}
class HandlerThreadFactory implements ThreadFactory {@Overridepublic Thread newThread(Runnable r) {System.out.println(this + " creating new Thread");Thread t = new Thread(r);System.out.println("created " + t);t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());System.out.println("eh = " + t.getUncaughtExceptionHandler());return t;}
}
class CaptureUncaughtException {public static void chapter21_2() {ExecutorService executorService = Executors.newCachedThreadPool(new HandlerThreadFactory());executorService.execute(new ExceptionThread());}
}

若代码中使用相同的异常处理器,将异常处理器设置为默认的即可

SettingDefaultHandler

class SettingDefaultHandler {public static void chapter21_2() {Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());ExecutorService executorService = Executors.newCachedThreadPool();executorService.execute(new ExceptionThread());}
}
21.3 共享受限资源(互斥)
21.3.2 解决共享资源竞争

即synchronized方法。

所有对象默认含有单一的锁。当在对象上调用synchronized方法时,对象被加锁。对象的其他synchronized方法只有等到之前方法调用完毕才能被调用。

一个任务线程可以多次获得对象的锁(即锁可重入,以引用计数的方式)。如果一个方法在同一个对象上调用了第二个方法,后者又调用了同一个对象的另一个方法,就会发生这种情况。只有先获得了锁的线程任务才能允许继续获取多个锁。

SynchronizedEvenGenerator

abstract class IntGenerator {public abstract int next();
}
class SynchronizedEvenGenerator extends IntGenerator {private int currentValue = 0;@Overridepublic synchronized int next() {++currentValue;++currentValue;return currentValue;}
}

同步偶数生成器

使用显式的Lock对象

MutexEvenGenerator

class MutexEvenGenerator extends IntGenerator {private int currentValue = 0;private Lock lock;@Overridepublic int next() {lock.lock();try {++currentValue;++currentValue;return currentValue;} finally {lock.unlock();}}
}

执行顺序:try finally return

21.3.3 原子性与可视性

原子性可应用于除了long和double之外的基本类型的"简单操作"。如简单的赋值和返回操作。(对应写操作和读操作)
(1)定义long或double变量时,如果使用volatile关键字,会获得原子性。
(2)若将域声明为volatile的,volatile关键字确保了可视性。只要对这个域产生了写操作,所有的读操作可以看到这个修改。即使使用了本地缓存,volatile域会立即被写入到主存中,而读操作就发生在主存中。
提醒,第一选择应该是使用synchronized关键字。
AtomicityTest

class AtomicityTest implements Runnable {private int i = 0;public int flag = 0;public int getValue() {//public synchronized int getValue() {return i;}private synchronized void evenIncrease() {i++;i++;}@Overridepublic void run() {while(true) {evenIncrease();if (flag == 1) {break;}}System.out.println("AtomicityTest 2");}public static void chapter21_3() {ExecutorService executorService = Executors.newCachedThreadPool();AtomicityTest atomicityTest = new AtomicityTest();executorService.execute(atomicityTest);while (true) {int val = atomicityTest.getValue();if (val % 2 != 0) {System.out.println(val);atomicityTest.flag = 1;break;}}System.out.println("AtomicityTest 1");}
}

return i虽然是原子性操作,但是缺少同步使得其数值可在不稳定的中间状态时被读取。除此以外,i不是volatile的,还存在可视性问题。
修改:synchronized getValue()

21.3.5 临界区

同步控制块

synchronized(syncObject) {

    //xxx

}

21.3.6 在其他对象上同步

一般使用同步控制块,synchronized(this) { xxx };如果必须在另一个对象上同步:

SynchObject

class DualSynch {private Object syncObject = new Object();public synchronized void f() {for (int i=0;i<5;i++) {System.out.println("f()");try {sleep(100);} catch (InterruptedException e) {e.printStackTrace();}Thread.yield();}}public void g() {synchronized (syncObject) {for (int i=0;i<5;i++) {System.out.println("g()");try {sleep(100);} catch (InterruptedException e) {e.printStackTrace();}Thread.yield();}}}
}
class SynchObject {public static void chapter21_3() {DualSynch dualSynch = new DualSynch();new Thread() {public void run() {dualSynch.f();}}.start();dualSynch.g();}
}

f()在this同步,g()在syncObject上同步,二者是相互独立的。

21.4 终结任务(中断)
21.4.1 装饰性花园

OrnamentalGarden

class Entrance implements Runnable {private static volatile boolean canceled = false;public static void cancel() {canceled = true;}@Overridepublic void run() {while(!canceled) {try {System.out.println("Entrance " + this + "do something!");TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}
}
class OrnamentalGarden {public static void chapter21_4() {ExecutorService executorService = Executors.newCachedThreadPool();for(int i=0;i<2;i++) {executorService.execute(new Entrance());}try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}Entrance.cancel();executorService.shutdown();try {if(!executorService.awaitTermination(50,TimeUnit.MILLISECONDS)) {System.out.println("Some tasks were not terminated!");}} catch (InterruptedException e) {e.printStackTrace();}}
}

shutdown只是起到通知关闭线程的作用,具体的关闭线程方法是:

在shutdown方法调用后,接着调用awaitTermination方法。这时只需要等待awaitTermination方法里第一个参数指定的时间。

ExcecutorService.awaitTermination()等待每个任务结束,如果所有任务在超时时间达到之前全部结束,则返回true,否则返回false,表示不是所有的任务都已经结束。

21.4.2 在阻塞时终结

线程状态:

(1)新建(new):分配系统资源,初始化。

(2)就绪(Runnable):这种状态下,只要调度器把时间片分给线程,线程就可以运行。

(3)阻塞(Blocked)

(4)死亡(Dead)

进入阻塞状态的原因:

(1)sleep()进入休眠状态

(2)wait()使线程挂起,直到线程得到notify()或notifyAll()消息。或java.util.concurrent的signal()或signalAll()消息。

(3)等待某个输入输出(IO)

(4)等待互斥锁

21.4.3 中断

Thread类的interrupt()方法:终止被阻塞的任务,设置线程为中断状态。若线程为阻塞状态,调用interrupt()方法会抛出InterruptedException。当抛出该异常或调用Thread.interrupted()时,中断状态将被复位。
(1)同时关闭Excecutor的所有线程:
在Excecutor上调用shutdownNow(),将发送interrupt()调用给他启动的所有线程。
(2)单独中断Excecutor的单个线程:
通过调用submit()启动任务,可持有该任务的上下文Future<?>,在其上调用cancel(),可在该线程上调用interrupt()以停止这个线程。
Interrupting

class SleepBlocked implements Runnable {@Overridepublic void run() {try {TimeUnit.SECONDS.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}
}
class IOBlocked implements Runnable {private InputStream in;public IOBlocked(InputStream is) {in = is;}@Overridepublic void run() {try {System.out.println("in.read() >>> ");in.read();} catch (IOException e) {e.printStackTrace();}}
}
class SynchronizedBlocked implements Runnable {public SynchronizedBlocked() {new Thread(){public void run() {f();}}.start();}public synchronized void f() {while(true) {Thread.yield();}}@Overridepublic void run() {System.out.println("try to call f() >>>");f();System.out.println("called f()");}
}
class Interrupting {private static ExecutorService executorService = Executors.newCachedThreadPool();static void test(Runnable r) throws InterruptedException {Future<?> f = executorService.submit(r);TimeUnit.SECONDS.sleep(1);System.out.println("Interrupting " + r.getClass().getName());f.cancel(true);System.out.println("Interrupt sent to " + r.getClass().getName());}public static void chapter21_4() {try {test(new SleepBlocked());} catch (InterruptedException e) {e.printStackTrace();}try {test(new IOBlocked(System.in));} catch (InterruptedException e) {e.printStackTrace();}try {test(new SynchronizedBlocked());} catch (InterruptedException e) {e.printStackTrace();}}
}

这个程序证明,SleepBlocked是可中断的阻塞,而IOBlocked和SynchronizedBlocked是不可中断的阻塞。

18章的nio类提供了更人性化的I/O中断,被阻塞的nio通道会自动地响应中断

NIOInterruption

class NIOBlocked implements Runnable {private final SocketChannel sc;NIOBlocked(SocketChannel sc) {this.sc = sc;}@Overridepublic void run() {System.out.println(" read in >>> " + this);try {sc.read(ByteBuffer.allocate(1));} catch (ClosedByInterruptException e) {System.out.println("catch e:" + e);} catch (IOException e) {e.printStackTrace();}System.out.println(" read out >>> " + this);}
}
class NIOInterruption {public static void chapter21_4() {ExecutorService executorService = Executors.newCachedThreadPool();try {ServerSocket serverSocket = new ServerSocket(8080);InetSocketAddress isa = new InetSocketAddress("localhost", 8080);SocketChannel sc = SocketChannel.open(isa);Future<?> f = executorService.submit(new NIOBlocked(sc));try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Interrupting ");f.cancel(true);System.out.println("Interrupt sented ");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}sc.close();} catch (IOException e) {e.printStackTrace();}}
}

Java SE5并发类库中添加了一个特性,即在ReentrantLock上阻塞的任务具备可以被中断的能力

Interrupting2

class BlockedMutex {private Lock lock = new ReentrantLock();public BlockedMutex() {System.out.println("BlockedMutex() " + currentThread());lock.lock();}public void f() {System.out.println("f() " + currentThread());try {lock.lockInterruptibly();System.out.println("f() acquired lock!");} catch (InterruptedException e) {System.out.println("f() InterruptedException!");}}
}
class Blocked2 implements Runnable {BlockedMutex blockedMutex = new BlockedMutex();@Overridepublic void run() {System.out.println("Blocked2() in >>>");blockedMutex.f();System.out.println("Blocked2() out >>>");}
}
class Interrupting2 {public static void chapter21_4() {Thread thread = new Thread(new Blocked2());thread.start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Interrupting ");thread.interrupt();System.out.println("Interrupted ");}
}
21.4.4 检查中断

可调用interrupted()来检查中断状态,这不仅可以告诉你interrupt()是否被调用过,而且还可以清除中断状态。

21.5 线程之间的协作(同步)
21.5.1 wait()与notifyAll()

[1]wait()会将任务线程挂起,只有notify()或notifyall()发生时,任务才会被唤醒去检查变化。
[2]当任务的方法里调用wait()时,线程被挂起,对象上的锁被释放。因为wait()将释放锁,意味着另一个任务可以获得这个锁,因此该对象的其他synchronized方法可以被调用。
[3]wait()可以带超时参数。
[4]wait(),notify()及notifyAll()都是基类Object的方法,不是Thread类的。只能在同步控制方法或同步控制块里调用wait(),notify()及nofityAll()。
WaxOMatic 打蜡与抛光

class Car {private boolean waxOn = false;//打蜡public synchronized void waxed() {waxOn = true;       notifyAll();}//抛光public synchronized void buffed() {waxOn = false;      notifyAll();}//等待打蜡public synchronized void waitForWax() throws InterruptedException {while(waxOn == false) {wait();}}//等待抛光public synchronized void waitForBuff() throws InterruptedException {while(waxOn == true) {wait();}}
}
//打蜡,等待抛光
class WaxOn implements Runnable {private Car car;public WaxOn(Car c) {car = c;}@Overridepublic void run() {try {while (!Thread.interrupted()) {TimeUnit.MILLISECONDS.sleep(200);System.out.println("Wax On! 打蜡!");car.waxed();System.out.println("Wax On! 等待抛光!");car.waitForBuff();}} catch (InterruptedException e) {System.out.println("WaxOn InterruptedException!");}System.out.println("WaxOn done!");}
}
//等待打蜡,抛光
class WaxOff implements Runnable {private Car car;public WaxOff(Car c) {car = c;}@Overridepublic void run() {try {while (!Thread.interrupted()) {System.out.println("Wax Off! 等待打蜡!");car.waitForWax();TimeUnit.MILLISECONDS.sleep(200);System.out.println("Wax Off! 抛光!");car.buffed();}} catch (InterruptedException e) {System.out.println("WasOff InterruptedException!");}System.out.println("WaxOff done!");}
}
class WaxOMatic {public static void chapter21_5() {Car car = new Car();ExecutorService executorService = Executors.newCachedThreadPool();executorService.execute(new WaxOff(car));executorService.execute(new WaxOn(car));try {TimeUnit.MILLISECONDS.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}executorService.shutdownNow();}
}

调用ExecutorService的shutdownNow()时,会调用由他控制的所有线程的interrupt()。

21.5.3 生产者与消费者

Restaurant

class Meal {private final int orderNum;Meal(int orderNum) {this.orderNum = orderNum;}public String toString() {return "Meal:" + orderNum;}
}
class WaitPerson implements Runnable {private Restaurant restaurant;public WaitPerson(Restaurant r) {restaurant = r;}@Overridepublic void run() {try {while (!Thread.interrupted()) {synchronized (this) {while (restaurant.meal == null) {wait();     //wait for chef to produce a meal}}System.out.println("WaitPerson got " + restaurant.meal);synchronized (restaurant.chef) {restaurant.meal = null;restaurant.chef.notifyAll();        //notify chef to produce another meal}}} catch (InterruptedException e) {System.out.println("WaitPerson InterruptedException");}System.out.println("WaitPerson out <<<");}
}
class Chef implements Runnable {private Restaurant restaurant;private int count = 0;public Chef(Restaurant r) {restaurant = r;}@Overridepublic void run() {try {while (!Thread.interrupted()) {synchronized (this) {while (restaurant.meal != null) {wait();}}if (++count == 5) {System.out.println("Out of food! Closing!");restaurant.executorService.shutdownNow();}System.out.println("Chef produce meal: " + count);synchronized (restaurant.waitPerson) {restaurant.meal = new Meal(count);restaurant.waitPerson.notifyAll();}//注意加sleep,否则while循环退出,不产生中断TimeUnit.MILLISECONDS.sleep(500);}} catch (InterruptedException e) {System.out.println("Chef InterruptedException");}System.out.println("Chef out <<<");}
}
class Restaurant {Meal meal;ExecutorService executorService = Executors.newCachedThreadPool();WaitPerson waitPerson = new WaitPerson(this);Chef chef = new Chef(this);Restaurant() {executorService.execute(chef);executorService.execute(waitPerson);}public static void chapter21_5() {new Restaurant();}
}

run()中,WaitPerson进入wait()模式,停止其任务,直到被Chef的notifyAll()唤醒。

一旦Chef送上Meal并通知WaitPerson,这个Chef就将等待,直到WaitPerson收集到订单并通知Chef,之后Chef就可以烧下一份Meal了。

shutdownNow()发出中断interrupt()后,WaitPerson是在wait()时抛出InterruptedException异常,Chef是在sleep()时抛出InterruptedException异常。

使用显式的Lock和Condition对象

可通过在Condition上调用await()来挂起一个任务。可通过signal()来唤醒一个任务或通过signalAll()来唤醒所有在这个Condition的任务。

WaxOMatic2

class Car2 {private Lock lock = new ReentrantLock();private Condition condition = lock.newCondition();private boolean waxOn = false;//打蜡public void waxed() {lock.lock();try {waxOn = true;condition.signalAll();} finally {lock.unlock();}}//抛光public void buffed() {lock.lock();try {waxOn = false;condition.signalAll();} finally {lock.unlock();}}//等待打蜡public void waitForWax() throws InterruptedException {lock.lock();try {while (waxOn == false) {condition.await();}} finally {lock.unlock();}}//等待抛光public void waitForBuff() throws InterruptedException {lock.lock();try {while(waxOn == true) {condition.await();}} finally {lock.unlock();}}
}
//打蜡,等待抛光
class WaxOn2 implements Runnable {private Car2 car;public WaxOn2(Car2 c) {car = c;}@Overridepublic void run() {try {while (!Thread.interrupted()) {TimeUnit.MILLISECONDS.sleep(200);System.out.println("Wax On! 打蜡!");car.waxed();System.out.println("Wax On! 等待抛光!");car.waitForBuff();}} catch (InterruptedException e) {System.out.println("WaxOn InterruptedException!");}System.out.println("WaxOn done!");}
}
//抛光,等待打蜡
class WaxOff2 implements Runnable {private Car2 car;public WaxOff2(Car2 c) {car = c;}@Overridepublic void run() {try {while (!Thread.interrupted()) {System.out.println("Wax Off! 等待打蜡!");car.waitForWax();TimeUnit.MILLISECONDS.sleep(200);System.out.println("Wax Off! 抛光!");car.buffed();}} catch (InterruptedException e) {System.out.println("WasOff InterruptedException!");}System.out.println("WaxOff done!");}
}
class WaxOMatic2 {public static void chapter21_5() {Car2 car = new Car2();ExecutorService executorService = Executors.newCachedThreadPool();executorService.execute(new WaxOff2(car));executorService.execute(new WaxOn2(car));try {TimeUnit.MILLISECONDS.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}executorService.shutdownNow();}
}

每个lock()的调用必须紧跟一个try-finally子句,保证所有情况下都可以释放锁。

21.5.4 生产者与消费者队列

同步队列 java.util.concurent.BlockingQueue

无界队列:LinkedBlockingQueue

有界队列:ArrayBlockingQueue

TestBlockingQueues

class LiftOffRunner implements Runnable {private BlockingQueue<LiftOff> rockets;public LiftOffRunner(BlockingQueue<LiftOff> queue) {rockets = queue;}public void add(LiftOff liftOff) {try {rockets.put(liftOff);} catch (InterruptedException e) {e.printStackTrace();}}@Overridepublic void run() {try {while(!Thread.interrupted()) {LiftOff rocket = rockets.take();rocket.run();}} catch (InterruptedException e) {System.out.println("LiftOffRunner InterruptedException");}System.out.println("Exit LiftOffRunner");}
}
class TestBlockingQueues {static void test(String msg, BlockingQueue<LiftOff> queue) {LiftOffRunner liftOffRunner = new LiftOffRunner(queue);Thread thread = new Thread(liftOffRunner);thread.start();//3个LiftOff,countDown 5for(int i=0;i<3;i++) {liftOffRunner.add(new LiftOff(5));}try {TimeUnit.MILLISECONDS.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}thread.interrupt();}public static void chapter21_5() {System.out.println("test LinkedBlockingDeque =====================");test("LinkedBlockingDeque", new LinkedBlockingDeque<>());       //Unlimited sizeSystem.out.println("test ArrayBlockingQueue =====================");test("ArrayBlockingQueue", new ArrayBlockingQueue<>(2));       //Fixed sizeSystem.out.println("test SynchronousQueue =====================");test("SynchronousQueue", new SynchronousQueue<>());       //1 size}
}
21.5.5 任务间使用管道进行输入/输出

管道:阻塞队列

PipedWriter类:允许任务线程向管道写

PipedReader类:允许不同任务线程从同一个管道读取

PipedIO

class Sender implements Runnable {private PipedWriter out = new PipedWriter();public PipedWriter getPipedWriter() {return out;}@Overridepublic void run() {try {while(true) {for (char c= 'A'; c <= 'z';c++) {out.write(c);TimeUnit.MILLISECONDS.sleep(500);}}} catch (IOException e) {System.out.println(e + " Sender write exception");} catch (InterruptedException e) {System.out.println(e + " === Sender sleep interrupted");}}
}
class Receiver implements Runnable {private PipedReader in;public Receiver(Sender sender) throws IOException {in = new PipedReader(sender.getPipedWriter());}@Overridepublic void run() {try {while(true) {System.out.println("Read: " + (char)in.read() + " ");}} catch (IOException e) {System.out.println(e + " Receiver read exception");}}
}
class PipedIO {public static void chapter21_5() {Sender sender = new Sender();try {Receiver receiver = new Receiver(sender);ExecutorService executorService = Executors.newCachedThreadPool();executorService.execute(sender);executorService.execute(receiver);try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}executorService.shutdownNow();} catch (IOException e) {e.printStackTrace();}}
}

两个任务线程使用一个管道进行通信的例子。Sender把数据放进Writer,然后休眠一段时间。Receiver没有sleep()和wait()。当Receiver调用read()时,如果没有更多的数据,管道将自动阻塞。shutdownNow()被调用时,可看到PipedReader与普通I/O之间最重要的差异:PipedReader是可中断的。若将in.read()修改为System.in.read(),则shutdownNow()发出的interrupt()不能中断read()调用。

21.6 死锁

2个哲学家,2根筷子,围坐在桌子周围,每人之间放一根筷子。当哲学家要就餐时,必须同时得到左边和右边的筷子,否则等待。

DeadLockingDiningPhilosophers

class Chopstick {private boolean taken = false;public synchronized void take() throws InterruptedException {while(taken) {wait();}taken = true;}public synchronized void drop() {taken = false;notifyAll();}
}
class Philosopher implements Runnable {private Chopstick left;private Chopstick right;private int id = 0;private int ponderFactor = 0;         //ponder沉思public Philosopher(Chopstick left, Chopstick right, int ident, int ponder) {this.left = left;this.right = right;id = ident;ponderFactor = ponder;}private void pause() throws InterruptedException {if(ponderFactor == 0)return;TimeUnit.MILLISECONDS.sleep(ponderFactor*100);}@Overridepublic void run() {try {while(!Thread.interrupted()){System.out.println(this + " " + "thinking!");//pause();System.out.println(this + " " + "grabbing right!");right.take();System.out.println(this + " " + "grabbing left!");left.take();System.out.println(this + " " + "eating!");pause();right.drop();left.drop();}} catch (InterruptedException e) {System.out.println(this + " " + "exiting via interrupt!");}}public String toString() {return "Philosopher " + id;}
}
class DeadlockingDiningPhilosophers {public static void chapter21_6() throws InterruptedException {int ponder = 3;int size = 2;ExecutorService executorService = Executors.newCachedThreadPool();Chopstick[] chopsticks = new Chopstick[size];for (int i=0; i<size; i++) {chopsticks[i] = new Chopstick();}for (int i=0; i<size; i++) {executorService.execute(new Philosopher(chopsticks[i], chopsticks[(i+1)%size], i ,ponder));}TimeUnit.SECONDS.sleep(5);executorService.shutdownNow();}
}

哲学家就餐死锁,2个人若同时先拿起右边筷子,则死锁。

死锁条件:

(1)互斥。资源不能共享,一根筷子一次只能被一个哲学家使用。

(2)至少有一个任务它必须持有一个资源且正在等待获取一个当前被别的任务持有的资源。即要发生死锁,哲学家必须拿着一根筷子并且等待另一根筷子。

(3)资源不能被任务抢占。哲学家不能从其他哲学家里抢夺筷子。

(4)必须有循环等待。哲学家1等哲学家2的筷子,哲学家2又等哲学家1的筷子。

防止死锁最容易破坏第4个条件。有这个条件的原因是每个哲学家都试图用特定的顺序拿筷子:先右后左。

所以解决死锁方法是:最后一个哲学家先拿左边筷子,后拿右边筷子。

FixedDiningPhilosophers

class FixedDiningPhilosophers {public static void chapter21_6() throws InterruptedException {int ponder = 3;int size = 2;ExecutorService executorService = Executors.newCachedThreadPool();Chopstick[] chopsticks = new Chopstick[size];for (int i=0; i<size; i++) {chopsticks[i] = new Chopstick();}for (int i=0; i<size; i++) {if (i < size-1) {executorService.execute(new Philosopher(chopsticks[i], chopsticks[i + 1], i, ponder));} else {executorService.execute(new Philosopher(chopsticks[0], chopsticks[i], i, ponder));}}TimeUnit.SECONDS.sleep(5);executorService.shutdownNow();}
}
21.7 新类库中的构件
21.7.1 CountDownLatch 锁存器

可以向CountDownLatch设置一个初始计数值,任何在这个对象上调用wait()的方法都将阻塞,直到这个计数值为0。

CountDownLatch被设计为只触发一次,计数值不能被重置。如果需要重置计数值,可以使用CyclicBarrier。

调用countDown()会减少计数值,调用await()会被阻塞直到计数值为0。

CountDownLatchDemo

// Performs some portion of a task
class TaskPortion implements Runnable {private static int counter = 0;private final int id = counter++;private CountDownLatch latch = null;TaskPortion(CountDownLatch latch) {this.latch = latch;}@Overridepublic void run() {try {doWork();latch.countDown();} catch (InterruptedException e) {e.printStackTrace();}}public void doWork() throws InterruptedException {TimeUnit.MILLISECONDS.sleep(500);System.out.println(this + " completed");}public String toString() {return "" + id;}
}
// Waits on the CountDownLatch
class WaitingTask implements Runnable {private static int counter = 0;private final int id = counter++;private CountDownLatch latch = null;WaitingTask(CountDownLatch latch) {this.latch = latch;}@Overridepublic void run() {try {latch.await();System.out.println("Latch barrier passed for " + this);} catch (InterruptedException e) {e.printStackTrace();}}public String toString() {return "WaitingTask " + id;}
}
class CountDownLatchDemo {static final int SIZE = 10;public static void chapter21_7() {ExecutorService executorService = Executors.newCachedThreadPool();CountDownLatch latch = new CountDownLatch(SIZE);for (int i=0;i<10;i++) {executorService.execute(new WaitingTask(latch));}for (int i=0;i<SIZE;i++) {executorService.execute(new TaskPortion(latch));}System.out.println("Launched all tasks");executorService.shutdown();System.out.println("exit!!!");}
}

例子中CountDownLatch设置为10,10个WaitingTask任务阻塞在await(),10个TaskPosition任务执行后,CountDownLatch计数值才为0,WaitingTask任务才可以继续执行。

21.7.2 CyclicBarrier 循环栅栏

CountDownLatch是只触发一次的事件(计数为0),而CyclicBarrier可以多次重用。

21.7.3 DelayQueue 延迟同步队列

这是一个无界的同步队列,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。

这种队列是有序的,即队头对象的延迟到期的时间最长。

如果没有任何延迟到期,就不会有任何头元素,且poll()将返回null。

DelayQueueDemo

class DelayedTask implements Runnable, Delayed {private static int counter = 0;private final int id = counter++;private int delta = 0;        //msprivate long trigger = 0;     //nsprotected static List<DelayedTask> sequence = new ArrayList<>();public DelayedTask(int milliseconds) {delta = milliseconds;trigger = System.nanoTime() + NANOSECONDS.convert(delta, MILLISECONDS);sequence.add(this);}@Overridepublic long getDelay(TimeUnit unit) {return unit.convert(trigger - System.nanoTime(), NANOSECONDS);}@Overridepublic int compareTo(Delayed o) {DelayedTask that = (DelayedTask)o;if(trigger < that.trigger) return -1;if(trigger > that.trigger) return 1;return 0;}@Overridepublic void run() {System.out.println(this + " ");}public String toString() {return delta + " Task " + id;}
}
class EndSentinel extends DelayedTask {     //哨兵private ExecutorService exec;public EndSentinel(int delay, ExecutorService e) {super(delay);exec = e;}public void run() {System.out.println(this + " Calling shutdownNow");exec.shutdownNow();}
}
class DelayedTaskConsumer implements Runnable {private DelayQueue<DelayedTask> q;public DelayedTaskConsumer(DelayQueue<DelayedTask> q) {this.q = q;}@Overridepublic void run() {try {while (!Thread.interrupted()) {q.take().run();}} catch (InterruptedException e) {e.printStackTrace();}System.out.println("DelayedTaskConsumer Finished!");}
}
class DelayQueueDemo {public static void chapter21_7() {Random random = new Random(47);ExecutorService executorService = Executors.newCachedThreadPool();DelayQueue<DelayedTask> queue = new DelayQueue<>();for (int i=0;i<10;i++) {queue.put(new DelayedTask(random.nextInt(300)));}queue.add(new EndSentinel(500,executorService));executorService.execute(new DelayedTaskConsumer(queue));}
}

类似时间优先级队列,DelayedTaskConsumer将最紧急的任务从队列取出并运行。

21.7.4 PriorityBlockingQueue 优先级同步队列

这是个优先级队列,具有可阻塞的读取操作。

21.7.5 使用ScheduledExecutor的温室控制器

期望一个事件在一个预定的时间运行。

通过使用schedule()(运行一次任务)或者scheduaAtFixedRate()(每隔规则的时间重复执行任务),你可以将Runnable对象设置为在将来的某个时刻执行。

21.7.6 Semaphore 信号量

正常的锁(concurrent.locks或synchronized锁)在任何时刻都只允许一个任务访问一项资源,而计数信号量允许n个任务同时访问这个资源。

21.9 性能调优
21.9.1 比较Lock和synchronized

一般来说,使用Lock通常比使用synchronized要高效。

并不意味着不应该使用synchronized,原因2点:

1 synchronized互斥方法的方法体一般较小,性能差异不大。当然,当你对性能调优时,应该比较两种方法,做实际的耗时测试。

2 synchronized可读性较高。

21.9.2 免锁容器

Java1.2中,Collections类提供了各种static的同步方法,这种开销是基于synchronized加锁机制的。

Java SE5添加了新的容器,使用更灵巧的技术来消除加锁,提高线程安全。

免锁容器背后的通用策略是:对容器的修改可以与读取操作同时发生,只要读取者只能看到完成修改的结果即可。修改是在容器数据结构某个部分的单独的副本(有时是整个数据结构的副本)上执行的,且这个副本在修改过程中是不可视的。只有当修改完成时,被修改的结构才会自动地与主数据结构进行交换(一个原子操作),之后读取者就可以看到这个修改了。

CopyOnWriteArrayList

CopyOnWriteArraySet

ConcurrentHashMap

ConcurrentLinkedQueue

性能对比:

CopyOnWriteArrayList比Collections.synchronizedList优

ConcurrentHashMap比Collections.synchronizedMap优

21.10 Future 活动对象

活动对象时另一种不同的并发模型。

每个对象都维护这自己的工作器线程和消息队列,并且所有对这种对象的请求都将进入队列排队,任何时候都只能运行其中的一个。

有了活动对象,可以串行化消息而不再是函数方法。

ActiveObjectDemo

class ActiveObjectDemo {private ExecutorService executorService = Executors.newSingleThreadExecutor();private void pause(int num) {try {MILLISECONDS.sleep(num);} catch (InterruptedException e) {e.printStackTrace();}}public Future<Integer> calculateInt(final int x ,final int y) {return executorService.submit(new Callable<Integer>() {@Overridepublic Integer call() throws Exception {System.out.println("starting " + x + " + " + y);pause(200);return x + y;}});}public void shutdown() {executorService.shutdown();}public static void chapter21_10() {ActiveObjectDemo activeObjectDemo = new ActiveObjectDemo();List<Future<?>> results = new CopyOnWriteArrayList<>();for (int i=0;i<5;i++) {results.add(activeObjectDemo.calculateInt(i,i));}System.out.println("All asynch calls made!");while(results.size() > 0) {for (Future<?> f:results) {if(f.isDone()) {try {System.out.println("result: " + f.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}results.remove(f);}}}activeObjectDemo.shutdown();}
}

例子中,将方法调用排进队列。

newSingleThreadExecutor()产生的单线程(每个对象有自己的线程)维护着自己的无界阻塞队列,且从该队列里取走任务并执行它们直到完成。

在calculateInt()中需要做的时用submit()提交一个Callable对象,以响应方法的调用,这样就可以把方法调用转化为消息。

调用者可以使用Future来发现何时任务完成,并收集实际的返回值。(f.isDone())

相关文章:

  • 硬件工程师笔记——运算放大电路Multisim电路仿真实验汇总
  • ArkUI--抽奖
  • dart实现单client连接的grpc server (以ip地址作判断)
  • 大数据时代的利剑:Bright Data网页抓取与自动化工具共建高效数据采集新生态
  • 2025年大一ACM训练-尺取
  • 代码随想录算法训练营 Day61 图论ⅩⅠ Floyd A※ 最短路径算法
  • methods的实现原理
  • Chainlink:连接 Web2 与 Web3 的去中心化桥梁
  • iOS 使用CocoaPods 添加Alamofire 提示错误的问题
  • 【Docker 新手入门指南】第十四章:Docker常用命令
  • HTML5实现简洁的端午节节日网站源码
  • 电子电路:深入了解4013D触发器的机制和原理
  • 设计模式之简单工厂模式
  • OSG编译wasm尝试
  • LVS-NAT 负载均衡群集
  • PHP7内核剖析 学习笔记 第九章 PHP基础语法的实现
  • 51. N-Queens
  • 【达梦】达梦数据库使用TypeHandler读取数据库时,将字段中的数据读取为数组
  • 用 Python 模拟雪花飘落效果
  • 【从零开始学习QT】快捷键、帮助文档、Qt窗口坐标体系
  • 小小的日本电影在线观看/北京网站优化服务
  • 网站信息资料库建设/百度关键词优化专家
  • 百度一般多久收录网站/今日新闻 最新消息 大事
  • 有哪些网站可以做按摩广告/想要网站导航正式推广
  • 自己弄个网站要多少钱/电商运营seo
  • wordpress js插件/新乡搜索引擎优化