《Java程序員修煉之道》

原子類:java.util.concurrent.atomic 
線程鎖:java.util.concurrent.locks 
對付死鎖:boolean acquired = lock.tryLock(wait,TimeUtils.MILLISECONDS); 
CountDownLatch鎖存器:讓線程在同步前作準備工做。 

CopyOnWriteArrayList 

BlockingQueue<LsxUnit<MyClass>> 

控制執行: 
任務:Callable, Future, FutureTask 
執行者:ScheduledThreadPoolExecutor 

分至合併框架:ForkJoinPool 
比線程小的併發單元:ForkJoinTask 

第1章 初識Java7
路線圖
第一部分:全面介紹Java7新特性
第1章:介紹了Java7及其Coin項目
第2章:全面介紹新I/O API,包括對文件系統API的全面梳理,新的異步I/O能力。
第二部分:Java關鍵編程知識和技術
第3章:依賴注入技術
第4章:現代併發開發
第5章:JVM的類文件和字節碼(Java工做原理)
第6章:Java應用程序調優,垃圾收集器
第三部分:JVM上的新語言和多語言編程
 
第四部分:平臺和多語言編程知識付諸實踐
——依賴注入,測試驅動開發,持續集成系統Jenkins。
——JVM上的多語言開發:Groovy,Scala,Clojure。

——併發,性能,字節碼,類加載java

——Java將來發展:lambda表達式,模塊化Jigsaw項目。程序員

 

 

初識Java7

Java7的變化爲兩塊:Coin項目,NIO.2。正則表達式

1.1 Java語言和平臺的區別編程

Java系統規範中最重要的是:《Java語言規範》JLR和《JVM規範》VMSpec。數組

鏈接Java語言平臺之間的是:統一的類文件.class格式定義。安全

大多數流行框架會在類加載過程當中對類進行改造。服務器

流程:.java(javac).class(類加載器)轉換後的.class(解釋器)可執行代碼(JIT編譯器)機器碼。網絡

注意:JVM字節碼實際上只是一種中間語言,不是真正的機器碼。多線程

而所謂的編譯器javac也不一樣於gcc,它只是一個針對Java源碼生成類文件的工具。併發

Java體系中真正的編譯器是JIT。

 

1.2 Coin項目:濃縮的都是精華

硬幣同樣小的變化。

提供新功能(難度遞增):1.類庫 2.工具提供的功能 3.語法糖4.語言新特性 5.類文件格式的變化 6.VM的新特性

——語法糖:數字中的下劃線(Java7)

——新的語言小特性,try-with-resources(Java7)

——類文件格式的變化,註解(Java5)

——JVM的新特性,動態調用(Java7)

Coin項目:3.語法糖4.語言新特性。

 

1.3 Coin項目中的修改

語法的6個新特性:

1.switch語句中的String。

2.數字常量的新形式。

3.改進的異常處理Multi-catch。

4.try-with-resources結構,自動關閉資源。

5.磚石語法:處理泛型。

6.變參警告位置的修改

 

1.3.1 switch語句中的String

    支持byte,char,short,int,枚舉常量和String

 

1.3.2 更強的數值文本表示法

——    數字常量可用二進制文本表示

——    整形常量中可以使用下劃線

之前:int x = Integer.parseInt("1100110",2);

Java7:int x = 0b1100110 (與二進制打交道)

100_00_000:編譯器會在編譯時去掉下劃線

long anotherLong = 2_147_483-648L;

 

1.3.3 改善後的異常處理

multicatch與final重拋。

try{....} catch(FileNotFoundException | ParseExceptoin  e) {.....}

 

catch(final Exception e ) {......}

final重拋:代表實際拋出的異常就是運行時遇到的異常。避免拋出籠統的異常類型。

 

1.3.4 try-with-resourch(TWR)

離開代碼塊時,資源自動關閉。文件或相似的東西。

把資源放在try的圓括號裏:

try(    OutputStream out = new FileOutputStream(file);

            InputStream is = url.openStream()        ) {

    byte[] buf = new byte[4096];

    int len;

    while((len = is.read(buf))>0){    

        out.write(buf,0,len);

    }

}

資源自動化管理代碼塊的基本形式

正確的用法:爲各個資源聲明獨立變量

try(FileInputStream in = new FileInputStream("SomeFile.bin");

    ObjectInputStream in = new ObjectInputStream(fin) ){......}

 

Java7中大多數資源類都被修改過,實現AutoCloseable接口。

 

1.3.5 磚石語法

    針對建立泛型定義和實例太多繁瑣的問題

原來:

Map<Integer,Map<String,String>> usersLists = 

    new HashMap<Integer,Map<String,String>>();

 

類型推斷改進:

Map<Integer,Map<String,String>> usersLists = 

    new HashMap<>();

 

1.3.6 簡化變參方法調用

遠離類型系統

 

修改語言很是困難,用類庫實現新特性老是相對容易一些。


第2章 新I/O

本章內容:

——新I/O API(NIO.2)

——Path:基於文件和目錄的I/O新基礎

——Files應用類和各類輔助方法

——如何實現常見的I/O應用場景

——介紹異步I/O

核心類庫的變化,java.nio包內。掌握Java以前版本處理I/O的方法。

優勢:

——徹底取代java.io.File與文件系統的交互。

——新的異步處理類,無需手動配置線程池和其餘底層併發控制。即可在後臺線程中執行文件和網絡I/O操做。

——引入新的Network-Channel構造方法,簡化了套接字與通道的編碼工做。

 

Java7的NIO.2 API支持目錄樹導航(Files.walkFileTree()),符號連接Files.isSysbolicLink(),能用一行代碼讀取文件屬性(Files.readFileAttributes())

 

讀取這些文件極可能打斷程序的主流程。面對這一要求,在Java5/6時代可能會用java.util.concurrent中的類建立線程池和工做線程隊列,再用單獨後臺線程讀取文件。

 

NIO.2 API 中用AsynchronousFileChannel,不用指定工做線程或隊列就可在後臺讀取大型文件。

NIO2爲多線程文件和套接字訪問的應用提供了一個簡單的抽象層。IDE,應用服務器和各類流行的框架會大量應用這些特性。

 

將try-with-resources和NIO.2中的新API結合起來能夠寫出很是安全的I/O程序。

 

先了解新的文件系統抽象層:Path和它的輔助類。Path之上,經常使用的文件系統操做,複製和移動文件。

 

2.1 Java I/O簡史

2.1.1 Java1.0-1.3

缺少對非阻塞I/O的支持。

 

2.1.2 在Java1.4中引入NIO

——在Java1.4中引入非阻塞I/O

——在Java7中對非阻塞I/O進行修改

2002年發佈Java1.4時新增的特性:

——爲I/O操做抽象出緩衝區和通道層。

——字符集的編碼和解碼功能。

——提供了可以將文件映射爲內存數據的接口。

——實現非阻塞I/O的能力。

——基於流行的Perl實現的正則表達式類庫。

但那時對文件系統中的文件和目錄處理而言支持力度還不夠。那時的java.io.File類有些比較煩人的侷限性。

——在不一樣的平臺中對文件名的處理不一致。

——沒有統一的文件屬性模型(好比讀寫訪問模型)

——遍歷目錄困難。

——不能使用平臺/操做系統的特性

——不支持文件系統的非阻塞操做。

 

2.1.3 下一代I/O - NIO.2

NIO.2的三個主要目標:

(1)提供一個批量獲取文件屬性的文件系統接口。去掉和特定文件系統相關的API,還有一個用於引入標準文件系統實現的服務提供者接口。

(2)提供一個套接字和文件都可以進行異步(與輪詢、非阻塞相對)I/O操做的API。

(3)完成JSR-51中定義的套接字——通道功能,包括額外對綁定,選項配置和多播數據報的支持。

 

2.2 文件I/O的基石:Path
NIO.2把位置(Path表示)的概念,和物理文件系統的處理(複製一個文件)分得很清楚。
物理文件系統的處理一般是由Files輔助類實現。

 

2.2.1 建立一個Path

Paths.get(String first, String...more)

Path listing = Path.get("/usr/bin/zip")

listing.toAbsolutePath()

2.2.2 從Path中獲取信息

正在處理的路徑的相關信息。

listing.getFileName()

listing.getNameCount()獲取名稱元素的數量

listing.getParent()

listing.getRoot()

listing.sunpath(0,2)

2.2.3 移除冗餘項

Path normalizedPath = Paths.get("./Listing_2_1.java").normalize();

toRealPath()方法融合了toAbsolutePath()和normalize()兩個方法,還能檢測並跟隨符號連接。

2.2.5 NIO.2 Path和Java已有的File類

File file = new File("../Listing_2_1.java");

Path listing = file.toPath();

listing.toAbsolutePath();

file = listing.toFile();

2.3 處理目錄和目錄樹
2.3.1 在目錄中查找文件
Path dir = Paths.get("C:\\workspace\\java7developer");
try(DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.properties")){
for(Path entry: stream)
{
    System.out.println(entry.getFileName());
}
}catch(IOException e){
        System.out.println(e.getMessage());
}

過濾流中使用到的模式匹配稱爲glob模式匹配。

2.3.2 遍歷目錄樹

Files.walkFileTree(Path startingDir, FileVisitor<? super Path> visitor);

默認實現類:SimpleFileVisitor<T>

public class Find
{
 public static void main(String[] args) throws IOException
 {
  Path startingDir = Paths.get("C:\\workspace\\java7developer\\src");
  Files.walkFileTree(startingDir,new FindJavaVisitor());
 }
 
 private static class FindJavaVisitor extends SimpleFileVisitor<Path>
 {
  @Override
  public FindVisitorResult visitFile(Path file,BasicFileAttributes attrs)
  {
   if(file.toString().endsWith(".java")){
    System.out.println(file.getFileName());
   }
   return FindVisitorResult.CONTINUE;
  }
 }
}
2.4 NIO.2的文件系統I/O 
Files類(複製移動刪除或者處理文件的工具類),WatchService類(監視文件或目錄的核心類)。
 
2.4.1 建立和刪除文件
Path target = Paths.get("D:\\backup\\lisuxuan.txt");
Path file = Files.createFile(target);
2.4.2 文件的複製和移動

Path source = Path.get("...");

Files.copy(source,target,REPLACE_EXISTING);

Files.move(Path source,Path target,CopyOptions...);

2.4.3 文件的屬性

Files.getLastModifiedTime(zip);

Files.size(zip);

Files.readAttributes(zip,"*");

2.4.4 快速讀取文件

1.打開文件

用帶緩衝區的讀取器和寫入器或者輸入輸出流。

Path logFile = Paths.get("/tmp/app.log");

try(BufferedReader reader = Files.newBufferedReader(logFile,StandardCharsets.UTF_8)){

 String line;

 while( (line = reader.readline())!= null ){

  ...

 }

}

打開寫入文件

Path logFile = Paths.get("/tmp/app.log");

try(BufferedWriter writer= Files.newBufferedWriter(logFile,StandardCharsets.UTF_8,StandardOpenOption.WRITE)){

Writer.write("hello world!");

  ...

 }

}

設置字符編碼 new String(byte[],StandardCharsets.UTF_8)

兼容過去基於java.io包的I/O:

Files.newInputStream(Path,OpenOption...)

 

讀取所有行和所有字節的簡化方法:

Path logFile = Paths.get("/tmp/app.log");

List<String> lines = Files.readAllLines(logFile,StandardCharsets.UTF_8);

byte[] bytes = Files.readAllBytes(logFile);

2.4.5 文件修改通知

 

2.4.6 SeekableByteChannel

java.nio.channels.FileChannel

這個類的尋址能力讓開發人員能夠靈活的處理文件內容。

Path logFile = Paths.get("c:\\temp.log");

ByteBuffer buffer = ByteBuffer.allocate(1024);

FileChannel channel = FileChannel.open(logFile,StandardOpenOption.READ);

channel.read(buffer,channel.size() - 1000);

2.5 異步I/O 操做

做用:可使用多個後臺線程讀寫文件、套接字和通道中的數據。

AsynchronousFileChannel

AsynchronousSocketChannel

AsynchronousServerSocketChannel

2.5.1 未來式

使用java.util.concurrent.Future接口。

當你但願由主控線程發起I/O操做,並輪詢等待結果時,通常都會採用未來式異步處理。

Future:用來保存異步處理的操做結果。

實現:API/JVM爲執行這個任務建立了線程池和通道組。

        AsynchronousFileChannel會關聯線程池,它的任務是接收I/O處理事件,並分發給負責處理通道中I/O操做結果的結果處理器。跟通道中發起的I/O操做關聯的結果處理器確保是由線程池中的某個線程產生的。

        默認線程池是由AsynchronousChannelGroup類定義的系統屬性進行配置的。

2.5.2 回調式

主線程會派一個偵察員CompletionHandler到獨立的線程中執行I/O操做。這個偵查員將帶着I/O操做的結果返回到主線程中,這個結果會觸發本身的completed或failed方法。

 

在異步事件剛一成功或者失敗並須要立刻採起行動的時候,採用回調式。

代碼清單2-9 異步I/O——回調式

============================================
try
{
 Path file = Paths.get("/usr/karianna/foobar.txt");
 AsynchronousFileChannel channel = AsynchronousFileChannel.open(file);
 
 ByteBuffer buffer = ByteBuffer.allocate(100_000);
 
 channel.read(buffer,0,buffer,new CompletionHandler<Integer,ByteBuffer>()
 {
  public void completed(Integer result,ByteBuffer attachment)
  {
   System.out.println("Bytes read [" + result + "]");
  }
  public void failed(Throwable exception,ByteBuffer attachment)
  {
   System.out.println(exception.getMessage());
  }
 });
}catch(IOException e)
{
 System.out.println(e.getMessage());
}

2.6 Socket和Channel的整合
NetworkChannel
——通道:java.nio.Channels包,表示鏈接到執行I/O操做的實體,好比文件和套接字,定義用於多路傳輸,非阻塞I/O操做的選擇器。
——java.net.Socket類,
2.6.1 NetworkChannel
新接口java.nio.channels.NetworkChannel表明一個鏈接到網絡套接字通道的映射。
 
package com.java7developer.chapter2;
 
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketOption;
import java.net.StandardSocketOptions;
import java.nio.channels.NetworkChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.Set;
 
public class Listing_2_10 {
 
  public static void main(String[] args) {
    SelectorProvider provider = SelectorProvider.provider();
    try {
      NetworkChannel socketChannel = provider.openSocketChannel();
      SocketAddress address = new InetSocketAddress(3080);
      socketChannel = socketChannel.bind(address);
 
      Set<SocketOption<?>> socketOptions = socketChannel.supportedOptions();
 
      System.out.println(socketOptions.toString());
      socketChannel.setOption(StandardSocketOptions.IP_TOS, 3);
      Boolean keepAlive = socketChannel
          .getOption(StandardSocketOptions.SO_KEEPALIVE);
    } catch (IOException e) {
      System.out.println(e.getMessage());
    }
  }
}
 

2.6.2 MuticastChannel

package com.java7developer.chapter2;
 
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.StandardProtocolFamily;
import java.net.StandardSocketOptions;
 
import java.nio.channels.DatagramChannel;
import java.nio.channels.MembershipKey;
 
public class Listing_2_11 {
 
  public static void main(String[] args) {
    try {//選擇網絡接口
      NetworkInterface networkInterface = NetworkInterface.getByName("net1");
 
      DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET);
 
      dc.setOption(StandardSocketOptions.SO_REUSEADDR, true);
      dc.bind(new InetSocketAddress(8080));
      dc.setOption(StandardSocketOptions.IP_MULTICAST_IF, networkInterface);
 
      InetAddress group = InetAddress.getByName("180.90.4.12");
      MembershipKey key = dc.join(group, networkInterface);
    } catch (IOException e) {
      System.out.println(e.getMessage());
    }
  }
}
 

 

 


第3章 依賴注入
第二部分 關鍵技術
——依賴注入。
——Java併發能力。Java內存模型,這個模型中的線程和併發是如何實現的。
——類加載,JVM如何加載、連接、驗證類。反射:Java7的MethodHandler、MethodType和動態調用。
——字節碼。
——垃圾回收和即時編譯器。這是JVM可以影響性能的兩個主要部分。
 
第3章 依賴注入
3.1 理解IoC和DI
依賴注入:使得對象從別人獲得依賴項,而不是由它本身來構造。
 
3.1.1 控制反轉
IoC思想:會有另外一段代碼擁有最初的控制線程,而且由它來調用你的代碼。而不是代碼調用它。
3.1.2 依賴注入
DI容器:Guice,Spring和PicoContainer。
3.1.3 轉成DI
package com.java7developer.chapter3;
 
import java.util.List;
 
/**
 * Code for listing 3_1 - AgentFinder interface
 */
public interface AgentFinder {
 
  public List<Agent> findAllAgents();
 
}
 
使用工廠或服務定位器模式:
問題:代碼中注入的是一個引用憑據,而不是真正實現接口的對象。
 
DI技術取代工廠模式:
手工DI注入
package com.java7developer.chapter3;
import java.util.ArrayList;
import java.util.List;
/**
 * Code for listing 3_4
 */
public class HollywoodServiceWithDI {
  public static List<Agent> getFriendlyAgents(AgentFinder finder) {
    List<Agent> agents = finder.findAllAgents();
    List<Agent> friendlyAgents = filterAgents(agents, "Java Developers");
    return friendlyAgents;
  }
  public static List<Agent> filterAgents(List<Agent> agents, String agentType) {
    List<Agent> filteredAgents = new ArrayList<>();
    for (Agent agent : agents) {
      if (agent.getType().equals("Java Developers")) {
        filteredAgents.add(agent);
      }
    }
    return filteredAgents;
  }
}
 
 
DI框架的優點:它能夠隨時隨地爲你的代碼提供依賴項。
 
3.2 Java中標準的DI
2009年Guice和SpringSource宣佈合做,共同打造一組標準的接口註解。JSR-330規範請求。倡導JavaSE的DI標準化。(javax.inject)
3.2.1 @Inject註解
註解(構造器,方法,屬性)
 
接下來是如何限定(進一步標識)注入的對象。
3.2.2 @Qualifier註解
 
3.2.3 @Named註解
Named註解是一個特別的@Qualifier註解。用名字標明要注入的對象。
 
3.2.4 @Scope註解
 
3.2.5 @Singleton註解
標準的生命週期註解。
3.3 Java中的DI參考實現:Guice 3
 

第4章 現代併發
——併發理論。——塊結構併發。——java.util.concurrent包(基本併發構建塊)。
——用分支/合併框架實現輕量級併發。
——Java內存模型,JMM。
 
不止原始的基礎Java併發機制。
本章目的:瞭解決定Java併發工做方式的底層平臺機制。
4.1 併發理論簡介
4.1.1 解釋Java線程模型
Java線程模型創建在兩個基本概念之上。
——共享的、默承認見的可變狀態。
——搶佔式線程調度。
 
java.util.concurrent包:一套編寫併發代碼的工具,比傳統的塊結構併發原語易用。
 
4.1.2設計理念
——安全性——活躍度——性能——重用性
 
4.2 塊結構併發(Java5以前)
synchroized,volatile等併發關鍵字,原始的、低級的多線程編程方式。
4.2.1 同步與鎖
synchroized【確立臨界區】代表執行整個代碼塊,或方法以前,線程必須取得合適的鎖。
對於方法,意味着要取得對象實例鎖。
對於靜態方法而言,則是類鎖。
對於代碼塊,程序員應該指明要取得哪一個對象的鎖。
 
Java同步與鎖的一些基本知識:
——只能鎖定對象,不能鎖定原始類型。
——被鎖定的對象數組中的單個對象不會被鎖定。
——靜態同步方法會鎖定它的Class對象,由於沒有實例對象能夠鎖定。
——內部類的同步是獨立於外部類的。
——synchroized並非方法簽名的組成部分,因此不能出如今接口的方法聲明中。
——Java的線程鎖是可重入的。
 
4.2.2 線程的狀態模型
最初建立:就緒狀態Ready。運行-睡眠/等待-準備就緒。
線程極可能由於等待I/O操做或者等待獲取其餘線程持有的鎖而被阻塞,可是沒有被交換出核心,而是仍然處於繁忙狀態。等待獲取可用的鎖或者數據。
 
4.2.3 徹底同步對象
——全部域在任何構造方法中的初始化都能達到一致的狀態。
——沒有公共域。
——全部方法都是同步的。
——全部方法經證實均可在有限的時間內終止。
——當處於非一致狀態時,不會調用非私有方法,也不會調用其餘實例的方法。
 
帶來的是性能的問題。
 
4.2.4 死鎖
有一個處理死鎖的技巧,就是在全部線程中都以相同的順序獲取線程鎖。
 
——不一樣線程對一個對象的修改經過主內存傳播。
 
4.2.6 關鍵字 volatile
——線程所見的值在使用以前會從主內存中再讀出來。
——線程所寫的值總會在指令完成以前被刷回到主內存中。
付出的代價:每次訪問都要額外刷一次內存。
好處:volatile不會引入線程鎖。該變量是真正線程安全的。
但只有寫入時不依賴當前狀態(讀取的狀態)的變量才應該聲明爲volatile。對於要關注當前狀態的變量,只能藉助線程鎖保證其絕對安全性。
 
4.2.7 不可變性
 
構建器模式。
 
final引用能夠指向非final域的對象。
 
4.3 現代併發應用程序的構件
4.3.1 原子類:java.util.concurrent.atomic
避免在共享數據上出現競爭危害的方法。
private final AtomicLong sequenceNumber = new AtomicLong(0);
sequenceNumber.getAndIncrement();
4.3.2 線程鎖:java.util.concurrent.locks
——添加不一樣類型的鎖,好比讀取鎖和寫入鎖。
——對鎖的阻塞沒有限制。容許在一個方法中上鎖,在另外一個方法中解鎖。
——tryLock()方法,若是線程得不到鎖,還能夠作別的事。
 
Lock接口的兩個實現類:
ReentrantLock:相似用在同步塊上的鎖。
ReentrantReadWriteLock:在須要讀取不少線程而寫入不多線程時,它的性能更好。



2006 Java5關鍵技術

1.依賴注入:解耦,可測試性,易讀性

2.併發編程:多核CPU革命

    java.util.concurrent內存模型,線程與併發實現

3.類加載

    JVM如何加載,連接和驗證類。用Javap深刻字節碼。

4.反射:Reflection

    Java7的MethodHandle,MethodType和動態調用。

5.性能調優藝術

    如何評測

6.垃圾回收GC和即時JIT編譯器

JVM中能影響性能的兩個主要部分

Java和JVM的內部工做機制

相關文章
相關標籤/搜索