目录
基础语法
- 字符 ~ 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 MyAppjava -ea:Myclass -ea:com.mylib MyApp对某个类/包启用java -disableassertions MyAppjava -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
SocketHandler:将记录发送到指定主机和端口//安装自定义处理器 Logger logger = Logger.getLogger("com.myApp"); logger.setlevel(level.FINE); logger.setUseParentHandlers(false); ConsoleHandler handler = new ConsoleHandler(); handler.setlevel(level.FINE); logger.addHandler(handler);
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接口:
//集合类的基本接口 -> 基本方法
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) - 构造无限定双端队列
- Queue接口:java.util.Queue
- 优先队列:任意顺序插入,有序(排序)检索
- PriorityQueue
- PriorityQueue (int initialCapacity) - 结构:堆(heap)- 自组织二叉树
- PriorityQueue
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(); ... ... } - Set
其它映射
- 弱散列映射: 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方法
- 新建New:
- 线程属性:
- 中断线程:
- 没有方法可以强制线程中止,通过interrupt方法可以请求终止线程
- 线程是否设置了中断状态:
Thread.currentThread().isInterrupted() - 在sleep或wait阻塞调用的线程上调用interrupt会产生InterriptedException异常中断
- interrupted - 检查当前线程是否被中断,并清除中断状态
- isinterrupted - 检查是否线程被中
- 守护线程:调用
t.setDaemon(true)转换为守护线程(为其它线程提供服务的线程) - 线程名:
t.setName("... ...")设置线程名 - 未捕获异常的处理器:非检查型异常会被传递到处理未捕获异常处理器
- 处理器为实现了Thread.UncaughtExceptionHandler的类 --- 实现方法
void uncaughtException(Thread t,Throwable e)
- 处理器为实现了Thread.UncaughtExceptionHandler的类 --- 实现方法
- 中断线程:
同步
- 锁对象:防止并发访问代码块
- 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();
- ProcessHandle接口(进程句柄)
浙公网安备 33010602011771号