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);}}}
}
  1. 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;}@Overridepublic 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;}@Overridepublic 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对串流数据打包。

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

  3. 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") {@Overridepublic 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.使用volatilesynchronized 要求达到的所标示区块的互斥性与可见性,互斥性是指 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 与 Condition1.使用Lock•Lock 接口主要操作类之一为 ReentrantLock,可以达到synchronized 的作用,也提供额外的功能。代码如下:

package cc.openhome;

import java.util.Arrays;
import java.util.concurrent.locks.*;

public class ArrayList {
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.使用ReadWriteLockReadWriteLock 接口定义了读取锁定与写入锁定行为,可以使用 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 {
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.使用ScheduledThreadPoolExecutorScheduledExecutorService 的操作类 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 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

转载于:https://www.cnblogs.com/20145201lzx/p/5373206.html

20145201 《Java程序设计》第六周学习总结相关推荐

  1. 20155227 2016-2017-2 《Java程序设计》第九周学习总结

    20155227 2016-2017-2 <Java程序设计>第九周学习总结 教材学习内容总结 JDBC简介 JDBC全名Java DataBase Connectivity,是java联 ...

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

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

  3. 20165203 2017-2018-2 《Java程序设计》第一周学习总结

    20165203 2017-2018-2<Java程序设计>第一周学习总结 教材学习内容总结 (一)Java的地位 Java是面向对象编程,并涉及网络.多线程等重要的基础知识,是一门很好的 ...

  4. 20155229 2016-2007-2 《Java程序设计》第一周学习总结

    20155229 2016-2007-2 <Java程序设计>第一周学习总结 教材学习内容总结 1~18章的提问: 第一章:怎样撰写Java才不会沦于死背API文件.使用"复制. ...

  5. 20175227张雪莹 2018-2019-2 《Java程序设计》第九周学习总结

    20175227张雪莹 2018-2019-2 <Java程序设计>第九周学习总结 教材学习内容总结 第十一章 JDBC数据库操作 MySQL数据库管理系统 下载安装MySQL 若下载的是 ...

  6. 20155305乔磊2016-2017-2《Java程序设计》第九周学习总结

    20155305 2016-2017-2 <Java程序设计>第九周学习总结 教材学习内容总结 JDBC入门 JDBC简介 1.JDBC是java联机数据库的标准规范,它定义了一组标准类与 ...

  7. 20155210潘滢昊 2016-2017-2《Java程序设计》第一周学习总结

    20155210 2016-2017-2<Java程序设计>第一周学习总结 教材学习内容总结 1.1.1: 本节主要讲了Java的由来,1995年5月23日是Java公认的诞生日.还有版本 ...

  8. 20175317 《Java程序设计》第一周学习总结

    20175317 <Java程序设计>第一周学习总结 教材学习内容总结 本周学习了Java大致的开发步骤,完成了课件自带的习题. 学习了在windows与Linux系统下不同的编译方法,掌 ...

  9. 20172318 2016-2017-2 《Java程序设计》第一周学习总结

    20172318 2016-2017-2 <Java程序设计>第一周学习总结 教材学习内容总结 在教材中基本明白了计算机系统的运行方式,了解了对于高级语言是使用是掌握好编程的关键,掌握了一 ...

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

    20175208 2018-2019-2 <Java程序设计>第九周学习总结 一.教材学习内容总结: 第11章 JDBC与MySQL数据库 MySQL数据库管理系统 MySQL数据库管理系 ...

最新文章

  1. C++ - 给出一个函数来连接两个字符串A和B,其中字符串A的后几个字节和字符串B的前几个字节重叠
  2. 大数据神器Kafka入门
  3. 实现verilog设计控制交通灯
  4. c#做端口转发程序支持正向连接和反向链接
  5. 嵌入式Linux初始化硬件RTC,嵌入式Linux系统中的快速启动技术研究
  6. Linux协议栈网桥部分之CAM表操作
  7. Vue2 后台管理系统解决方案
  8. 快手内容运营-数据分析面试
  9. 金橙子打标卡labview打标开发没头绪?c#封装成dll后labview调用真香!
  10. kettle 使用教程(入门)
  11. vbs定时自动发送qq消息
  12. 2022-2023年华南师范大学工商管理硕士(MBA)招生简章
  13. mysql函数 动态语句_自定义函数动态执行SQL语句
  14. MySQL【触发器】
  15. 《R语言数据挖掘》读书笔记:三、分类
  16. DVB信道编解码与调制解调
  17. Collections与Comparator
  18. 二叉树算法解题思路总结(一)
  19. quartus 中直接引用的延时模块(Modelsim已验证)
  20. python信号降噪_EEG(P300)信号数据滤波降噪

热门文章

  1. 浅谈集群、分布式、微服务的异同
  2. 创业被泼冷水怎么办?
  3. 与其纠结,不如放弃!
  4. Stack Overflow 2017 开发者调查报告(程序员必看)
  5. 这视频里的人真是帅爆了!
  6. twrp两种方式刷入的mm管理器_Google Pixel C 刷入Magisk
  7. *第十六周*数据结构实践项目二【大数据集上排序算法性能的体验】
  8. Linux进阶之排错
  9. spark 三种部署模式的区别对比
  10. quartz定时任务中常用的cron表达式