• ch03 Threads
  • Running Threads
  • Subclassing Thread
  • Implementing the Runnable Interface
  • Returning Information from a Thread
  • Race Conditions
  • Polling
  • Callbacks
  • Futures Callables and Executors
    • Java 范型 Generic Types

      • 范型接口
      • 泛型方法
    • Synchronized Methods
  • Deadlock
  • Thread Scheduling
    • Priorities
    • Preemption
    • Blocking
    • Yielding
    • Joining threads
    • Waiting on an object
  • Thread Pools and Executors

ch03 Threads

By the time a server is attempting to handle a thousand or more simultaneous connections, performance slows to a crawl.

There are at least two solutions to this problem.
* reuse processes rather than spawning new ones
* use lightweight threads instead of heavyweight processes to handle connections

a thread-based design is usually where you should start until you can prove you’re hitting a wall.

Running Threads

A thread with a little t is a separate, independent path of execution in the virtual machine. A Thread with a capital T is an instance of the java.lang.Thread class.

There is a one- to-one relationship between threads executing in the virtual machine and Thread ob‐ jects constructed by the virtual machine.

Thread t = new Thread();
t.start();

To give a thread something to do, you either subclass the Thread class and override its run() method, or implement the Runnable interface and pass the Runnable object to the Thread constructor Separates the task that the thread performs from the thread itself more cleanly.

When the run() method completes, the thread dies. In essence, the run() method is to a thread what the main() method is to a traditional nonthreaded program.

A multithreaded program exits when both the main() method and the run() methods of all nondaemon threads return. (Daemon threads perform background tasks such as garbage collection and don’t prevent the virtual machine from exiting.)

Subclassing Thread

/**
* subclass of Thread whose run() method calculates a 256-bit SHA-2 message digest
* for a specified file. It does this by reading the file with a DigestInput Stream.
* This filter stream calculates a cryptographic hash function as it reads the file.
* When it’s finished reading, the hash function is available from the digest() method.
*/import java.io.*;
import java.security.*;
import javax.xml.bind.*;public class DigestThread extends Thread{private String filename;public DigestThread(String filename){this.filename = filename;}// -----run@Overridepublic void run(){try{FileInputStream in = new FileInputStream(filename);/** MessageDigest 类为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法* 信息摘要是安全的单向哈希函数,它接收任意大小的数据,并输出固定长度的哈希值。* ```public static MessageDigest getInstance(String algorithm)throws NoSuchAlgorithmException返回实现指定摘要算法的 MessageDigest 对象。algorithm - 所请求算法的名称```*/MessageDigest sha = MessageDigest.getInstance("SHA-256");/** **DigestInputStream*** 使用输入流的方式完成摘要更新,调用on(boolean on)方法开启和关闭摘要功能。* 如果on(false),则DigestInputStream就变成了一般的输入流。* 默认摘要功能是开启的,如果开启了摘要功能,调用read方法时,* 将调用MessageDigest 类的update方法更新摘要。输入流的内容是read的字节而不是摘要。*/DigestInputStream din = new DigestInputStream(in, sha);while(din.read() != -1);din.close();// 在调用 digest 之后,MessageDigest 对象被重新设置成其初始状态。byte[] digest = sha.digest();/** String 字符串常量* StringBuffer 字符串变量(线程安全)* StringBuilder 字符串变量(非线程安全)*/StringBuilder result = new StringBuilder(filename);result.append(": ");/** printXXX 的函数就是encode,parseXXX 的函数就是decode。* 比如,String printBase64Binary(byte[])就是将字节数组做base64编码,* byte[] parseBase64Binary(String) 就是将Base64编码后的String还原成字节数组。*/result.append(DatatypeConverter.printHexBinary(digest));System.out.println(result);}catch(IOException ex){System.err.println(ex);}catch(NoSuchAlgorithmException ex){System.err.println(ex);}}// -----runpublic static void main(String[] args){for(String filename : args){Thread t = new DigestThread(filename);t.start();}}}

编译运行

JunrdeMacBook-Pro:src junr$ java DigestThread ch02.md
ch02.md: 0318537999FF14474A9963B5DA244810913E75ECB8AA0C3162A6021FB3A8AC6B

Getting information out of a thread back into the original calling thread is trickier because of the asynchronous nature of threads.

If you subclass Thread, you should override run() and nothing else! The various other methods of the Thread class—for example, start(), interrupt(), join(), sleep(), and so on—all have very specific se‐ mantics and interactions with the virtual machine that are difficult to reproduce in your own code.

Implementing the Runnable Interface

One way to avoid overriding the standard Thread methods is not to subclass Thread. Instead, write the task you want the thread to perform as an instance of the Runnable interface. This interface declares the run() method, exactly the same as the Thread class:

public void run()

import java.io.*;
import java.security.*;
import javax.xml.bind.*;public class DigestRunnable implements Runnable{private String filename;public DigestRunnable(String filename){this.filename = filename;}// -----run@Overridepublic void run(){try{FileInputStream in = new FileInputStream(filename);/** MessageDigest 类为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法* 信息摘要是安全的单向哈希函数,它接收任意大小的数据,并输出固定长度的哈希值。* ```public static MessageDigest getInstance(String algorithm)throws NoSuchAlgorithmException返回实现指定摘要算法的 MessageDigest 对象。algorithm - 所请求算法的名称```*/MessageDigest sha = MessageDigest.getInstance("SHA-256");/** **DigestInputStream*** 使用输入流的方式完成摘要更新,调用on(boolean on)方法开启和关闭摘要功能。* 如果on(false),则DigestInputStream就变成了一般的输入流。* 默认摘要功能是开启的,如果开启了摘要功能,调用read方法时,* 将调用MessageDigest 类的update方法更新摘要。输入流的内容是read的字节而不是摘要。*/DigestInputStream din = new DigestInputStream(in, sha);while(din.read() != -1);din.close();// 在调用 digest 之后,MessageDigest 对象被重新设置成其初始状态。byte[] digest = sha.digest();/** String 字符串常量* StringBuffer 字符串变量(线程安全)* StringBuilder 字符串变量(非线程安全)*/StringBuilder result = new StringBuilder(filename);result.append(": ");/** printXXX 的函数就是encode,parseXXX 的函数就是decode。* 比如,String printBase64Binary(byte[])就是将字节数组做base64编码,* byte[] parseBase64Binary(String) 就是将Base64编码后的String还原成字节数组。*/result.append(DatatypeConverter.printHexBinary(digest));System.out.println(result);}catch(IOException ex){System.err.println(ex);}catch(NoSuchAlgorithmException ex){System.err.println(ex);}}// -----runpublic static void main(String[] args){for(String filename:args){DigestRunnable dr = new DigestRunnable(filename);Thread t = new Thred(dr);t.start()}}
}

Returning Information from a Thread

Most people’s first reaction is to store the result in a field and provide a getter method

Race Conditions

假设我们写了下面的类,用于返回一个结果。

import java.io.*;
import java.security.*;public class ReturnDigest extends Thread{private String filename;private byte[] digest;public ReturnDigest(String filename){this.filename = filename;}@Overridepublic void run(){try{FileInputstream in = new FileInputStream(filename);MessageDigest sha = MessageDigest.getInstance("SHA-256");DigestInputStream din = new DigestInputStream(in, sha);while(din.read() != -1);din.close();digest = sha.digest();}catch(...){...}}public byte[] getDIgest(){return digest;}
}

上面的类通过getDigest 返回了一个结果

我们下面的类,调用了上面的类

import javax.xml.bind.*;
public classs ReturnDigestUserInterface{public static void main(String[] args) {ReturnDigest[] digests = new ReturnDigest[args.length];for(int i=0;i < args.length; i++){digests[i] = new ReturnDigest(args[i]);digests[i].start();}for(int i = 0; i < args.length; i++){System.out.println(digests[i].getDigest());}}
}

那么就有可能出现竞争,第二个for循环可能在第一个循环结束,但是线程都没有结束的情况下输出,就会出现错误。

Polling

The solution most novices adopt is to make the getter method return a flag value (or perhaps throw an exception) until the result field is set.

可以在main中的循环里,不断判断是否完成,是不是 == null,不等于再输出。

Callbacks

let the thread tell the main program when it’s finished. It does this by invoking a method in the main class that started it. This is called a callback because the thread calls its creator back when it’s done. This way, the main program can go to sleep while waiting for the threads to finish and not steal time from the running threads.

在 run 的最后加上

CallbackDigestUserInterface.receiveDigest(digest, filename);
import javax.xml.bind.*;public class CallbackDigestUserInterface{public static void receiveDigest(byte[] digest, String name){System.out.print(digest)}public static void main(String[] args){for(String filename : args){CallbackDigest cb = new CallbackDigest(filename);Thread t = new Thread(cb);t.start();}}
}

就只是相当于加了个函数??
具体的代码请参考原书P65

use static methods for the callback so that CallbackDigest only needs to know the name of the method in CallbackDigestUserInterface to call. However, it’s not much harder (and it’s considerably more common) to call back to an instance method. In this case, the class making the callback must have a reference to the object it’s calling back. Generally, this reference is provided as an argument to the thread’s constructor. When the run() method is nearly done, the last thing it does is invoke the instance method on the callback object to pass along the result.

Therefore, it’s good form to avoid launching threads from con‐ structors.

Futures, Callables and Executors

Instead of directly creating a thread, you create an ExecutorService that will create threads for you as needed.

You submit Callable jobs to the ExecutorService and for each one you get back a Future.

At a later point, you can ask the Future for the result of the job. If the result is ready, you get it immediately. If it’s not ready, the polling thread blocks until it is ready. The advantage is that you can spawn off many different threads, then get the answers you need in the order you need them.

import java.util.concurrent.Callable;class FindMaxTask implements Callable<Integer>{private int[] data;private int start;private int end;FindMaxTask(int[] data, int start, int end){this.data = data;this.start = start;this.end = end;}public Integer call(){int max = Integer.MIN_VALUE;for(int i = start; i < end; i++){if(data[i] > max) max = data[i];}return max;}
}

Although you could invoke the call() method directly, that is not its purpose. Instead, you submit Callable objects to an Executor that spins up a thread for each one.

import java.util.concurrent.*;
public class MultithreadedMaxFinder {public static int max(int[] data) throws InterruptedException, ExecutionException {if (data.length == 1) { return data[0];} else if (data.length == 0) {throw new IllegalArgumentException();}// split the job into 2 piecesFindMaxTask task1 = new FindMaxTask(data, 0, data.length/2);FindMaxTask task2 = new FindMaxTask(data, data.length/2, data.length);// spawn 2 threadsExecutorService service = Executors.newFixedThreadPool(2);Future<Integer> future1 = service.submit(task1);Future<Integer> future2 = service.submit(task2); return Math.max(future1.get(), future2.get());}
}

Java 范型 Generic Types

class Cell<E>{private Cell<E> next;private E element;public Cell(E element){this.element = element;}public Cell(E element, Cell<E> next){this.element = element;this.next = next;}public Cell<E> getNext(){return next;}public void setNext(Cell<E> next){this.next = next;}
}

By convention, type variables have single character names: E for an element type, K for a key type, V for a value type, T for a general type, and so forth.

When you define a generic class, all invocations of that generic class are simply expressions of that one class. Declaring a variable strCell as Cell tells the compiler that strCell will refer to an object of type Cell where E will be String. It does not tell the compiler to create a new class Cell.

The following code shows quite clearly that there is just one class because the value of same is TRue:

Cell<String> strCell = new Cell<String>("Hello");
Cell<Integer> intCell = new Cell<Integer>(25);
boolean same = (strCell.getClass() == intCell.getClass());

泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。

泛型类中的类型参数几乎可以用于任何可以使用接口名、类名的地方,下面的代码示例展示了 JDK 5.0 中集合框架中的 Map 接口的定义的一部分:
public interface Map

范型接口

在泛型接口中,生成器是一个很好的理解,看如下的生成器接口定义

public interface Generator<T> {public T next();
}

然后定义一个生成器类来实现这个接口:

public class FruitGenerator implements Generator<String> {private String[] fruits = new String[]{"Apple", "Banana", "Pear"};@Overridepublic String next() {Random rand = new Random();return fruits[rand.nextInt(3)];}
}
public class Main {public static void main(String[] args) {FruitGenerator generator = new FruitGenerator();System.out.println(generator.next());System.out.println(generator.next());System.out.println(generator.next());System.out.println(generator.next());}
}

泛型方法

一个基本的原则是:无论何时,只要你能做到,你就应该尽量使用泛型方法。也就是说,如果使用泛型方法可以取代将整个类泛化,那么应该有限采用泛型方法。下面来看一个简单的泛型方法的定义:

public class Main {public static <T> void out(T t) {System.out.println(t);}public static void main(String[] args) {out("findingsea");out(123);out(11.11);out(true);}
}```abstract 可以看到方法的参数彻底泛化了,这个过程涉及到编译器的类型推导和自动打包,也就说原来需要我们自己对类型进行的判断和处理,现在编译器帮我们做了。这样在定义方法的时候不必考虑以后到底需要处理哪些类型的参数,大大增加了编程的灵活性。<T>是为了规范参数;T表示的是返回值类型。<div class="se-preview-section-delimiter"></div># SynchronizationThe exact order in which one thread preempts the other threads is indeterminate.<div class="se-preview-section-delimiter"></div>## Synchronized BlocksTo indicate that these five lines of code should be executed together, wrap them in a synchronized block that synchronizes on the System.out object, like this:<div class="se-preview-section-delimiter"></div>```java
synchronized (System.out) {System.out.print(input + ": "); System.out.print(DatatypeConverter.printHexBinary(digest)); System.out.println();
}

Synchronization must be considered any time multiple threads share resources. These threads may be instances of the same Thread subclass or use the same Runnable class, or they may be instances of completely different classes.

Synchronized Methods

You can synchronize an entire method on the current object (the this reference) by adding the synchronized modifier to the method declaration.

public synchronized void write(String m) throws IOException{//
}

Simply adding the synchronized modifier to all methods is not a catchall solution for synchronization problems.

  • 减慢运行速度
  • 增加deadlock的几率

Deadlock

Thread Scheduling

It is possible for such a thread to starve all other threads by taking all the available CPU resources. With a little thought, you can avoid this problem. In fact, starvation is a considerably easier problem to avoid than either mis-synchronization or deadlock.

Priorities

Not all threads are created equal. Each thread has a priority, specified as an integer from 0 to 10. When multiple threads are ready to run, the VM will generally run only the highest-priority thread, although that’s not a hard-and-fast rule. In Java, 10 is the highest priority and 0 is the lowest. The default priority is 5, and this is the priority that your threads will have unless you deliberately set them otherwise.

public final void setPriority(int newPriority)

Preemption

There are two main kinds of thread scheduling: preemptive and coop‐ erative.

There are 10 ways a thread can pause in favor of other threads or indicate that it is ready to pause. These are:
• It can block on I/O.
• It can block on a synchronized object.
• It can yield.
• It can go to sleep.
• It can join another thread.
• It can wait on an object.
• It can finish.
• It can be preempted by a higher-priority thread.
• It can be suspended.
• It can stop.

Blocking

Blocking occurs any time a thread has to stop and wait for a resource it doesn’t have.

Yielding

A thread does this by invoking the static Thread.yield() method.

Joining threads

Java provides three join() methods to allow one thread to wait for another thread to finish before continuing. These are:

public final void join() throws InterruptedException
public final void join(long milliseconds) throws InterruptedException
public final void join(long milliseconds, int nanoseconds)throws InterruptedException

The joining thread (i.e., the one that invokes the join() method) waits for the joined thread (i.e, the one whose join() method is invoked) to finish.

double[] array = new double[10000];
for (int i = 0; i < array.length; i++){array[i] = Math.random();
}
SortThread t = new SortThread(array);
t.start();
try{t.join();System.out.print("Minimum: " + array[0]);...
}catch(InterruptedException ex){...
}

比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。

line 8 joins the current thread to the sorting thread. At this point, the thread executing these lines of code stops in its tracks. It waits for the sorting thread to finish running.

Notice that at no point is there a reference to the thread that pauses. It’s not the Thread object on which the join() method is invoked; it’s not passed as an argument to that method. It exists implicitly only as the current thread. If this is within the normal flow of control of the main() method of the program, there may not be any Thread variable anywhere that points to this thread.

A thread that’s joined to another thread can be interrupted just like a sleeping thread if some other thread invokes its interrupt() method.

Waiting on an object

等待时释放lock,其他线程改变object时,会通知等待线程。
A thread can wait on an object it has locked. While waiting, it releases the lock on the object and pauses until it is notified by some other thread.

Another thread changes the object in some way, notifies the thread waiting on that object, and then continues.

Waiting pauses execution until an object or re‐ source reaches a certain state. Joining pauses execution until a thread finishes.

为了wait on an object,需要暂停的thread首先得到 the lock on the object using synchronized 然后调用wait

public final void wait() throws InterruptedException
public final void wait(long milliseconds) throws InterruptedException
public final void wait(long milliseconds, int nanoseconds)throws InterruptedException

These methods are not in the Thread class; rather, they are in the java.lang.Object class.

When one of these methods is invoked, the thread that invoked it releases the lock on the object it’s waiting on (though not any locks it possesses on other objects) and goes to sleep.

It remains asleep until one of three things happens:
• The timeout expires.
• The thread is interrupted.
• The object is notified.

Thread Pools and Executors

Starting a thread and cleaning up after a thread that has died takes a noticeable amount of work from the virtual machine, especially if a program spawns hundreds of threads

The Executors class in java.util.concurrent makes it quite easy to set up thread pools. You simply submit each task as a Runnable object to the pool. You get back a Future object you can use to check on the progress of the task.

import java.io.*;
import java.util.zip.*;pubic class GZipRunnable implements Runnable{private final File input;public GZipRunnable(FIle input){this.input = input;}@Overridepublic void run(){if(!input.getName().endsWith(".gz")){File output = new File(input.getParent(), input.getName() + ".gz");if(!output.exists()){try{InputStream in = new BufferedInputStream(new FileInputStream(input));OutputStream out = new BufferedOutputStream(new GZIPOutputStream(new FileOutputStream(output)));int b;while((b = in.read()) != -1)out.write(b);out.flush()}catch(IOException ex){System.err.println(ex);}}}}
}
import java.io.*;
import java.util.concurrent.*;public class GZipAllFiles{public final static int THREAD_COUNT = 4;public static void main(String[] args){ExecutorService pool = Executors.newFixedThreadPool(THREAD_COUNT);for(String filename : args){File f = new File(filename);if(f.exists()){if(f.isDirectory()){FIle[] files = f.listFiles();for(int i = 0; i < files.length; i++){if(!files[i].isDirectory()){Runnable task = new GZipRunnable(files[i]);pool.submit(task);}}}else{}}}pool.shutdown()}
}

java网络编程读书笔记-Ch03相关推荐

  1. java 网络编程学习笔记

    java 网络编程学习笔记 C/S模式:客户端和服务器 客户端创建流程 1 1.建立Socket端点 2 3 Socket s = new Socket(绑定地址, 绑定端口); 2.确认源数据方式和 ...

  2. JAVA网络编程个人笔记 第4章 inet地址

    JAVA网络编程个人笔记 第4章 inet地址 Internet寻址 创建InetAddress对象 常用方法 getLocalHost() 代码实现 getByName() 代码实现 getAllB ...

  3. java网络编程 个人笔记 第二章 流

    java网络编程 个人笔记 第二章 流 流 I/O介绍 java流式输入/输出原理 输入输出流分类 节点流和处理流 节点流的类型 处理流的类型 InputStream(输入流) InputStream ...

  4. UNIX网络编程--读书笔记

    会集中这段时间写UNIX网络编程这本书的读书笔记,准备读三本,这一系类的文章会不断更新,一直会持续一个月多,每篇的前半部分是书中讲述的内容,每篇文章的后半部分是自己的心得体会,文章中的红色内容是很重要 ...

  5. Unix 网络编程 读书笔记1

    第一章: C/C++语言提供两种不同的编程模式:IPL32和PL64. ► IPL32 ● 表示integer/pointer/long三种数据类型是32位(4个字节),在这种模式下,提供32 位的地 ...

  6. UNIX网络编程读书笔记:套接口选项

    概述 有很多方法来获取和设置影响套接口的选项: getsockopt和setsockopt函数 fcntl函数 ioctl函数 getsockopt和setsockopt函数 这两个函数仅用于套接口. ...

  7. java并发编程-读书笔记

    第一章.Java锁类 Lock接口 使用方式 Lock lock = new ReentrantLock(); lock.lock(); try {// do something }finally { ...

  8. Java 网络编程学习笔记(三)—— URL 和 URI

    一.URL URL是一个URI,除了标识一个资源,还会为资源提供一个特定的网络位置.然而通用的URI可以告诉你一个资源是什么,但是无法告诉你它在哪里以及如何得到这个资源. URL的语法: protoc ...

  9. UNIX网络编程读书笔记:套接口地址结构

    前言 大多数套接口函数都需要一个指向套接口地址结构的指针作为参数.每个协议族都定义它自己的套接口地址结构.这些结构的名字均以"sockaddr_"开头,并以对应每个协议族的唯一后缀 ...

最新文章

  1. 2022-2028年中国聚碳酸亚丙酯(PPC)行业市场深度分析及未来趋势预测报告
  2. 我所认识的数据产品经理(文末有彩蛋)
  3. 前端 js 非控件 使用标签打印机 打印二维码和文本_青岛Web前端(HTML5)面试题分享...
  4. CSS结构的基础认知
  5. 人工智能 - paddlepaddle飞桨 - 深度学习基础教程 - 词向量
  6. Python稳基修炼的经典案例14(计算机二级、初学者必会字符格式处理)
  7. ant design Cascader 实现联动省市区数据
  8. raspberry ubuntu 修改源为清华_在Windows7基础上安装Ubuntu系统,实现双系统操作
  9. [算法]圆圈中最后剩下的数
  10. CentOS下常用配置文件和命令以及目录结构备注
  11. 这款完全开源可自主DIY的小程序商城太强大了,直接可给客户搭建赚米
  12. FPS 游戏飞天遁地原理
  13. 8cm等于多少像素_一寸照片像素是多少
  14. 海外 网易云音乐无法正常使用 极简
  15. 小程序:Thu May 05 2022 11:03:00 GMT+0800 (中国标准时间) 渲染层错误
  16. 推荐一款好用解压RAR、ZIP文件Mac软件,可以输入密码Dr. Unarchiver
  17. leetcode717.1比特与2比特字符(帮你读题)
  18. php adodb5,常用的php ADODB使用方法集锦
  19. python3.5安装JPype1失败问题
  20. LPL2019职业联赛春季+夏季赛数据分析

热门文章

  1. L2-032 彩虹瓶 (25 分)
  2. 作为一名Java程序员,我为何不在生产项目中转向Go?
  3. Xubuntu22.04之超级效率工具:推4款番茄法(第一百七十)
  4. 2020 超分辨率技术发展趋势
  5. 【Word标题样式随正文的样式进行首行缩进的解决办法】
  6. 数字1亿里面有多少个1呢
  7. 【电子学会】2020年06月图形化四级 -- 计算生肖
  8. OpenCV--图像的基本表示方法
  9. 移动硬盘显示磁盘未被格式化要怎样办啊
  10. 把最后一个two单词首字母大写,也可以为其他单词