读书笔记——《JAVA核心技术》第一卷

第三章 《Java的基本程序设计结构》

1.一个简单的Java程序

  • Java区分大小写

2.注释

  • 使用//或者/* /以及自动生成方式(/* */)标注注释

3.数据类型

  • 8种基本数据类型,4种整型,2种浮点型,1种用于表示Unicode的字符型char和一种表示真值的boolean
  • 整型用于没有小数的部分,包括int(4字节)、short(2字节)、long(8字节)和byte(1字节)
  • 浮点类型用于有小数部分的数值,float(4字节,前1个字节为指数位,后3个字节为有效位,)、double(8字节,前1个字节为指数位,后7个字节为有效位)
  • char类型的字面值需要用单引号括起来,java内码中一般占用2个字节,但是由于字符越来越多,超过了16位65536的限制,UTF-8用一个字符组代表一个字符内容
  • boolean类型有两个值:true和false,并且不能与整数值转换

4.变量

  • 变量名必须由字母开头并由字母或数字构成的序列,字母包括'A'-'Z','a'-'z','_','$'或者某种语言中宝石字母的任何Unicode字符,空格'+'不可以,大小写敏感,长度没有限制,此外,不能使用Java保留字
  • 常量可以通过关键词final赋值,赋值后无法更改,可以使用static final定义个一类常量,习惯上,常量名使用全大写

5.运算符

  • 当参与/除法时,当两个操作数都是整数时,表示整数除法,否则表示浮点除法
  • 整数的求余操作(取模),用%表示
  • Math类中包含了各种各样的数学函数
  • 运算过程中,有一个double,另一个也会转换为double类型,这样的规则依次是double,float,long,int
  • 强制类型转换,double转为int会强制截断、如9.9变为9
  • 逻辑运算中,&&和||是按照“短路”的方式来求值的,如果一个操作数已经能够确定表达式的值,第二个就不必计算了
  • Java支持三元操作符 condition?expression1:expression2
  • 位运算符:&位与、|位或、^异或、~否、<<左移、>>右移、>>>右移高位补0,不存在<<<,<<操作过程中要完成取模32操作(long类型为64)
  • &&运算优先级比||高,运算符按照从左到右的次序进行计算、除了右结合运算符外
  • 枚举类型变量的取值只在一个有限的集合

6.字符串

  • 字符串没有内置字符串类型,而是由一个预定义类String表示,每个用双引号括起来的字符串都是String的一个实例
  • substring方法可以从一个较大的字符串提取一个子串,如substring(0,2)就是提取0~1的字符串
  • 字符串可以通过“+”进行拼接
  • String类对象为不可变字符串
  • 可以通过equals方法来检测两个字符串是否相等,equalsIgnoreCase可以进行不区分大小写对比,千万不要使用==比较字符串相等,这是由虚拟机是否将相同的字符串共享,但只有字符串常量是共享的
  • 空串""是长度为0的字符串,但不是null
  • String的length方法返回的是UTF-16编码的代码单元数量,而实际的长度即码点数量可以调用codePointCount方法,而对于用一对代码单元表示的字符使用charAt方法会索引其代码单元的值,因此,尽量不要使用char类型
  • String方法有许多有用的API
  • 可以通过StringBuffer和StringBuilder来构建字符串,可以提高效率(但现在的JAVA已经优化了这个,区别不大了)

7.输入输出

  • 通过构造Scanner对象来读取输入流,通过next、nextLine、nextInt等方法依次读取数据
  • java可以使用c语言中printf方法对输出进行格式化处理
  • 文件的输入需要通过File对象构造一个Scanner对象
  • 控制流程属于基础知识,与其他语言类似
  • java.math包中两个有用的类BigInteger和BigDecimal可以表示大数值
  • java中数组是一种数据结构,可以通过for each遍历
  • 数组可以通过构造{}进行初始化赋值
  • 拷贝数组可以通过Arrays的copyof方法
  • 在Java应用程序的main方法中,程序名并没有存储在args数组中
  • Arrays.sort 方法可以对数组进行排序
  • java运行使用多维数组,如int[][]表示二维数组,并且子数组的长度不一定需要一致

第四章 《对象和类》

1.面向对象程序设计概述

  • 类是构造对象的模板或蓝图,由类构造对象的过程称为创建类的实例
  • 类具有封装性,对象中的数据称为实例域,操纵数据的过程称为方法
  • 对象三大特效:行为、状态和标识
  • 类之间的关系:依赖、聚合和继承等

2.使用预定义类

  • 要想使用对象必须使用构造器构造,并指定其初始状态
  • 使用new操作符构造对象
  • 对象变量并没有包含一个对象,而仅仅引用一个对象
  • java有两个时间类,一个表示时间点的Date类,另一个是日历表示法的LocalDate类,JAVA8引入了一些类来处理日期与时间不一致的方面
  • java中时间类有相应的更改器与访问器方法

3.用户自定义类

  • 一个完整的程序,若干类组合在一期,一般其中只有一个类有main方法
  • 构造器总是伴随着new操作符的执行被调用,而不能对一个已经存在的对象调用构造器来达到重新设置实例域的目的
  • 封装类应该提供三项内容:一个私有的数据域、一个公有的域访问器方法、一个公有的域更改器方法
  • final关键字来初始化值,可以保证这个值创建时必初始化,且在后续操作中不可再对它进行修改

4.静态域与静态方法

  • 使用static修饰符定义静态域与静态方法
  • 静态方法属于类而不是由类创建的单独对象
  • 使用static final 定义静态常量,并可以被public修饰
  • 在下面两种情况使用静态方法:一个方法不需要访问对象状态、其所需参数都是通过显式参数提供;另一个方法只需要访问类的静态域
  • 工厂方法使用静态方法创建实例,而不适用构造器创建,是由于:一是无法命名构造器、二是当使用构造器时,无法改变所构造的对象类型
  • main方法不对任何对象进行操作,静态的main方法将执行并创建程序所需要的对象

5.方法参数

  • 按值调用表示方法接收的是调用者提供的值
  • 按引用调用表示方法接收的是调用者提供的变量地址
  • 一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值

6.对象构造

  • 相同的名字、不同的参数所构成的构造方法就是重载
  • 如果构造器中没有显示地给域赋予初值,那么就会被自动地赋为默认值:数值为0,布尔值为false,对象引用为null。但这并不是一个好习惯
  • 如果在编写一个类时没有编写构造器,那么系统就会提供一个无参数的构造器
    但如果类中有至少一个构造器就不会提供无参构造器
  • 在执行构造器之前,先执行赋值操作
  • 可以使用关键字this引用方法的隐式参数
  • 两种初始化数据域的方法:在构造器中设置值,在声明中赋值
  • java有自动的垃圾回收器,不需要人工回收内存,所以java不支持析构器
  • 当某些对象使用了内存之外的其他资源,可以添加finalize方法在垃圾回收器清除对象之前调用

7.包

  • java允许使用包将类组织起来,并可以有效确保类名的唯一性
  • 使用import语句导入包
  • import语句不仅可以导入类,还增加了导入静态方法和静态域的功能
  • 编译器对java文件进行操作,Java解释器加载类
  • public部分可以被任意类使用,标记为private部分只能被定义它们的类使用
  • 从1.2版本开始,JDK修改了类加载器,明确禁止用户自定义的、包名以“java.”开始的类,用户自定义类可以通过包密封机制来保证安全性、

8.类路径

  • 类的路径必须与包名匹配,jar包可以包含多个压缩形式的类文件和子目录,既节约空间又改善性能
  • 从java6开始可以在jar文件目录中指定通配符

9.文档注释

  • jdk的工具javadoc可以将源文件生成一个html文档
  • 注释以/开始,/结束,而/* ... */文档注释在标记之后紧跟着自由格式文本
  • 类注释必须放在import语句之后,类定义之前
  • 每一个方法注释必须放在锁描述的方法之前,如@param变量描述、@return返回描述、@throws异常类描述
  • 只需要对公有域(通常指静态常量)建立文档
  • 部分通用注释:@anthor姓名、@version版本、@since始于,@deprecated过期、@see引用
  • 使用javadoc抽取注释

10.类设计技巧

  • 一定要保证数据私有
  • 一定要对数据初始化
  • 不要在类中使用过多的基本类型
  • 不是所有域都需要独立的域访问器和更改器
  • 将职责过多的类进行分解
  • 类名和方法名要能够体现他们的职责
  • 优先使用不可变的类

第五章 《继承》

1.类、超类和子类

  • 关键词extends表示继承
  • 被继承的类为超类或父类,继承其他类的类为子类
  • 子类可以重写父类的方法来重新设计,若需要调用父类同名方法,可以使用super关键词来调用
  • 子类创建构造方法可以通过super来调用父类的构造方法
  • 继承不仅限于一个层次,可以多层继承
  • 一个类最多只能继承一个类
  • 在java程序设计语言中,对象变量是多态的
  • 在方法调用过程中,编译器查看对象的声明类型和方法名,接下来,编译器将查看调用方法时提供的参数类型,如果是private方法、static方法、final方法或者构造器,编译器会准确之道应该调用哪个方法,这被称作静态绑定。而其他情况下,当程序运行时,并且采用动态绑定调用方法时,虚拟机一定调用与x所引用对象的实际类型最何时的那个类的方法。
  • 每次调用方法都进行搜索,开销太大,因此,虚拟机预先为每个类创建一个方法表
  • 可以使用final修饰符来阻止继承
  • 只能在继承层次内进行类型转换
  • 在将超类转换成子类之前,应该使用instanceof进行检查
  • 抽象类中的抽象方法充当着占位的角色,它们的具体实现在子类中。
  • protected对本包和所有子类可见

2.Object

  • Object是所有类的超类
  • Object中equals方法用于检测一个对象是否等于另一个对象
  • equals方法具有:自反性、对称性、传递性、一致性和对任意非空引用x,x.equals(null)返回false
  • hasCode()返回散列码,是由对象导出的一个整型值
  • 对象的toString()方法不重写的话,会返回地址值

3.泛型数据列表

  • ArrayList是一个采用类型参数的泛型类

4.对象包装器与自动装箱

  • 基本类型有对应的包装类,如int的包装类为Integer
  • 包装类可以支持基本类型的泛型和装箱拆箱需求

5.参数数量可变的方法

  • 使用...表示这个方法可以接受任意数量的对象

6.枚举类

  • 在比较两个枚举类型的值时,永远不要调用equals,而直接使用“==”

7.反射

  • 能够分析类能力的程序称为反射
  • Object类中的getClass()方法将会返回一个Class类型的实例
  • 可以通过getClass(),getName(),.class方法返回一个Class对象
  • 可以使用newInstance()动态创建实例
  • java.lang.reflect包中有三个类Field、Method和Constructor分别描述类的域,方法和构造器,这三个类都有一个叫做getName的方法,来返回项目的名称。
  • Field类有一个getType方法,用来返回描述域所属类型的Class对象。
  • Method和Constructor类有能够报告参数类型的方法,Method类还有一个可以报告返回类型的方法。
  • 这三个类还有一个叫做getModifiers的方法,它将返回一个整型数值,用不同的位开关描述public和static这样的getModifiers返回去的整型数值。
  • 可以利用Modifier类的静态方法分析getModifiers返回的整型数值

8.继承的设计技巧

  • 将公共操作和域放在超类
  • 不要使用受保护的域
  • 使用继承实现“is-a”关系
  • 除非所有继承的方法都有意义,否则不要使用继承
  • 在覆盖方法时,不要改变预期的行为
  • 使用多态,而非类型信息
  • 不要过多的使用反射

第六章 《接口、lambda表达式和内部类》

1.接口

  • 接口不是类,而是对类的一组需求描述
  • 接口中所有方法自动地属于public,java8中有静态方法,默认方法等,java9有私有方法
  • 让类实现一个接口,首先将类声明为实现给定的接口,并对接口中的所有方法进行定义
  • 接口不是类,无法实例化
  • 接口中的域将被自动设为public static final
  • 每个类只能继承一个超类,却可以实现多个接口
  • 接口可以允许静态方法
  • 可以为接口方法提供默认实现,使用default修饰,可以有效避免有些实现接口的类不需要实现所有接口中的方法
  • 如果接口中一个方法定义为默认方法,然后又在超类或另一个接口中定义了同样的方法,那么首先是超类优先,而如果是接口冲突时,实现类必须覆盖这个方法来解决冲突

2.接口示例

  • 回调可以指出某个特定事件发生时应该采取的动作
  • 通过定时器来实现回调
  • Comparator接口通过实现compareTo方法来实现排序
  • Cloneable提供一个安全的clone方法
  • clone方法是Object的一个protected方法
  • 如果一个对象在调用clone,但这个对象的类没有实现Cloneable接口,Object类的clone方法就会抛出一个CloneNotSupportedException

3.lambda表达式

  • lambda表达式是一种函数式编程
  • 对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个lambda表达式,这种接口称为函数式接口
  • lambda表示式可以使用::来实现方法引用,包括构造器方法的引用
  • 在lambda表达式中,只能引用值不会改变的变量
  • lambda表达式可以实现延迟执行

4.内部类

  • 内部类方法可以访问该类定义所在的作用域的数据,包括私有数据
  • 内部类可以对同一个包中的其他类隐藏起来
  • 当想要定义一个回调函数并且不想编写大量代码时,使用匿名内部类比较便捷
  • 外围类对象的引用称为outer
  • 在方法中定义的类为局部内部类,不可以用public或private修饰,作用域仅仅在这个类块中
  • 值创建这个类的一个对象,可以使用匿名内部类
  • 静态内部类除了没有对生成它的外围类对象的引用特权外,和其他所有内部类完全一样

5.代理

  • 使用代理可以在运行时创建一个实现了一组给定接口的新类
  • 使用调用处理器的invoke方法来实现方法的调用
  • 创建代理对象,需要使用Proxy类的newProxyIntance方法,有三个参数:一个类加载器,一个Class对象数组,一个调用处理器

第七章 《异常、断言和日志》

1.处理错误

  • 由于出现错误而使得某些操作没有完成,程序应该返回到一种安全状态,并能够让用户执行一些其他命令,或者允许用户保存所有操作的结果,并以妥善的方式终止程序
  • 常见的错误有:用户输入错误,设备错误,物理限制以及代码错误
  • 所有的异常退休都是派生于Throwable类的一个实例,有两个分支:Error和Exception,其中,Exception有两个分支:RuntimeException和其他异常
  • 派生于RuntimeException的异常包含:错误的类型转换、数组访问越界和访问null指针,而其他异常包括:试图在文件尾部后面读取数据、试图打开一个不存在的文件、试图根据给定的字符串查找Class对象,而这个字符串表示的类并不存在
  • 如果出现RuntimeException异常,那么一定是你的问题
  • 派生于Error类或RuntimeException类的所有异常称为unchecked异常
  • 方法应该在收不声明所有可能抛出的异常
  • 以下情况需要抛出异常:调用一个抛出受查异常的方法、程序运行过程中发现错误,并用throw语句抛出一个受查异常、程序出现错误、Java虚拟机和运行库出现的内部错误
  • 可以通过创建异常类来充分地描述清楚问题

2.捕获异常

  • 如果异常发生的时候没有在任何地方进行捕获,那程序就会终止执行,并在控制台上打印出异常信息,其中包括异常的类型和堆栈的内容

3.使用异常机制的技巧

  • 异常处理不能代替简单的测试
  • 不要过分的细化异常
  • 利用异常层次结构
  • 不要压制异常
  • 在检测错误时,“苛刻”要比放任更好
  • 不要羞于传递异常

4.使用断言

  • java引入关键字assert来设置断言 assert 条件:表达式;
  • 默认情况下,断言被禁用,可以在运行程序时用-enableassertions或-ea选项启用
  • 三种处理系统错误的方式:抛出异常、日志和使用断言

5.记录日志

  • 可以使用全局日志记录器并调用info方法来实现简单的日志记录
  • 有7个日志记录器级别:SEVER、WARNING、INFO、CONFIG、FINE、FINER、FINEST,默认情况只记录前三个级别
  • 可以使用Level.ALL开启所有级别的记录,或使用Level.OFF来关闭所有级别的记录
  • 本地化的应用程序包含资源包中的本地特定信息
  • 在默认情况下,日志记录器将记录发送到ConsoleHandler中,并由它输出到System.err流中,特别地是,日志记录器还会将日志发送到父处理器中,而最终的处理器(命名为“”)有一个ConsoleHandler
  • 在默认情况下,过滤器根据日志记录进行过滤
  • ConsoleHandler类和FileHandler类可以生成文本和XML格式的日志记录,也可以自定义格式
  • 为一个简单的应用程序,选择一个日志记录器,并把日志记录器命名为与主应用程序包一样的名字
  • 默认的日志配置将级别等于或高于INFO级别的所有消息记录到控制台

6.调试技巧

  • 可以使用sout或者Logger.getGlobal().info()打印任意值
  • 在每个类中放置一个单独的main方法
  • 使用Junit进行测试
  • 日志代理是一个子类的对象,它可以截获方法的调用,并进行日志记录,然后调用超类中的方法
  • 利用Throwable类提供的printStackTrace方法,可以从任何一个异常对象中获得堆栈情况
  • 一般来说,堆栈轨迹显示在System.err上
  • 通常,将一个程序中的错误信息保存在一个文件中是非常有用的
  • 让非捕获异常的堆栈轨迹出现在System.err中并不是一个很理想的方法
  • 要想观察类的加载过程,可以用-verbose标志启动Java虚拟机
  • -Xlint选项告诉编译器对一些普遍容易出现的代码问题进行检查
  • Java虚拟机增加了对Java应用程序进行监控和管理的支持
  • 可以使用jmap实用工具获得一个堆的转储,其中显示了堆中的每个对象
  • 如果使用-Xprof标志运行Java虚拟机,就会运行一个基本的剖析器来跟踪那些代码中经常被调用的方法

第八章 泛型程序设计

1.为什么要使用泛型程序设计

  • 泛型程序设计意味着编写的代码可以被很多不同类型的对象所重用

2.定义简单泛型类

  • 一个泛型类就是具有一个或多个类型变量的类

3.泛型方法

  • 泛型方法可以定义在普通类中,也可以定义在泛型类中

4.类型变量的限定

  • 有时,类或方法需要对类型变量加以约束

5.泛型代码和虚拟机

  • 无论何时定义一个泛型类型,都自动提供了一个相应的原始类型。原始类型的名字就是删去类型参数后的泛型类型名。擦除类型变量,并替换为限定类型(无限定的变量用Object)
  • 虚拟机中没有泛型,只有普通的类和方法
  • 所有的类型参数都用它们的限定类型替换
  • 桥方法被合成来保持多态
  • 为保持类型安全性,必要时插入强制类型转换

6.约束与局限性

  • 不能用基本类型实例化类型参数
  • 运行时类型查询只适用于原始类型
  • 不能创建参数化类型的数组
  • 使用@SuppressWarning("unchecked")或者@SafeVarargs来消除警告
  • 不能实例化类型变量
  • 不能构造泛型数组
  • 泛型类的静态上下文中类型变量无效
  • 不能抛出或捕获泛型类的实例
  • 可以消除对受查异常的检查
  • 注意擦除后的冲突

7.泛型类型的继承规则

  • 泛型之间没有继承关系

8.通配符类型

  • 通配符类型中,允许类型参数变化
  • 带有超类型限定的通配符可以向泛型对象写入,带有子类型限定的通配符可以从泛型对象读取
  • 还可以使用无限定的通配符,如Pair<?>
  • 通配符捕获只有在许多限制的情况下才是合法的,编译器必须能够确信通配符表达的是单个、确定的类型

9.反射和泛型

  • Class类是泛型的
  • 使用Class<T>参数进行类型匹配
  • 虚拟机中的泛型类型信息

第九章 《集合》(这部分内容较多,建议看原文)

1.JAVA集合框架

  • java集合类库将接口和实现分离
  • 集合类的基本接口是Collection接口,有两个基本方法add 和 iterator,以及其他方法
  • Collection接口扩展了iterable接口,因此,对于任何集合都可以使用for each循环,元素被访问的顺序取决于集合类型,如ArrayList是从0索引,每次加1,而HashSet是随机遍历
  • 由于Collection与Iterator都是泛型接口,可以编写操作任何集合类型的实用方法
  • 集合有两个基本接口Collection和Map

2.具体的集合

  • 链表是一个有序集合

3.映射

  • 两个通用的Map实现:HashMap和TreeMap

4.视图与包装器

5.算法(这部分参考算法与数据结构)

6.遗留的集合

第十章到十二章为java应用程序设计(跳过)

第十三章 部署java应用程序

1.jar文件

  • jar可以包含类文件,也可以包含诸如图像和声音这些其他类型的文件,此外,jar文件是zip压缩格式的
  • 使用jar命令创建jar文件
  • jar文件包含一个用于描述归档特征的清单文件,格式为manifest
  • 使用java命令运行jar文件

2.应用首选项的存储

  • 通过Properties类来实现属性映射,完成首选项的存储与部署

3.服务加载器

  • 可以用来加载插件

4.applet(与java应用程序有关,跳过)

  • applet是包含在html页面中的java程序

5.Java Web Start(具体过程见原文)

  • Java Web Start应用程序一般通过浏览器发布
  • Java Web Start应用程序并不在浏览器窗口内
  • Java Web Start应用程序不适用浏览器的Java实现
  • 数字签名应用程序可以被赋予访问本地机器的任意权限

第十四章 并发

1.什么是线程

  • Runnable是一个函数式接口,有run()方法,可以用lambda表达式建立一个实例
  • 不要调用Thread类或者Runnable对象的run方法,调用run方法只会执行同一个线程中的任务,而不会启动线程,应调用Thread.start方法创建一个执行run方法的新线程

2.中断线程

  • 当线程的run方法执行方法中最后一条语句后,并经由执行return语句返回时,或者出现了在方法中没有捕获的异常时,线程将终止
  • interrupt方法可以请求终止线程,当调用interrupt方法时,线程的中断状态将被置位,这是每一个线程都具有的boolean标志,每个线程都应该不时地检查这个标志,以判断线程是否被中断
  • Interrupted方法是静态方法,isInterrupted方法是实例方法,都可以检测线程是否被中断,前者会清除该线程的中断状态,后者不会改变

3.线程状态

  • 有6种线程状态:New、Runnable、Blocked、Waiting、Timed waiting、Terminated
  • 当使用new操作符创建线程时,线程处于New状态
  • 一旦调用start方法,线程处于runnable状态,但也有可能没在运行,这取决于操作系统给线程提供的运行时间
  • 当线程处于被阻塞或等待状态时,它暂时不活动,不运行任何代码且消耗最少的资源,直到线程调度器重新激活它
  • 线程被终止有两个原因,一是因为run方法正常退出而自然死亡,另一个没有捕获的异常终止了run方法而意外死亡

4.线程属性

  • 每个线程只有一个优先级,默认情况下,一个线程继承它的父线程的优先级,MIN_PRIORITY=1,MAX_PRIORITY=10,NORM_PRIORITY=5
  • 优先级高度依赖于系统,一般选择新线程,有较高的优先级
  • yield方法可以让当前执行线程处于让步状态,这是一个静态方法
  • 可以通过调用t.setDaemon(true)将线程转换为守护线程,守护线程的唯一用途就是为其他线程提供服务
  • 未捕获异常处理器必须属于一个实现Thread.UncaughtExceptionHandler接口的类,接口只有一个方法 uncaughtException();可以用setUncaughtExceptionHandler方法为任何线程安装一个处理器,也可以用Thread类的静态方法setDefaultUncaughtExceptionHandler为所有线程安装一个默认的处理器,替换处理器可以使用日志API发送未捕获异常的报告到日志文件
  • 如果不安装默认的处理器,此时的处理器就是该线程的ThreadGroup对象

5.同步

  • 为避免多线程引起对共享数据的错误,必须学习如何同步存取
  • 有两种机制防止代码受并发访问干扰,java语言提供一个synchronized关键字,可以自动提供一个锁以及相关的“条件”,java5又引入了ReentrantLock类。
  • 锁是可重入的,通过设置持有计数来跟踪对lock方法的嵌套调用,线程在每次调用lock方法都需要用unlock来释放锁
  • 一旦一个线程调用await方法,它进入该条件的等待集,当锁可用时,该线程不能马上接触阻塞,相反,它一直处于阻塞状态,直到另一个线程调用同一条件上的signalAll方法为止
  • 如果一个方法用synchronized关键字声明,那么对象的锁保护整个方法
  • 内部对象锁通过wait方法添加一个线程到等待集中,notifyAll/notify方法解除等待线程的阻塞状态
  • 内部锁和条件存在一些局限性:不能终端一个正在试图获得锁的线程,试图获得锁不能设定超时,每个锁仅有单一的条件
  • 最好既不使用Lock也不使用synchronized关键字,可以使用java.util.concurrent包中的锁机制
  • 使用监视器可以在不需要程序员考虑如何加锁的情况下,就可以保证多线程的安全性
  • 监视器只包含私有域的类,每个监视器类的对象有一个相关的锁,使用该锁对所有方法进行加锁,该锁有任意多个相关条件
  • volatile关键字为实例域的同步访问提供了一种免锁机制,如果声明一个域为volatile,那么编译器和虚拟机就之道该域是可能被另一个线程并发更新的
  • 声明为final的域是线程安全的
  • java.util.concurrent.atomic包中有很多类使用了高效的机器级指令(而不是锁)来保证其操作原子性
  • LongAdder和LongAccumulator类可以提高一些原子操作的性能
  • 避免出现死锁情况(循环求锁)
  • java.util.concurrent.locks定义了两个锁类,包括ReentrantLock类和ReentrantReadWriteLock类
  • stop方法终止所有未结束的方法,导致对象处于不一致的状态,因此不安全
  • suspend方法容易导致死锁

6.阻塞队列

  • 当试图向队列添加元素而队列已满,或是想从队列移出元素而队列为空的时候,阻塞队列导致线程阻塞
  • java.util.concurrent包提供了阻塞队列的几种变种。默认情况下,LinkedBlockingQueue的容量是没有上边界的,LinkedBlockingDeque是一个双端的版本,ArrayBlockingQueue在构造时需要指定容量,并且有一个可选的参数来指定是否需要公平性,PriorityBlockingQueue是一个带优先级的队列

7.线程安全的集合

  • java.util.concurrent包提供了映射、有序集和队列的高效实现:ConcurrentHashMap,ConcurrentSkipListMap,ConcurrentSkipListSet和ConcurrentLinkedQueue
  • CopyOnWriteArrayList和CopyOnWriteArraySet是线程安全的集合,其中所有的修改线程对底层数组进行复制
  • Arrays.parallelSort方法可以对一个基本类型值或者对象的数组进行并行排序
  • 任何集合类都可以通过同步包装器变成线程安全的

8.Callable与Future

  • Callable与Runnable类似,但是有返回值,Callable接口是一个参数化的类型,只有一个方法call,类型参数是返回值的类型
  • Future保存异步计算的结果,可以启动一个计算,将Future对象交给某个线程,然后忘掉它,Future对象的所有者在结果计算好之后就可以获得它

9.执行器

  • 执行器类有许多静态工厂方法来构建线程池
  • newCachedThreadPool方法构建一个线程池,如果有空闲线程可用,立即让它执行任务,如果没有可用的空闲线程,则创建一个新线程
  • newFixedThreadPool方法构建一个具有固定大小的线程池
  • newSingleThreadExecutor是一个退化了的大小为1的线程池
  • 使用sumbit方法将Runnable对象和Callable对象提交给ExecutorService

10.同步器

  • 信号量管理许多许可证,可以有效解决许多线程同步问题
  • 倒计时门栓让一个线程集等待直到计数为0,是一次性的,一旦为0就不能再用
  • CyclicBarrier类实现了一个集结点称为barrier,它是循环的,可以在所有等待线程被释放后被重用
  • 当两个线程在同一个数据缓冲区的两个实例上工作的时候,就可以使用交换器
  • 同步队列是一种将生产者和消费者线程配对的机制

11.线程与swing(与应用程序相关,跳过)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值