• 《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未来发展: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的内部工作机制


  • 相关阅读:
    java中的lamda表达式
    Arrays.sort()中Lambda表达式
    检索中的函数及不同范围的处理
    2014.6.24
    2014.6.23
    第六天培训
    第五天培训
    第四天培训
    第三天培训!
    第二天培训
  • 原文地址:https://www.cnblogs.com/lsx1993/p/4631751.html
Copyright © 2020-2023  润新知