多线程编程
多任务处理有两种不同的类型:基于进程的多任务处理和基于线程的多任务处理
基于进程的多任务处理“大局”,而基于线程的多任务处理“细节”
使用多线程可以编写出更加高效的程序,以最大限度地利用系统提供的处理功能(使空闲时间保持最小)比如网络上数据传输速率比计算机处理速率低很多,多写本地文件处理速度比cpu的处理速度慢很多
单线程环境:单线程系统使用一种轮询时间循环的方法。单个线程在一个无限循环中控制运行,轮询一个事件队列以决定下一步做什么。一旦轮询返回一个信号,比如准备读取网络文件的信号,事件循环就将控制调度至适当的事件处理程序。在这个事件处理程序返回之前,程序不能执行任何其他工作。通常,在单线程环境中,当线程因为等待某些资源而阻塞时,整个程序会停在运行
Java多线程的优点:消除了主循环/轮询机制
线程状态:运行、挂起(suspended)、恢复(resumed)、等待资源时->阻塞(blocked)
线程的优先级用于决定何时从一个运行的线程切换到下一个,(上下文切换)context switch
线程愿的放弃控制:线程显式地放弃控制权、休眠或在I/O之前阻塞,这种情况下,优先级高的线程会获取CPU资源
线程被优先级更高的线程取代:抢占式多任务处理
具有相同优先级的两个线程竞争CPU,对于Windows,优先级相等的线程以轮询方式自动获取Cpu资源。对于其他类型的操作系统,优先级相等的线程必须自愿地向其他线程放弃控制权,否则其他线程就不能运行
Java以监视器进程间同步模型为基础,实现了一种巧妙的方案。一旦一个线程进入监视器,其他所有线程就必须等待,直到该线程退出监视器,Java没有提供Monitor类,每个对象都有自己隐士监视器。
将程序分隔到独立线程之后,需要定义它们之间相互通信的方式。Java的消息传递系统允许某个线程进入对象的同步方法,然后进行等待。直到其他线程显示地通知这个线程退出为止。
只有当类正在以某种方式增强或修改时,才应该对类进行扩展。
如果线程仍然在运行,isAlive()方法就返回true,否则返回false
虽然isAlive()方法有时很有用,但是通常使用join()方法来等待线程结束,该方法会一直等待,直到调用线程终止。Join()方法的另外一种形式,允许指定希望等待指定线程终止的最长时间
为了避免轮询检测,java通过wait()、notify()以及notifyAll()方法,提供一种巧妙的进程间通信机制
wait()方法通知调用线程放弃监视器并进入休眠,直到其他一些线程进入同一个监视器并调用notify()方法或notifyAll()方法
notify()方法唤醒调用相同对象的wait()方法的线程
notifyAll()方法唤醒调用相同对象的wait()方法的所有线程,其中的一个线程将得到访问授权
存在假唤醒,Oracle推荐应当在一个检查线程等待条件的循环中调用wait()方法
需要避免的与多任务处理明确相关的特殊类型的错误是死锁
现在不能使用suspend()、resume()以及stop()方法控制线程。Java2反对
为了创建能够自动伸缩以及尽可能利用多核系统中可用处理的计算密集型应用程序,可以考虑使用新的Fork/Join框架
枚举、自动装箱与注解
在java中,枚举定义了一种类类型。枚举可以具有构造函数、方法以及实例变量。
每个枚举常量被隐式声明为枚举类的共有、静态final成员,这些常量被称为是“自类型化的”其中的“自”是指封装常量的枚举
定义了枚举之后,可以创建枚举类型的变量。但是,尽管枚举定义了类类型。却不能使用new实例化枚举
Switch表达式中的枚举类型已经隐式指定了case常量的枚举类型
所有枚举都自动包含两个预定义方法values()和valueOf()
枚举有两条限制:第一,枚举不能继承其他类;第二,枚举不能是超类,
每个枚举常量都是定义它的类的对象
所有枚举类都自动继承超类java.lang.Enum,通过ordinal()方法可以检索序数值。
出于性能考虑,为这些数据使用基本类型而不是对象。为这些数据使用对象,即使是最简单的计算也会增加不可接受的开销。
Java实现的许多标准数据结构是针对对象进行操作的,不能通过引用为方法传递基本类型,Java提供了类型封装器,用来将基本类型封装到对象中。
JDK5开始,java增加了两个重要特性:自动装箱和自动拆箱。
通常,应当限制类型封装器的使用,只有当需要基本类型的对象表示形式时才应当使用。提供的自动装箱/拆箱特性不是用来作为消除基本类型的后门
注解(元数据)
从JDK5开始,Java支持在源文件中嵌入补充信息,这类信息称为注解。注解不会改变程序的动作,因此也就不会改变程序的语义。但是在开发和部署期间,各种工具可以使用这类信息。
注解是通过基于接口的机制创建的。注解内的方法的行为更像是域变量。
注解不能包含extends子句。但是,所有注解类型都自动扩展了Annotation接口
JDK8之前,注解只能用于声明。
指定保留策略,决定了在什么位置丢弃注解。SOURCE、CLASS、RUNTIME
SOURCE:只在源文件中保留
CLASS:编译时被存储到.class文件中。但是,在运行时通过JVM不能得到这些注解
RUNTIME:编译时存储在.class文件,并且在运行时通过JVM获取这些注解
注意:局部变量声明的注解不能存储在.class文件中
如果没有为注解指定保留策略@Retention,将使用默认策略CLASS
在运行时使用反射获取注解
可以为注解提供默认值
Type member() default value
标记注解是特殊类型的注解,其中不包含成员。唯一目的就是标记声明。
单成员注解只包含一个成员。除了允许使用缩写形式指定成员的值之外,单成员注解的工作方式和常规注解类似。为了使用缩写形式,成员名称必须是value
Java提供了许多内置注解。大部分是专用注解。但是有9个用于一般目的。
4个来自java.lang.annotation:
@Retention、@Documented、@Target和@Inherited
另外5个@override、@Deprecated、@FunctionlInterface、@SaveVarargs和@SuppressWarnings
来自:java.lang
@Inherited是标记注解,只能用于另外一个注解声明。@Inherited只影响用于类声明的注解。@Inherited会导致超类的注解被子类继承
@FunctionalInterface是JDK8新添加的一个标记注解,用于接口,指出被注解的接口是一个函数式接口。函数式接口是指仅包含一个抽象方法的接口,由lambda表达式使用。创建函数式接口并不需要使用@FunctionalInterface,理解这一点很重要。根据定义,任何仅有一个抽象方法的接口都是函数接口。因此@FunctionalInterface的意义仅在于提供信息
@SafeVarargs是标记注解,只能用于方法和构造函数,指示没有发生与可变长度参数相关的不安全动作。只能用于varargs方法或者声明为static或final的构造函数
从JDK8开始,Java增加了可以使用注解的方法。在能够使用类型的大多数地方,也可以指定注解。例如可以注解方法的返回类型,方法内this的类型、强制转换、数组级别、被继承的类以及throws子句。还可以注解泛型,包括泛型参数边界和泛型类型参数。
类型注解很重要,因为它们允许工具对代码执行额外的检查
类型注解必须包含ElementType.TYPE_USER作为目标
不能对void返回类型添加注解
JDK8中新增加的另一注解特性允许在相同元素上重复应用注解,这种特性称为重复注解。可重复的注解必须用@Repeatable进行注解
转载请注明:学时网 » java8学习笔记总结(三)