20145201 《Java程序设计》第六周学习总结

教材学习内容总结

本周学习了课本第十、十一章内容,即输入/输出、线程与并行API。

第十章 输入输出

10.1 InputStream与OutputStream

10.1.1

  • java将输入/输出抽象化为串流,数据有来源及目的地,衔接两者的是串流对象。

  • 从应用程序角度来看,如果要将数据从来源取出,可以使用输入串流,如果要将数据写入目的地,可以使用输出串流。在JAVA中,输入串流代表对象为java.io.InputStream 实例,输出串流代表对象为java.io.OutputStream 实例。

  • 通用的 dump() 方法:

    代码如下:

package cc.openhome;

import java.io.*;

public class IO {
public static void dump(InputStream src, OutputStream dest)
throws IOException {
try (InputStream input = src; OutputStream output = dest) {
byte[] data = new byte[1024];
int length;
while ((length = input.read(data)) != -1) {
output.write(data, 0, length);
}
}
}
}
  • 每次从InputStream读入的数据,都会先置入byte数组中。InputStream的 read() 方法,每次会尝试读入 byte 数组的数据,并返回实际读入的字节,只要不是 -1,就表示读取到数据。可以使用OutputStream 的 write() 方法,指定要写出的 byte 数组、初始索引与数据长度。

  • 将某个文档读入并另存为另一个文档:

    代码如下:

package cc.openhome;

import java.io.*;

public class Copy {
public static void main(String[] args) throws IOException {
IO.dump(
new FileInputStream(args[0]),
new FileOutputStream(args[1])
);
}
}
  • 从http服务器读取某个网页,并另存为文档:
package cc.openhome;

import java.io.*;
import java.net.URL; public class Download {
public static void main(String[] args) throws IOException {
URL url = new URL(args[0]);
InputStream src = url.openStream();
OutputStream dest = new FileOutputStream(args[1]);
IO.dump(src, dest);
}
}

10.1.2串流继承架构

  • System.in:文本模式下取得整行用户输入。可以使用System的setIn()方法指定InputStream实例,重新指定标准输入来源。System.out为PrintStream实例。使用 setOut() 方法指定 PrintStream 实例,将结果输出至指定的目的地

  • System.err为PrintStream实例,称为标准错误输出串流,用来立即显示错误信息。

  • 使用System.setErr()指定PrintStream,重新指定标准错误输出串流。

  • FileInputStream是InputStream的子类,可以指定文件名创建实例,一旦创建文档就开启,接着就可用来读取数据,主要操作 InputStream 的 read() 抽象方法,使之可以从文档中读取数据。

  • FileOutputStream可以指定文件名创建实例,一旦创建文档就开启,接着就可用来写出数据,主要操作 InputStream 的 write() 抽象方法,使之可以写出数据至文档。

  • ByteArrayInputStream是InputStream的子类,可以指定 byte 数组创建实例,一旦创建就可将 byte 数组当作数据源进行读取。主要操作了 InputStream 的 read() 抽象方法,使之可从 byte 数组中读取数据。

  • ByteArrayOutputStream是OutputStream的子类可以指定 byte 数组创建实例,一旦创建就可将 byte 数组当作目的地写出数据。主要操作了 OutputStream 的 write() 抽象方法,使之可写出数据至 byte 数组。

10.1.3串流处理装饰器

BufferedInputStream与BufferedOutputStream主要在内部提供缓冲区功能。

代码如下:

package cc.openhome;

import java.io.*;

public class BufferedIO {
public static void dump(InputStream src, OutputStream dest)
throws IOException {
try(InputStream input = new BufferedInputStream(src);
OutputStream output = new BufferedOutputStream(dest)) {
byte[] data = new byte[1024];
int length;
while ((length = input.read(data)) != -1) {
output.write(data, 0, length);
}
}
}
}

DataInputStream与DataOutputStrea用来装饰InputStream、OutputStream、DataInputStream、DataOutputStream 提供读取、写入 java 基本数据类型的方法,像是s读写int double boolean 等的方法。这些方法会自动在指定的类型与字节间转换。

代码如下:

package cc.openhome;

import java.io.*;

public class Member {
private String number; private String name;
private int age; public Member(String number, String name, int age) {
this.number = number;
this.name = name;
this.age = age;
} public String getNumber() {
return number;
} public void setNumber(String number) {
this.number = number;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public String toString() {
return String.format("(%s, %s, %d)", number, name, age);
} public void save() throws IOException {
try(DataOutputStream output =
new DataOutputStream(new FileOutputStream(number))) {
output.writeUTF(number);
output.writeUTF(name);
output.writeInt(age);
}
} public static Member load(String number) throws IOException {
Member member;
try(DataInputStream input =
new DataInputStream(new FileInputStream(number))) {
member = new Member(
input.readUTF(), input.readUTF(), input.readInt());
}
return member;
}
}
  • ObjectInputStream 提供 readObject() 方法将数据读入为对象,ObjectOutputStream 提供 writeObject() 方法将对象写至目的地。

    代码如下:
package cc.openhome;

import java.io.*;

public class Member2 implements Serializable {
private String number;
private String name;
private int age; public Member2(String number, String name, int age) {
this.number = number;
this.name = name;
this.age = age;
} public String getNumber() {
return number;
} public void setNumber(String number) {
this.number = number;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public String toString() {
return String.format("(%s, %s, %d)", number, name, age);
} public void save() throws IOException {
try(ObjectOutputStream output =
new ObjectOutputStream(new FileOutputStream(number))) {
output.writeObject(this);
}
} public static Member2 load(String number)
throws IOException, ClassNotFoundException {
Member2 member;
try(ObjectInputStream input =
new ObjectInputStream(new FileInputStream(number))) {
member = (Member2) input.readObject();
}
return member;
}
}

10.2 字符处理类

10.2.1 Reader与Writer 继承架构

针对字符数据的读取,Java SE 提供了 java.io.Reader 类,其抽象化了字符数据读入的来源。针对字符数据的写入,Java SE 提供了 java.io.Writer 类,其抽象化了数据写出的目的地。

  • 如果从来源读入字符数据、将字符数据写至目的地,可以使用 CharUtil.dump() 方法:代码如下
package cc.openhome;

import java.io.*;

public class CharUtil {
public static void dump(Reader src, Writer dest) throws IOException {
try(Reader input = src; Writer output = dest) {
char[] data = new char[1024];
int length;
while((length = input.read(data)) != -1) {
output.write(data, 0, length);
}
}
}
}

每次从Reader 读入的数据,都会先置入 char 数组中.Reader 的 read() 方法,每次会尝试读入 char 数组长度的数据,并返回实际读入的字符数,只要不是-1,就表示读取到字符。可以使用 write() 方法,指定要写出的 byte 数组、初始索引与数据长度。

  • 使用CharUtil.dump() 读入文档、转为字符串并显示在文本模式中:

    代码如下
package cc.openhome;

import java.io.*;

public class CharUtilDemo {
public static void main(String[] args) throws IOException {
FileReader reader = new FileReader(args[0]);
StringWriter writer = new StringWriter();
CharUtil.dump(reader, writer);
System.out.println(writer.toString());
}
}

10.2.2 字符处理装饰器

1.

可以使用InputStreamReader 与 OutputStreamWriter对串流数据打包。

BufferedReader 与 BufferedWriter,可对 Reader、Writer 提供缓冲区作用,在处理字符输入/输出时,对效率也会有所帮助。

PrintWriter:除了可对OutStream打包之外,还可对Writer 进行打包,提供 print()、println()、format() 方法。

第十一章 线程与并行API

11.1线程

11.1.1线程简介

•单线程程序就是启动的程序从 main() 程序进入点开始至结束只有一个流程。

多线程程序:程序拥有多个流程

•在 java 中,从 main() 开始的流程会由主线程执行。可以创建Thread实例来执行Runnable实例定义的run()方法:

代码如下:

package cc.openhome;

public class TortoiseHareRace2 {
public static void main(String[] args) {
Tortoise tortoise = new Tortoise(10);
Hare hare = new Hare(10);
Thread tortoiseThread = new Thread(tortoise);
Thread hareThread = new Thread(hare);
tortoiseThread.start();
hareThread.start();
}
}

运行结果:

11.1.2 Thread 与 Runnable

  • 如果想要创建Thread实例,就要启动额外CPU就是调用Thread实例的 start() 方法。

  • 额外线程执行流程的进入点,有两种方式:可以定义在 Runnable 的 run() 方法中。也可以继承 Thread 类,重新定义 run() 方法。

11.1.3线程生命周期

  1. Daemon 线程
  • 主线程会从 main() 方法开始执行,直到 main() 方法结束后停止 JVM。如果主线程中启动了额外线程,默认会等待被启动的所有线程都执行完 run() 方法才中止 JVM。如果一个 Thread 被标示为 Daemon 线程,在所有的非 Daemon 线程都结束时,JVM 自动就会终止。

  • 从 main() 方法开始的就是一个非 Daemin 线程,可以使用 setDaemon() 方法来设定一个线程是否为 Daemon 线程。

代码如下:

package cc.openhome;

public class DaemonDemo {

    public static void main(String[] args) {
Thread thread = new Thread(() -> {
while (true) {
System.out.println("Orz");
}
});
thread.setDaemon(true);
thread.start();
}
}

若没有使用setDaemon()设定为true,则程序会不断输出orz不终止,

使用 isDaemon() 方法可以判断线程是否为Daemon线程。

2.Thread 基本状态图

在调用 Thread 实例 start() 方法后,基本状态为可执行(Runnable)、被阻断(Blocked)、执行中(Running)

同一时间点上,一个 CPU 只能执行一个线程,只是 CPU 会不断切换线程,且切换动作很快,所以看起来像是同时执行.

  • 改进效能的方式:运用多线程,当某线程进入 Blocked 时,让另一线程排入 CPU 执行,避免 CPU 空闲下来

  • 一个进入 Blocked 状态的线程,可以由另一个线程调用,该线程的 interrupt() 方法,让它离开 Blocked 状态.

代码如下:

package cc.openhome;

public class InterruptedDemo {

    public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
Thread.sleep(99999);
} catch (InterruptedException ex) {
System.out.println("我醒了XD");
}
});
thread.start();
thread.interrupt(); // 主线程调用thread的interrupt()
}
}

3.安插线程

  • 如果A线程正在运行,流程中允许B线程加入,等到B线程执行完毕后再继续A线程流程,则可以使用 join() 方法完成这个需求。

代码如下:

package cc.openhome;

import static java.lang.System.out;

public class JoinDemo {

    public static void main(String[] args) throws InterruptedException {
out.println("Main thread 开始..."); Thread threadB = new Thread(() -> {
out.println("Thread B 开始...");
for (int i = 0; i < 5; i++) {
out.println("Thread B 执行...");
}
out.println("Thread B 将结束...");
}); threadB.start();
threadB.join(); // Thread B 加入 Main thread 流程 out.println("Main thread将结束...");
}
}

运行结果:

4 . 停止线程

  • 线程完成 run() 方法后,就会进入 Dead ,进入 Dead 的线程不可以再次调用 start() 方法,否则会抛出 IllegalThreadStateException.

  • 直接调用 Thread 的 stop() 方法,将不理会所设定的释放、取得锁定流程,线程会直接释放所有已锁定对象,这有可能使对象陷入无法预期状态.

  • 如果要停止线程,最好自行操作,让线程跑完应有的流程,而非调用 Thread 的 stop() 方法.

11.1.4 关于ThreadGroup

11.1.4 关于ThreadGroup

  • 每个线程都属于某个线程群组。若在main()主流程中生产一个线程,该线程会属于main线程群组。可以使用一下程序片段取得目前线程所属线程群组名:

    Thread.currentThread().getThreadGroup().getName();

  • 每个线程产生时,都会归入某个线程群组,这视线程在那个群组中产生。如果没有指定,则归入产生该子线程的线程群组。也可以自行指定线程群组,线程一旦归入某个群组,就无法再更换。

  • java.lang.ThreadGroup可以管理群组中的线程。可以使用以下方式产生群组,并在产生线程时指定所属群组:

ThreadGroup group1 = new ThreadGroup("group1");

ThreadGroup group2 = new ThreadGroup("group2");

Thread thread1 = new Thread(group1,"group1's member");

Thread thread2 = new Thread(group2,"group2's member");

•interrupt()方法可以中断群组中所有线程。

setMaxpriority()方法可以设定群组中所有线程最大优先权(本来就有更高优先权的线程不受影响)。

enumerate():一次取得群组中所有线程。例如:

Thread[] threads = new Thread[threadGroup1.activeCount()];

threadGroup1.enumerate(threads);

activeCount()方法取得群组的线程数量,enumerate() 方法要传入 Thread 数组,这会将线程对象设定至每个数组索引。

•uncaughtException()方法:群组中某个线程发生异常而未捕捉时,JVM 会调用此方法进行处理。如果 ThreadGroup 有父 ThreadGroup,就会调用父 ThreadGroup 的 uncaughtException() 方法,否则看看异常是否为 ThreadDeath 实例。若是则什么都不做,若不是则调用异常的 printStrackTrace()。如果必须定义 ThreadGroup 中的线程异常处理行为,可重新定义此方法。

例:

package cc.openhome;

public class ThreadGroupDemo {

    public static void main(String[] args) {
ThreadGroup group = new ThreadGroup("group") {
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
System.out.printf("%s: %s%n",
thread.getName(), throwable.getMessage());
}
}; Thread thread = new Thread(group, () -> {
throw new RuntimeException("测试异常");
}); thread.start();
}
}
  • 在JDK5 之后,如果 ThreadGroup 中的线程发生异常,uncaughtException() 方法处理顺序为:

    • 如果 ThreadGroup 有父 ThreadGroup,就会调用父 ThreadGroup 的 uncaughtException() 方法。

    • 否则,看看 Thread 是否使用 setUncaughtExceptionHandler() 方法设定 Thread.Uncaught-ExceptionHandler 实例,有的话就会调用其 uncaughtException() 方法。

    • 否则,看看异常是否为 ThreadDeath 实例,若“是”则什么都不做,若“否”则调用异常的 printfStractTrace()。

•对于线程本身未捕捉的异常,可以自行指定处理方式。

例:

package cc.openhome;

public class ThreadGroupDemo2 {

    public static void main(String[] args) {
ThreadGroup group = new ThreadGroup("group"); Thread thread1 = new Thread(group, () -> {
throw new RuntimeException("thread1 测试例外");
});
thread1.setUncaughtExceptionHandler((thread, throwable) -> {
System.out.printf("%s: %s%n",
thread.getName(), throwable.getMessage());
}); Thread thread2 = new Thread(group, () -> {
throw new RuntimeException("thread2 测试异常");
}); thread1.start();
thread2.start();
}
}

11.1.5 synchronized 与 volatile

  1. synchronized

    每个对象都会有个内部锁定,或称为监控锁定。被标示为 synchronized 的区块将会被监控,任何线程要执行 synchronized 区块都必须先取得指定的对象锁定。

    如果在方法上标示 synchronized,则执行方法必须取得该实例的锁定。

    线程若因尝试执行 synchronized 区块而进入 Blocked,在取得锁定之后,会先回到 Runnable 状态,等待 CPU 排版器排入 Running 状态。

    java的 synchronized 提供的是可重入同步,也就是线程取得某对象锁定后,若执行过程中又要执行 synchronized,尝试取得锁定的对象来源又是同一个,则可以直接执行

    由于线程无法取得锁定时会造成阻断,不正确地使用synchronized有可能造成能效低落,另一问题则是死结

    例如有些资源在多线程下彼此交叉取用,有可能造成死结

    代码如下:

package cc.openhome;

class Resource {
private String name;
private int resource; Resource(String name, int resource) {
this.name = name;
this.resource = resource;
} String getName() {
return name;
} synchronized int doSome() {
return ++resource;
} synchronized void cooperate(Resource resource) {
resource.doSome();
System.out.printf("%s 整合 %s 的资源%n",
this.name, resource.getName());
}
} public class DeadLockDemo {
public static void main(String[] args) {
Resource resource1 = new Resource("resource1", 10);
Resource resource2 = new Resource("resource2", 20); Thread thread1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
resource1.cooperate(resource2);
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
resource2.cooperate(resource1);
}
}); thread1.start();
thread2.start();
}
}

2.使用volatile

synchronized 要求达到的所标示区块的互斥性与可见性,互斥性是指 synchronized 区块同时间只能有一个线程,可见性是指线程离开 synchronized 区块后,另一线程接触到的就是上一线程改变后的对象状态。

•在java中对于可见性的要求,可以使用 volatile 达到变量范围,代码如下:

package cc.openhome;

class Variable1 {
static int i = 0, j = 0; static void one() {
i++;
j++;
} static void two() {
System.out.printf("i = %d, j = %d%n", i, j);
}
} public class Variable1Test {
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
while (true) {
Variable1.one();
}
});
Thread thread2 = new Thread(() -> {
while (true) {
Variable1.two();
}
}); thread1.start();
thread2.start();
}
}

可以在变量上声明 volatile,表示变量是不稳定的、易变的,也就是可能在多线程下存取,这保证变量的可见性,也就是若有线程变动了变量值,另一线程一定可以看到变更。被标示为 volatile 的变量,不允许线程快取,变量值的存取一定是在共享内存中进行.

代码如下:

package cc.openhome;

class Variable3 {
volatile static int i = 0, j = 0; static void one() {
i++;
j++;
} static void two() {
System.out.printf("i = %d, j = %d%n", i, j);
}
} public class Variable3Test {
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
while (true) {
Variable3.one();
}
});
Thread thread2 = new Thread(() -> {
while (true) {
Variable3.two();
}
});
thread1.start();
thread2.start();
}
}

由此可见,volatile 保证的是单一变数的可见性,线程对变量的存取一定是在共享内存中,不会在自己的内存空间中快取变量,线程对共享内存中变量的存取,另一线程一定看得到

11.1.6等待与通知

wait()、notify()、notifyAll() 是 Object 定义的方法,可以通过这三个方法控制线程释放对象的锁定,或者通知线程参与锁定竞争。

  • 执行 synchronized 范围的程序代码期间,若要调用锁定对象的 wait() 方法,线程会释放对象锁定,并进入对象等待集合而处于阻断状态,其他线程可以竞争对象锁定,取得锁定的线程可以执行 synchronized 范围的程序代码。

  • 被竞争锁定的对象调用 noyify() 时,会从对象等待集合中随机通知一个线程加入排班,再次执行 synchronized 前,被通知的线程会与其他线程共同竞争对象锁定。

  • 如果调用 notifyAll(),所有等待集合中的线程都会被通知参与排班,这些线程会与其他线程共同竞争对象锁定。

11.2并行API

11.2.1Lock、ReadWriteLock 与 Condition

1.使用Lock

•Lock 接口主要操作类之一为 ReentrantLock,可以达到synchronized 的作用,也提供额外的功能。

代码如下:

package cc.openhome;

import java.util.Arrays;
import java.util.concurrent.locks.*; public class ArrayList<E> {
private Lock lock = new ReentrantLock();
private Object[] elems;
private int next; public ArrayList(int capacity) {
elems = new Object[capacity];
} public ArrayList() {
this(16);
} public void add(E elem) {
lock.lock();
try {
if (next == elems.length) {
elems = Arrays.copyOf(elems, elems.length * 2);
}
elems[next++] = elem;
} finally {
lock.unlock();
}
} public E get(int index) {
lock.lock();
try {
return (E) elems[index];
} finally {
lock.unlock();
}
} public int size() {
lock.lock();
try {
return next;
} finally {
lock.unlock();
}
}
}

想要锁定 Lock 对象,可以调用其 lock 方法,只有取得 Lock 对象锁定的线程,才可以继续往后执行程序代码,要接触锁定,可以调用 Lock 对象的 unlock().

  • Lock 接口还定义了tryLock() 方法,如果线程调用 tryLock() 可以取得锁定会返回 true,若无法取得锁定并不会发生阻断,而是返回 false。

    代码如下:
package cc.openhome;

import java.util.concurrent.locks.*;

class Resource {
private ReentrantLock lock = new ReentrantLock();
private String name; Resource(String name) {
this.name = name;
} void cooperate(Resource res) {
while (true) {
try {
if (lockMeAnd(res)) {
System.out.printf("%s 整合 %s 的资源%n", this.name, res.name);
break;
}
} finally {
unLockMeAnd(res);
}
}
} private boolean lockMeAnd(Resource res) {
return this.lock.tryLock() && res.lock.tryLock();
} private void unLockMeAnd(Resource res) {
if (this.lock.isHeldByCurrentThread()) {
this.lock.unlock();
}
if (res.lock.isHeldByCurrentThread()) {
res.lock.unlock();
}
}
} public class NoDeadLockDemo { public static void main(String[] args) {
Resource res1 = new Resource("resource1");
Resource res2 = new Resource("resource2"); Thread thread1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
res1.cooperate(res2);
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
res2.cooperate(res1);
}
}); thread1.start();
thread2.start();
}
}

2.使用ReadWriteLock

ReadWriteLock 接口定义了读取锁定与写入锁定行为,可以使用 readLock()、writeLock() 方法返回 Lock 操作对象。

  • ReentrantReadWriteLock.ReadLock 操作了Lock 接口,调用其 lock() 方法时,若没有任何 ReentrantReadWriteLock.WriteLock 调用过 lock() 方法,也就是没有任何写入锁定时,就可以取得读取锁定。

  • ReentrantReadWriteLock.WriteLock 操作了 Lock 接口,调用其 lock() 方法时,若没有任何 ReentrantReadWriteLock.ReadLock 或 ReentrantReadWriteLock.WriteLock 调用过 lock() 方法,也就是没有任何读取或写入锁定时,才可以取得写入锁定。

3.使用StampedLock

•JDK8新新增了StampedLock类,可支持乐观读取操作。

代码如下:

package cc.openhome;

import java.util.Arrays;
import java.util.concurrent.locks.*; public class ArrayList3<E> {
private StampedLock lock = new StampedLock();
private Object[] elems;
private int next; public ArrayList3(int capacity) {
elems = new Object[capacity];
} public ArrayList3() {
this(16);
} public void add(E elem) {
long stamp = lock.writeLock();
try {
if (next == elems.length) {
elems = Arrays.copyOf(elems, elems.length * 2);
}
elems[next++] = elem;
} finally {
lock.unlockWrite(stamp);
}
} public E get(int index) {
long stamp = lock.tryOptimisticRead();
Object elem = elems[index];
if (!lock.validate(stamp)) {
stamp = lock.readLock();
try {
elem = elems[index];
} finally {
lock.unlockRead(stamp);
}
}
return (E) elem;
} public int size() {
long stamp = lock.tryOptimisticRead();
int size = next;
if (!lock.validate(stamp)) {
stamp = lock.readLock();
try {
size = next;
} finally {
lock.unlockRead(stamp);
}
}
return size;
}
}

4.使用Condition

Condition 接口用来搭配 Lock,最基本的用法就是达到 Object 的 wait()、notify()、notifyAll() 方法的作用

代码如下:

package cc.openhome;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class Clerk {
private int product = -1;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition(); public void setProduct(int product) throws InterruptedException {
lock.lock();
try {
waitIfFull();
this.product = product;
System.out.printf("生产者设定 (%d)%n", this.product);
condition.signal();
} finally {
lock.unlock();
}
} private void waitIfFull() throws InterruptedException {
while (this.product != -1) {
condition.await();
}
} public int getProduct() throws InterruptedException {
lock.lock();
try {
waitIfEmpty();
int p = this.product;
this.product = -1;
System.out.printf("消费者取走 (%d)%n", p);
condition.signal();
return p;
} finally {
lock.unlock();
}
} private void waitIfEmpty() throws InterruptedException {
while (this.product == -1) {
condition.await();
}
}
}
  • 要通知等待集合中的一个线程,则可以调用 signal() 方法。

    如果要通知所有等待集合中的线程,可以调用 signalAll()。

  • 一个Condition 对象可代表有一个等待集合,可以重复调用 Lock 的newCondition(),取得多个Condition 实例,这代表了可以有多个等待集合。

11.2.2使用Executor

•从JDK5 开始,定义了 java.util.concurrent.Executor 接口,目的是将 Runnable 的指定与实际如何执行分离。

Executor 接口只定义了一个 execute() 方法:

package java.util.concurrent;

public interface Executor{

void execute(Runnable command);

}

1.使用ThreadPoolExecutor

根据不同的线程池需求,ThreadPoolExecutor 拥有数种不同构造函数可供使用,不过通常会使用 java.util.concurrent.Executors 的 newCachedThreadPool()、newFixedThreadPool() 静态方法来创建 ThreadPoolExecutor 实例。

代码如下:

package cc.openhome;

import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class Download3 {
public static void main(String[] args) throws Exception {
URL[] urls = {
new URL("http://openhome.cc/Gossip/Encoding/"),
new URL("http://openhome.cc/Gossip/Scala/"),
new URL("http://openhome.cc/Gossip/JavaScript/"),
new URL("http://openhome.cc/Gossip/Python/")
}; String[] fileNames = {
"Encoding.html",
"Scala.html",
"JavaScript.html",
"Python.html"
}; ExecutorService executorService = Executors.newCachedThreadPool();
new Pages(urls, fileNames, executorService).download();
executorService.shutdown();
}
}

2.使用ScheduledThreadPoolExecutor

ScheduledExecutorService 的操作类 ScheduledThreadPoolExecutor 为 ThreadPoolExecutor 的子类,具有线程池与排程功能。

代码如下:

package cc.openhome;

import java.util.concurrent.*;

public class ScheduledExecutorServiceDemo {

    public static void main(String[] args) {
ScheduledExecutorService service
= Executors.newSingleThreadScheduledExecutor(); service.scheduleWithFixedDelay(
() -> {
System.out.println(new java.util.Date());
try {
Thread.sleep(2000); // 假设这个工作会进行两秒
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}, 2000, 1000, TimeUnit.MILLISECONDS);
}
}

3.使用ForkJoinPool

  • java.util.ForkJoinPool主要目的是在解决分而治之的问题

  • 在分而治之需要结合并行的情况下,可以使用 ForkJoinTask,其操作了 Future 接口,可以让你在未来取得耗时工作的执行结果

•ForkJoinPool 与其他的 ExecutorService 操作不同的地方在于,它实现了工作窃取演算,其建立的线程如果完成手边任务,会尝试寻找并执行其他任务建立的子任务,让线程保持忙碌状态,有效利用处理器的能力。

11.2.3 并行Collection简介

CopyOnWriteArrayList操作了List接口,这个类的实例在写入操作时,内部会建立新数组,并复制原有数组索引的参考。

CopyOnWriteArraySet 操作了 List 接口,这个类的实例在写入操作时,内部会建立新数组,并复制原有数组索引的参考,然后在新数组上进行写入操作,写入完成后,再将内部原参考旧数组的变量参考至新数组。

BllockingQueu 是 Queue 的子接口,新定义了 put() 与 take() 等方法,线程若调用 put() 方法,在队列已满的情况下会被阻断,线程若调用 take() 方法,在队列为空的情况下会被阻断。

代码如下:

package cc.openhome;

import java.util.concurrent.BlockingQueue;

public class Producer3 implements Runnable {
private BlockingQueue<Integer> productQueue; public Producer3(BlockingQueue<Integer> productQueue) {
this.productQueue = productQueue;
} public void run() {
System.out.println("生产者开始生产整数......");
for(int product = 1; product <= 10; product++) {
try {
productQueue.put(product);
System.out.printf("生产者提供整数 (%d)%n", product);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
}
}

代码调试中的问题和解决过程

现在调试代码基本可以一次通过,主要是要细心看书,联系前后文,细心一些就可以了。

本周代码托管截图





其他(感悟、思考等,可选)

学习进程已经过半,现在基本有了自己的一套学习模式。书本是本源,首先应该把书上的知识弄懂,这期间可能会出现问题,要及时求教老师和同学早日弄懂。这种全新的学习模式,提高了我的自学能力,相信也会在将来的学习工作中是一个很好的经验。

目前还不能自己编写出一些较为复杂的程序,还需要积累经验,不能只是机械的敲代码,要理解其深刻含义。

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 4500行 30篇 400小时
第一周 200/200 2/2 20/20
第二周 300/500 1/3 18/38
第三周 400/900 1/4 22/60
第四周 1000/1900 1/5 35/95
第五周 800/2700 1/6 30/125
第六周 700/3400 1/7 30/155

20145201 《Java程序设计》第六周学习总结的更多相关文章

  1. 20145201《Java程序设计》第九周学习总结

    20145201 <Java程序设计>第九周学习总结 教材学习内容总结 JDBC是用于执行SQL的解决方案,开发人员使用JDBC的标准接口,数据库厂商则对接口进行操作,开发人员无须接触底层 ...

  2. 20145201 《Java程序设计》第二周学习总结

    20145201 <Java程序设计>第二周学习总结 教材学习内容总结 本周学习了课本第三章内容,即JAVA基础语法. 3.1 类型.变量与运算符 基本类型:在java中基本类型主要可区分 ...

  3. 20145201 《Java程序设计》第一周学习总结(修改)

    # 20145201 <Java程序设计>第一周学习总结 ## 教材学习内容总结 万事开头难,终于开始学习了Java.寒假的时候看到老师的要求确实有点慌,但是这周翻开书,从书本知识第一行学 ...

  4. 20145201 《Java程序设计》第一周学习总结

    # 20145201 <Java程序设计>第一周学习总结 ## 教材学习内容总结 万事开头难,终于开始学习了Java.寒假的时候看到老师的要求确实有点慌,但是这周翻开书,从书本知识第一行学 ...

  5. 20145213《Java程序设计》第九周学习总结

    20145213<Java程序设计>第九周学习总结 教材学习总结 "五一"假期过得太快,就像龙卷风.没有一点点防备,就与Java博客撞个满怀.在这个普天同庆的节日里,根 ...

  6. 21045308刘昊阳 《Java程序设计》第九周学习总结

    21045308刘昊阳 <Java程序设计>第九周学习总结 教材学习内容总结 第16章 整合数据库 16.1 JDBC入门 16.1.1 JDBC简介 数据库本身是个独立运行的应用程序 撰 ...

  7. 《Java程序设计》第九周学习总结

    20145224 <Java程序设计>第九周学习总结 第十六章 整合数据库 JDBC入门 ·数据库本身是个独立运行的应用程序 ·撰写应用程序是利用通信协议对数据库进行指令交换,以进行数据的 ...

  8. 20145236 《Java程序设计》第九周学习总结

    20145236 <Java程序设计>第九周学习总结 教材学习内容总结 第十六章 整合数据库 JDBC简介 1.JDBC是java联机数据库的标准规范.它定义了一组标准类与接口,标准API ...

  9. 20155304田宜楠2006-2007-2 《Java程序设计》第一周学习总结

    20155304田宜楠2006-2007-2 <Java程序设计>第一周学习总结 教材学习内容总结 - 浏览教材,根据自己的理解每章提出一个问题 第一章 除了书上提到的开发工具还有什么适合 ...

  10. 20155303 2016-2017-2 《Java程序设计》第二周学习总结

    20155303 2016-2017-2 <Java程序设计>第二周学习总结 教材学习内容总结 『注意』 "//"为单行批注符: "/*"与&quo ...

随机推荐

  1. LNK2005 _DllMain@12 mfcs100d.lib

    起因是将之前使用 MFC 规则 DLL 的动态库都改为了 MFC 扩展 DLL,在将动态库中从 CWinApp 继承的类替换为 DllMain 函数后,就出现 LNK2005 错误,说 DllMain ...

  2. eclipse新建maven web工程

    每次建maven项目,总会有问题.决定在这整理一次,避免以后浪费时间. 最后目录为 1.首先修改pom.xml 之前老是出现明明改了 java compiler 已maven update 一下就又变 ...

  3. 【BZOJ2179】FFT快速傅立叶

    [BZOJ2179]FFT快速傅立叶 Description 给出两个n位10进制整数x和y,你需要计算x*y. Input 第一行一个正整数n. 第二行描述一个位数为n的正整数x. 第三行描述一个位 ...

  4. jpa单向一对一关系外键映射

    项目结构: Wife package auth.model; import javax.persistence.Column; import javax.persistence.Entity; imp ...

  5. Leetcode-Combinations Sum II

    Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in ...

  6. 动态获取selected的value值

    两个select选项,第一个选项可以选1-9之间的数字,第二个选项可以选0到9之间的数字,要满足成人和儿童的总和不大于9,且一个成人最多带两名儿童 <div> <span>成人 ...

  7. 原生js:js获得当前选中的内容的字体大小

    利用currentStyle()和ComputedStyle() function getstyle(obj, key) {    if (obj.currentStyle) {        ret ...

  8. ASP.Net请求处理机制初步探索之旅 - Part 3 管道(转)

    开篇:上一篇我们了解了一个ASP.Net页面请求的核心处理入口,它经历了三个重要的入口,分别是:ISAPIRuntime.ProcessRequest().HttpRuntime.ProcessReq ...

  9. Pycharm如何取消自动换行

    1.只对当前文件有效的操作是: 菜单栏->View -> Active Editor -> Use Soft Wraps (不选中) 2.要是想对所有文件都起到效果,就要在setti ...

  10. Spoken English Practice( let me just pull over(pull,give))

    绿色:连读:                  红色:略读:               蓝色:浊化:               橙色:弱读     下划线_为浊化 口语蜕变(2017/6/26) ...