• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
Lith
博客园    首页    新随笔    联系   管理    订阅  订阅
Java学习笔记(一) - Java基础语法
参考书目:Java核心技术 卷I

目录

  • 目录
  • 基础语法
  • 类/继承
    • 类关系
    • 类/对象
    • 继承
      • 反射
  • 接口/Lambda/内部类
    • 接口
    • Lambda
    • 内部类
    • 代理
  • 错误处理
    • 异常
    • 断言
    • 日志
  • 泛型
    • 泛型类
    • 泛型和虚拟机
    • 通配符类型
    • 反射和泛型
  • 集合
    • 集合接口
    • Collection集合
      • 链表
      • 数组列表
      • 散列集
      • 树集
      • 队列
    • Map集合
      • 基本映射
      • 其它映射
    • 泛型算法
  • 并发
    • 线程
    • 同步
    • 线程安全集合
    • 线程池
    • 异步
    • 进程

基础语法

  • 字符 ~ String , StringBuilder , String.format()
  • IO
    • Scanner , Console , System.out.println()
    • 日期/时间(索引$) : 应使用Java.time类
    • 文件 : Scanner in = new Scanner(Path.of("my.txt"),"UTF-8");
          PrintWrite.out = new PrintWriter("my.txt",StandardCharsets.UTF_8");
  • 循环
    • while() { } , do { } while()
    • for(int i;i<10;i++) { }
    • foreach(int e:a)
    • switch() {case 1: ... break; ..... ..... default: ... break;}
  • 大数
    • BigInteger
    • BigDecimal
  • 数组
    • int[] a = new int[100]
    • 重初始化 a = new int {.....}
  • 数据类型
    • byte,short,int,long,float,double,boolean,char(char类型涉及到字符集问题,推荐使用String代替)
序号 数据类型 位数 默认值 取值范围 举例说明
1 byte(位) 8 0 -2^7 - 2^7-1 byte b = 10;
2 short(短整数) 16 0 -2^15 - 2^15-1 short s = 10;
3 int(整数) 32 0 -2^31 - 2^31-1 int i = 10;
4 long(长整数) 64 0 -2^63 - 2^63-1 long l = 10l;
5 float(单精度) 32 0.0 -2^31 - 2^31-1 float f = 10.0f;
6 double(双精度) 64 0.0 -2^63 - 2^63-1 double d = 10.0d;
7 char(字符) 16 空 0 - 2^16-1 char c = 'c';
8 boolean(布尔值) 8 false true、false boolean b = true;

类/继承

类关系

  • 依赖:一个类的方法使用另一个类的对象
  • 聚合:类A的对象包含类B的对象
  • 继承:类A扩展类B
  • 接口实现,关联,直接关联

类/对象

  • 大小写敏感:Java 是大小写敏感的。
  • 类名:对于所有的类来说,类名的首字母应该大写。由若干单词组成,每个单词的首字母大写,例如 MyFirstJavaClass 。
  • 方法名:所有的方法名都以小写字母开头。含有若干单词,后面的单词首字母大写。
  • 源文件名:源文件名必须和类名相同。当保存文件的时候,你应该使用类名作为文件名保存,文件名的后缀为 .java。
  • 主方法入口:所有的 Java 程序由 public static void main(String []args) 方法开始执行。
public class Dog
{
  // 成员变量
  int age;
  String color;
  // 方法
  void hungry() { String time = "2020-04-11"; } // 局部变量
}

构造器

public class Dog
{
    public Dog() { }
    public Dog(String name) { }
}
// 初始化
Dog dog = new Dog()

静态 static

  • 静态字段:private static int A
  • 静态常量:public static final double A
  • 静态方法:math.pow(x,a) 可以通过class类名直接调用方法

方法参数

  • 按值引用:基本数据类型

  • 按引用调用:对象引用

    方法不能修改基本数据类型参数
    方法可以改变对象参数的状态
    方法不能让一个对象参数引用一个新对象

注释

  • /** ... ... */
  • 类注释:
  • 方法注释:@param variable description @return description @throws class description
  • 字段注释:为公共字段(静态常量)建立
  • 通用:@author name @version text

java包

包主要用来对类和接口进行分类

package com.xx.xxx

根据包路径和类名,引用指定的类

import java.io.*;

继承

  • 基类: extends ...
  • 超类: super - super.fun() 调用超类方法
        super(...,...,...) 调用超类具有相同参数类型的构造器
  • 多态: 子类-(引用赋值)-超类-(不能赋值)-子类 (子类方法的可见性不能低于超类方法:子类public-超类private)
  • 阻止继承 final: public final class A extends B (阻止派生A类的子类) ; 修饰方法时代表禁止重写-子类不能覆盖
  • 抽象类/方法: public abstract class A 包含抽象方法的类须被声明为抽象,抽象类不能实例化
//抽象类示例,统一调用超类来实现子类方法
Person people = new Person[2];
people[0] = new Employee(...);
people[1] = new Student(...);
for(Person p : people) System.out.println(p.getDescription());
  • Obejct类: 泛型容器-引用任何类型的对象(需要强制类型转换)
          equals:java.util.Arrays --- equals(x[] a,x[] b),java.util.Objects --- equals(Object a,Object b)
          hashCode:由对象导出的散列码值(重新定义equals方法需要重新定义hashCode方法)
          toString:表示对象值的字符串 --- x.toString() or +x
  • 泛型列表ArrayList:ArrayList<A> a = new ArrayList<>();
             访问:a.add(i,element),a.set(i,element),a.get(i),a.remove(i)
  • 变参: 可变参数数量方法 --- public double max(double... values) values会成为一个double数组

反射

  • Class类: 类信息(.java文件在jvm中转换为的.Class信息)
    Class c = e.getClass() , 可获得类的属性如类名.getName();
    通过类名得到Class对象Class.forName("...")或直接T.Class
  • 类的结构:Field:类的字段(getDeclaredFields),Method:类的方法(getDeclaredMethods),Constructor:类的构造器(getDeclaredConstructors)
  • 反射机制:分析对象
//利用反射访问对象
Employee h = new Employee(...);
//获得h对象类的Class信息
Class c = h.getClass();
//获得类中的name字段
Field f = c.getDeclaredField("name");
//obj为任何包含f字段的类的对象(例如h),获取或设置obj对象的当前字段值
Object v = f.get(obj);
Object v = f.set(obj,value)
//利用反射创建对象
String className = "...";
Object obj = Class.forName(className).getConstructor().newInstance();

反射的作用:
1.注解功能需要反射来实现
2.实现类的动态加载,动态加载需要的对象
3.动态创建/实例化对象,避免重新编译

  • 函数调用: Method类 --- Object invoke(Object obj,Object... args)
    Method m = Employee.class.getMethod("raiseSalary",double.class)
    String n = (String) m.invoke(h) h为实例对象,如果调用的函数返回类型为基本类型,则invoke会返回对应的包装器类型
    推荐使用接口或Lambda表达式代替此方式实现

接口/Lambda/内部类

接口

1.接口为默认public,但实现接口时必须将方法声明为public
2.接口中不能包含实例字段/方法,但可以包含常量(public static final)
3.接口中允许实现静态方法(public static)
4.除抽象类外,接口中的方法必须要全部实现
5.接口和超类冲突时,实行类优先原则

  • 使用接口: 编译器能检查方法是否确实存在/多继承
public interface B<T>
{
    Object C(T t);
}
//实现接口
class A implements B,D,...
  • 默认方法:default
public interface B<T>
{
    default object C(T t) {... ...}
}
  • 回调:制定某个特定事件发生时应采取的动作
//java.awt.event
public interface ActionListener
{
    void actionPerformed(ActionEvent event);
}
//ActionListener实现
class TimePrinter implements ActionListener
{
    public void actionPerformed(ActionEvent event)
    {
        System.out.println(event.getwhen());
    }
}
//timer生成event事件 -> 调用listener的actionPerformed -> 回调timer.event的getwhen()
TimePrinter listener = new TimePrinter();
Timer timer = new Timer(1000,listener);
  • 对象克隆: Object类中的clone()方法只能实现类的浅拷贝,深拷贝需要Cloneable接口实现

Lambda

Lambda表达式:函数式编程 --- 将代码块传入对象

  • 特点:延迟执行
  • 语法:
//参数式lambda
(String a,String b) ->
{
    if (a.length() < b.length()) return -1;
    else return 0;
}
//无参lambda
() -> { for(int i=10;i>=0;i--) System.out.println(i); }

方法只有一个参数且参数类型可以推导出,可以省略小括号;无须指定lambda返回类型

  • 函数式接口:对只有一个抽象方法的接口,需要接口的实例对象时,可采用Lambda表达式代替。
//Supplier<T>函数接口
public interface Supplier<T>
{
    T get();
}
//利用无参函数接口实现必要时才构建类
//day一般不为null,只在day为null时去构造LocalDate
LocalDate hireDay = Objects.requireNonNullOrElseGet(day,() -> new LocalDate(1970,1,1));

//java.util.function Predicate<T>函数接口--- 专用于传递lambda表达式
public interface Predicate<T>
{
    boolean test(T t);
}
list.removeIf(e -> e == Null); //从列表中删除所有Null值

方法引用:生成一个函数式接口的实例

//出现Timer事件时打印该事件
Time timer = new Timer(1000,System.out::println); 
/*等价于*/ Time timer = new Timer(1000,event -> System.out.println(event));

//从一个列表删除所有Null值
list.removeIf(Objects::isNull); 
/*等价于*/ list.removeIf(e -> e == Null);

构造器引用:

Person::new /*等价于*/ x -> new Person(x)
Integer[]::new /*等价于*/ n -> new Integer[n]

变量作用域:Lambda表达式内部的外部变量必须是“事实最终变量”即变量初始化后不会再赋新值
      Lambda表达式中的变量不能与局部变量同名

  • Comparator方法:
Array.sort(people,Comparator.comparing(Person::getName)); //按名字排序
Array.sort(people,Comparator.comparing(Person::getLastName).thenComparing(Person::getfirstName));
Array.sort(people,Comparator.comparingInt(p -> p.getName().length())); //按名字长度排序
Array.sort(people,Comparator.comparing(Person::getMiddleName,nullsFirst(naturalOrder().reversed()))); //按MiddleName进行逆排序(支持null的情况)

内部类

  • 内部类:
    • 内部类可以对同一个包中的其它类隐藏
    • 内部类方法可以访问定义这个类的作用域中的数据,包括原本私有的数据
    • 隐式引用:"OuterClass".this
  • 局部内部类:
    • 局部方法中定义类
    • 声明局部类不能有访问说明符
    • 对外界完全隐藏,无法访问
  • 匿名内部类:
    • 局部内部类但无需指定类名(指创建一个类对象,并实现了对应的接口或者继承于某个超类)
    • var a = new SuperType(param) { }
    • var a = new InterfaceType() { }
  • 静态内部类:static修饰的内部类 --- 没有生成对应外部类对象的引用

代理

概念:运行时创建实现了一组给定接口的新类。

用途:

  • 为了调试,跟踪方法调用
  • 将方法调用路由到远程服务器
  • 在运行的程序中将用户界面事件与动作关联起来

用法:

  • Proxy.newProxyInstance(类加载器ClassLoader,Class对象数组[元素为实现的各个接口],调用处理器InovactionHandler)

错误处理

异常

  • 异常分类:
    • Throwable: 异常类的超类
    • Error:系统错误和资源耗尽错误
    • Exception:RuntimeException - 编程错误的异常 ; IOException - I/O错误异常
    • 非检查型异常:Error/RuntimeException派生的异常,其它异常为检查型异常
  • 抛出检查型异常:当调用了抛出检查型异常的方法或检测到错误并用throw抛出检查型异常时(不应声明RuntimeException继承的非检查型异常)
  • 继承异常:
    • 子类方法可以抛出更特定的异常,或者根本不抛出任何异常
    • 如超类方法未抛出任何检查型异常,子类也不能抛出任何检查型异常。
    • 方法抛出的异常可能属于这个类或者其某个子类
  • 抛出异常: throw new EOFException();
  • 异常类:自定义异常类(继承于某个异常类) --- 应有默认构造器和包含详细信息的构造器
  • 捕获异常:try { } catch(ExceptionType e) { }
  • 异常消息:
    • e.getMessage() --- 错误信息
    • e.getClass().getName() --- 异常对象类型
    • e.printStackTrace() --- 堆栈轨迹
  • finally
//外层try负责处理错误,内层try负责确保关闭输入流
try
{
  try{ code might throw exceptions }
  finally { in.close(); }
}
catch(IOException e) { show error message }
  • try-with-Resources
//对实现了AutoCloseable接口的资源类
try(Resource res = ......) { ... ... } //可以指定多个资源
如 Scanner in = new Scanner { new InputStream(... ...) }
//try块退出后,会自动调用res.close()
  • 堆栈轨迹: 程序执行点上所有挂起方法调用的列表
//Throwable类的printStackTrace
Throwable t = new Throwable(); StringWriter out = new StringWriter();
t.printStackTrace(new PrintWriter(out)); String description = out.toString();
//栈帧实例流StackFrame
StackWalker walker = StackWalker.getInstance();
//walker.forEach(frame -> analyze frame) 或懒方式 walker.walk(stream -> process stream)
walker.forEach(System.out::println);

断言

  • 关键字:
    • assert condition; condition为false时,抛出AssertionError异常
    • assert condition : expression; condition为false时,expression传入AssertionError构造器并转换为消息字符串
  • 启用/禁用:
    • java -enableassertions MyApp
    • java -ea:Myclass -ea:com.mylib MyApp 对某个类/包启用
    • java -disableassertions MyApp
    • java -da:Myclass MyApp
  • 使用断言:
    • 致命的/不可恢复的错误(如方法的参数检查)
    • 开发/测试阶段(自我检查)

日志

  • 基本日志:
    • 全局日志记录器: Logger.getGlobal().info("... ... ...");
    • 取消日志(一般放在main最前面):Logger.getGlobal().setLevel(Level.OFF);
  • 高级日志:
    • 创建/获取日志记录器: private static final Logger myLogger = Logger.getLogger("com.myapp");
    • 日志级别:SEVERE,WARNING,INFO,CONFIG,FINE,FINER,FINEST(默认只记录前3个级别)
      • 设置级别:logger.setlevel(Level.FINE); // Level.ALL
      • 级别日志记录: logger.warning(message) // logger.fine(message)
      • 指定级别记录: logger.log(Level.FINE,message);
    • 异常记录:
      • void throwing(String className,String methodName,Throwable t)
      • void log(Level l,String message,Throwable t)
    • 日志管理器配置:
      • 修改配置文件: jre/lib/logging.properties
      • 修改: .level=INFO // 添加com.myapp.level=FINE来指定自定义级别
      • 控制台接收: 添加java.util.logging.ConsoleHandler.level=FINE
      • 日志处理器: java.util.logging.ConsoleHandler.level = INFO
      //安装自定义处理器
      Logger logger = Logger.getLogger("com.myApp");
      logger.setlevel(level.FINE);
      logger.setUseParentHandlers(false);
      ConsoleHandler handler = new ConsoleHandler();
      handler.setlevel(level.FINE);
      logger.addHandler(handler);
      
      SocketHandler:将记录发送到指定主机和端口
      FileHandler:将记录收集到文件中
      • 日志过滤器: 实现Filter接口并定义boolean isLoggable(LogRecord record)方法
      • 格式化器: 自定义格式 --- String format(LogRecord record)(或formatMessage)。调用setFormatter安装到处理器

泛型

泛型类

  • 定义:public class A<T,U> { private T a; }
  • 实例化:A<String,String>
  • 泛型方法:可以在普通类中定义,也可在泛型类中定义
    //声明
    Class A
    {
      public <T> T get(T t) {}
    }
    //调用;<>内的实际可以省略
    String m = A.<String>get("M");
    
    • 类型限定: public <T extends A> T get(T t) {}
            限制T只能为实现了接口A或者继承于A的类型
            多限定:T extends A & B

泛型和虚拟机

  • 类型擦除: 在虚拟机中,会提供去掉类型参数的原始类型(替换为第一个限定类型或Object)
  • 转换泛型:
    • 调用泛型方法/字段时,编译器在强制类型转换前插入
    • 桥方法:解决类型擦除和多态的冲突(使用泛型,而子类覆盖超类方法时,由于类型擦除,会产生两个不同的方法(一个由虚拟机通过类型擦除产生,一个为超类方法)
    //桥方法 --- {}内部的为子类方法
    public void set(Object second) { setSecond((LocalDate) second); }
    
  • 限制/局限性:
    • 不能用基本类型实例化类型参数(有A< Double >而没有A< double >)
    • 运行时类型查询只适用于原始类型(无法查询对象是否为某个泛型,而getClass方法会返回原始类型)
    • 不能创建参数化类型数组(```new Pair[10];)
    • Varargs警告(允许通过可变参数方法传递泛型数组)
    • 不能实例化类型变量(不能使用类似new T()的形式来使用类型变量)
    • 不能构造泛型数组(实例化数组)
    • 不能在静态字段或方法中引用泛型类型变量
    • 不能抛出或捕获泛型类的实例(泛型类扩展Thorwable非法)
    • 可以取消对检查型异常的检查(Task.<RuntimeException>throwsAs(e);多线程中有用:Runnable不允许抛出检查型异常)
    • 注意擦除后的冲突(多态冲突-类型擦除产生的方法与超类方法冲突//不能使用泛型对同一接口不同参数化)
  • 泛型继承:
    • 无论S和T之间有什么关系,A< S >和A< T >之间无任何关系
    • 可以将参数化泛型类型转化为原始类型

通配符类型

  • 通配符: 解决泛型类型的严格限定 --- S是T的子类,则A< S >是A<? extends T>的子类
    • 编译器无法将通配符类型作为参数传递,但可以return
  • 超类型限定: ? super A 限定为A的所有超类型
    • 可以作为参数传递,但不能return
  • 无限定通配符: A<?> ---> ? get() get的返回值只能赋给Object,?作参数的方法无法调用
//无法避免使用通配符的例子
public static <T> void swapHelper(Pair<T> p)
{
  ... //实现Pair内的变量a和变量b的交换
}
//Manager类是Employee类的子类
public static void maxmin(Manager[] a, Pair<? super Manager> result)
{
  minmax(a,result);
  swapHelper(result);
}    

反射和泛型

  • 泛型和Class类: Class类是泛型的(如String.class实际为Class类的对象)
  • 类型匹配:
public static <T> Pair<T> make(Class<T> c) throws ... ...
{ return new Pair<>(c.newInstance(),c.newInstance) }
//调用(通过调用Employee.class自动匹配<Employee>类型) - 无需调用时指定<Employee>类型
make(Employee.class)
  • 泛型与虚拟机: 可以通过反射获取虚拟机中保留的泛型信息 java.lang.reflect --> Type接口 - 接口包含以下子类型
    • Class类,描述具体类型
    • TypeVariable接口,描述类型变量
    • WildcardType接口,描述通配符
    • ParameterizedType接口,描述泛型类或接口类型
    • GenericArrayType接口,描述泛型数组
  • 类型字面量: 获取泛型类的泛型参数 Type type = new TypeLiteral<ArrayList<Integer>>() {};

集合

集合接口

Collection

  • Collection接口:
//集合类的基本接口 -> 基本方法
public interface Collection<E>
{
  boolean add(E element);
  Iterator<E> iterator();
  ... ...
} 
  • 迭代器: public interface Iterator<E>
    • E next(); 逐个访问集合元素(可使用foreach代替,编译器会自动转换为迭代器循环)
    • boolean hasNext(); 迭代器对象是否还有可访问元素
    //Lambda表达式处理
    iterator.forEachRemaining(element -> ... ... ... ...);
    
    • void remove(); 删除上次调用next方法返回的元素
    • default void forEachRemaining(Consumer<? super E> action);
    • Iterator<E> iterator() 返回一个迭代器
  • 集合框架接口:
    • List - Collection子接口:元素可以添加到特定位置;可以访问特定位置
    • ListIterator - Iterator子接口:在迭代器位置前面增加一个元素
    • Set - Collection子接口:更严格的Collection方法(不允许增加重复元素等)
    • SortedSet/SortedMap - 提供用于排序的比较器对象
    • 数组适合于随机访问,链表适合迭代器访问

Collection集合

链表

  • LinkedList类(List接口): 双向链接,包含前驱以及后驱的引用
    • LinkedList.add(...)方法将对象添加到链表尾部
    • ListIterator提供了add方法,用于在中间添加数据
      • interface ListIterator<E> extends Iterator<E>
      • void add(E element)
      • E previous() 反向遍历
      • boolean hasPrevious()
      • ListIterator<E> lisIterator() 返回一个listIterator迭代器,也可在()中指定索引位置
      • set(......) 用一个新元素替换next/previous方法返回的上一个元素
      • get(...) 访问特定元素(效率低)
  • 可以同时进行读写的迭代器只能有一个(并发下链表只跟踪结构性修改,允许多个迭代器修改数据)

数组列表

  • ArrayList类(List接口):动态再分配的对象数组(非多线程同步)
    • 多线程同步可以使用Vector类

散列集

  • hash table:散列表由链表数组实现,每个列表称为桶。由计算的散列码再与桶总数取余得到桶的索引
    • 桶:具有相同散列值元素的集合
  • HashSet类:散列集
    • 散列集:没有重复元素的元素集合
    • 迭代器:依次访问所有的桶

树集

  • TreeSet类:有序集合,对集合遍历时,值将按排序后顺序呈现(红黑树数据结构)
    • 使用树集需要元素实现了Comparable接口或构造时提供Comparator

队列

  • 队列:
    • Queue接口:java.util.Queue -- 尾进头出
    • Deque接口:java.util.Deque -- 双端队列(ArrayDeque和LinkedList类已实现)
    • ArrayDeque类:ArrayDeque(int initialCapacity) - 构造无限定双端队列
  • 优先队列:任意顺序插入,有序(排序)检索
    • PriorityQueue - PriorityQueue(int initialCapacity)
    • 结构:堆(heap)- 自组织二叉树

Map集合

  • 映射:键/值对
  • 结构: java.util.Map<K,V>

基本映射

  • HashMap:散列映射 - 对键进行散列
//example
HashMap staff = new HashMap<String,Employee>();
Employee harry = new Employee("Harry Hacker");
staff.put("987-98-9996",harry);
//检索
staff.get("987-98-9996");
//默认值
Map<String,Integer> scores = ......
int score = scores.getOrDefault(id,0);
//迭代
scores.forEach((k,v) -> System.out.println("key=" + k +", value=" + v));
  • TreeMap:树映射 - 根据键的顺序将元素组织为搜索树
  • 映射视图: 实现了Collection接口或子接口的对象
    • Set keySet() --- 键集
    • Collection values() --- 值集
    • Set<Map.Entry<K,V>> entrySet() --- 键/值对集合
    Set<String> keys = map.keySet();
    for(String key:keys) { ... ... }
    for(Map.Entry<String,Employee>entry:staff.entrySet()) {
      String k = entry.getKey();
      Employee v = entry.getValue();
      ... ... }
    

其它映射

  • 弱散列映射: WeakHashMap类 --- 若键的引用消失,WeakHashMap会将此键/值对回收
  • 链接散列集与映射:
    • LinkedHashSet --- 链接散列集
    • LinkedHashMap --- 链接散列映射(保存插入元素项的顺序信息,可使用iterator()迭代)
  • 枚举集与映射:
    • EnumSet:操作Enum的集合
    • EnumMap:键类型为枚举类型的映射
  • 标识散列映射:
    • IdentityHashMap: 键的散列值由System.IdentityHashMap计算(根据内存地址计算散列码)
    • 使用"=="比较,不同键的对象即使内容相同,也被视为不同对象
  • 子范围: .sublist(... , ...)
  • 同步视图: Collections.synchronizedMap(...); --- 将映射转换为有同步访问方法的Map
  • 检查型视图: Collections.checkedList(...,String.class); ---检查插入的对象是否属于给定类

泛型算法

  • 排序与混排:
    • Collections.sort(...) --- 对实现了List接口的集合排序
    • s.sort(Comparator.comparingDouble(Employee::getSalary));
    • s.sort().reversed()
    • Collections.shuffle(...) --- 混排
  • 二分查找:Collections.binarySearch(c,element)
  • 批操作: removeAll() , retainAll()
  • 集合与数组转换:
    • 数组->集合:String[] values = ...; HashSet s = new HashSet<>(List.of(values));
    • 集合->数组:String[] values = s.toArray(new String[0]);

并发

线程

  • 多线程:多线程的不同线程之间共享数据,与多进程相对
    • Runable:将线程代码放入一个类的run方法,这个类要实现Runnable接口
    //Runnable
    public interface Runnable
    {
      void run();
    }
    //实例化
    Runnable r = () -> { task code };
    //构造Thread对象
    Thread t = new Thread(r);
    //启动线程
    t.start();
    
  • 线程状态:
    • 新建New:new Thread(r)
    • 可运行线程Runnable: 调用start方法便处于可运行状态
      • static Thread currentThread() 返回正在执行的线程Thread对象
    • 阻塞Blocked/等待Waiting:非活动线程
      • 获取对象锁但被其它线程占有 - 阻塞
      • 线程等待另一个线程通知调度器出现一个条件时
      • 计时等待Timed waiting
      • t.join() --- 执行线程t,等待线程t结束后再继续进行
    • 终止Terminated:
      • run方法正常退出
      • 因没有捕获的异常而终止run方法
  • 线程属性:
    • 中断线程:
      • 没有方法可以强制线程中止,通过interrupt方法可以请求终止线程
      • 线程是否设置了中断状态:Thread.currentThread().isInterrupted()
      • 在sleep或wait阻塞调用的线程上调用interrupt会产生InterriptedException异常中断
      • interrupted - 检查当前线程是否被中断,并清除中断状态
      • isinterrupted - 检查是否线程被中
    • 守护线程:调用t.setDaemon(true)转换为守护线程(为其它线程提供服务的线程)
    • 线程名:t.setName("... ...")设置线程名
    • 未捕获异常的处理器:非检查型异常会被传递到处理未捕获异常处理器
      • 处理器为实现了Thread.UncaughtExceptionHandler的类 --- 实现方法void uncaughtException(Thread t,Throwable e)

同步

  • 锁对象:防止并发访问代码块
    • ReentrantLock类:重入锁
    //只有一个线程能进入 --- 不能使用try-with-resources语句!
    myLock.lock(); //a ReentrantLock object
    try { 临界区 critical section }
    finally { myLock.unlock(); } //确保抛出异常后能够解锁
    
    • 条件对象:管理获得锁却不能有用工作的线程(临界区内存在满足条件后才能执行的代码--如需要其它线程执行才能满足条件的情况)
      • private Condition s
      • 获得条件对象s = myLock.newCondition()
      • 条件不满足,调用s.await()线程暂停并放弃锁,允许另一个线程进行
      • 另一线程在完成活动时,调用s.signalAll()重新激活等待线程
      //通常,await应如下使用
      while(!(OK to proceed))
        condition.await();
      
    • synchronized关键字: 内部对象锁
      • wait方法:将线程增加到等待集
      • notifyAll:解除等待线程的阻塞
      • 静态方法锁定:锁定.class对象
    • 同步块: 获得java对象的内部锁
    //获得obj的锁
    synchronized(obj)
    {
      critical section
    }
    
    • volatile字段: 字段同步访问的免锁机制
      • 确保对字段进行修改时对所有读取变量的线程都可见
    • final变量: 字段声明为final时,保证其它线程看到的是字段被更新后的值
    • 原子性: 共享变量只进行赋值操作,可以声明为volatile
      • java.util.concurrent.atomic包 --- 原子操作类
      • 大量线程访问原子值:LongAdder类、LongAccumulator类
    • 死锁:
      • 所有线程都不满足条件而wait()
      • 调用signal方法(只解除一个线程阻塞,而这个线程不能继续运行)
    • 线程局部变量: ThreadLocal类
    //为每个线程构造一个实例
    public static final ThreadLocal<SimpleDateFormat> dateFormat
      = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
    //访问实例
    //线程首次调用get时,会调用构造器中的lambda表达式,get方法返回属于该线程的实例
    String dateStamp = dateFormat.get().format(new Date());
    //为线程设置新值
    void set(T t)
    
    //返回属于当前线程的Random类实例
    int random = ThreadLocalRandom.current().nextInt(upperBound);
    

线程安全集合

  • 阻塞队列: 一个线程将指令对象插入队列,另一个线程取出指令对象。当队列已满或空时,线程阻塞
    • offer(x) / offer(x,100,TimeUnit.MILLISECONDS) --- 添加元素
    • poll(x) / poll(100,TimeUnit.MILLISECONDS) --- 删除元素
    • element
  • 高效映射、集和队列:
    • 集合:不会抛出ConcurrentModificationException(集合在迭代器构造后发生改变),不会将同一个值返回两次
    • 映射:高效支持大量阅读器和一定数量书写器(16)
    • ConcurrentHashMap
    • ConcurrentSkipListMap
    • ConcurrentSkipListSet
    • ConcurrentLinkedQueue
  • 映射原子更新:
    • map.compute(word,(k,v) -> v == null ? 1 : v+1); 整数计数器映射
    • computeIfPresent --- 只在有原值的情况下计算新值
    • computeIfAbsent --- 只在没有原值的情况下计算新值
    • map.merge(world,1L,(existingValue,newValue) -> existingValue + newValue); 首次增加一个键
  • 并发散列映射批操作:
    • search(搜索)
    • reduce(归约)组合所有键或值
    • forEach
    • 每个操作都有4个版本:operationKeys,operationValues,operation(处理键/值),operationEntries(处理Map.Entry对象)
    //找出第一个出现次数超过1000次的单词
    String result = map.search(threshold,(k,v) -> v > 1000 ? k : null);
    //ForEach
    map.forEach(threshold,(k,v) -> System.out.println(k + " -> " + v));
    //第二个参数为转换器,只要返回null,就会跳过这个值
    map.forEach(threshold,(k,v) -> v > 1000 ? k + "->" + v : null,System.out::println);
    //计算所有值总和
    Long sum = map.reduceValues(threshold,Long::sum);
    
  • 数组拷贝:
    • CopyOnWriteArrayList
    • CopyOnWriteArraySet
  • 并行数组算法:
    • Arrays.parallelSort(words); --- 排序
    • Arrays.parallelSort(words,Comparator.comparing(String::length));
    • Arrays.parallelSetAll(values,i -> i % 10); --- 用函数计算得到的值填充一个数组,i为位置索引

线程池

  • 使用线程池: 构造新线程开销巨大。如果程序中创建了大量短生命周期的线程,则应使用线程池(run方法退出时,线程不会死亡,准备为下一个请求提供服务)
  • Callable与Future:
    • Callable 返回异步计算的对象
    //返回V对象的异步计算
    public interface Callable<V>
    {
      V call() throws Exception;
    }
    
    • Future 保存异步计算的结果
      • V get() 调用阻塞直到计算完成
    Callable<Integer> task = ...;
    FutureTask futureTask = new FutureTask<Integer>(task);
    Thread t = new Thread(futureTask);
    t.start();
    ...
    Integer result = task.get();
    
  • 执行器Executors类:
    • newCachedThreadPool --- 构造一个线程池
    • newFixedThreadPool --- 构造一个有固定大小的线程池
    • newWorkStealingPool --- 构造一个适合“fork-join”任务的线程池
    • newSingleThreadExcutor --- 一个退化了的大小为1的线程池
    • newScheduledThreadPool --- 构造固定线程池(用于并发线程数等于处理器内核数的情况)
    • newSingleThreadScheduledExecutor --- 构造单线程池
    • 将Runnable或Callable对象提交给ExecutorService
    Future<T> submit(Callable<T> task)
    Future<?> submit(Runnable task)
    Future<T> submit(Runnable task,T result) //get方法在完成时返回result对象
    //返回的Future对象可用于得到结果或取消任务
    //线程池服务
    ExecutorService executor = Executors.newCachedThreadPool();
    //也可以使用execute方法代替submit
    executor.execute(task) --- 将Runnable提交给线程池
    
    • void shutdown() 启动线程池的关闭序列
  • 控制任务组:
    • invokeAny --- 提交一个Callable对象集合中的所有对象,返回最快完成的任务结果
    • invokeAll --- 提交一个Callable对象集合中的所有对象,但会阻塞,返回Future对象列表

异步

  • 可完成Future:
    • CompletableFuture类:实现了Future接口,注册一个回调,当结果可用,便利用该结果调用这个回调
    • CompletableFuture<String> f = ...;
    • f.thenAccept(s -> Process the result string s);
    • f.whenComplete((s,t) -> { if(t==null) { Process the result s; } else {Process the Throwable t; } });

进程

  • 建立进程: Process builder = new ProcessBuilder("gcc","myapp.c");
    • 指定想执行的命令,可以是List< String >或者命令字符串
    • 默认下,进程的工作目录与虚拟机相同
    • builder = builder.directory(path.toFile()) 改变工作目录
    • builder.redirectIO() 进程输出显示在控制台上,或也可指定输入、输出和错误流
    • builder.redirectErrorStream(true) 合并输出和错误流
  • 运行进程:
    • builder.start() 运行
    • int result = builder.waitFor() 等待进程完成
    • builder.waitfor(delay,TimeUnit.SECONDS) 等待完成或超时; builder.exitValue()获取退出值
    • builder.destoryForcibly()杀死进程
  • 进程句柄:
    • ProcessHandle接口(进程句柄)
      • 给定Process对象builder,builder.toHandle()生成它的ProcessHandle
      • 给定long类型的系统进程ID,ProcessHandle.of(id)生成进程句柄
      • Process.current()得到运行Java虚拟机的进程句柄
      • ProcessHandle.allProcess()生成对当前进程可见的所有系统进程的Stream
    • 给定进程句柄handle
      • 进程ID:long pid = handle.pid()
      • 父进程:Optional< ProcessHandle > parent = handle.parent();
      • 子进程:Stream< ProcessHandle > children = handle.children();
      • 后代进程:Stream< ProcessHandle > descendants = handle.descendants();
posted on 2020-08-13 09:39  Lith  阅读(77)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3