极*Java速成教程 - (8)
Java高级特性
注解
注解可以在代码之外添加更多的信息,更加完整地描述程序,帮助编译器进行工作,或者实现某些特定的Java代码之外的功能。
注解可以简化某些重复的流程,自动化那些过程。
注解的使用
注解的使用与其他修饰符的使用没有区别。Java提供了三种标准注解:
- @Override
表明该方法将要覆盖父类的方法,当方法签名与被覆盖方法不同时,编译器就会报错。 - @Deprecated
如果开发者使用了被注解的元素,那么编译器就会发出警告。 - @SuppressWarnings
关闭不当的编译器警告信息。
注解的定义
在使用注解前,需要对注解进行定义。注解不支持继承,注解的定义像一个空的接口,使用@interface的修饰,定义注解时,上方需要一些元注解。
元注解有四种,用于注解其他的注解:
- @Target 用于定义你的注解应用于什么地方,比如一个方法或者一个域
- CONSTRUCTOR 构造器的声明
- FIELD 域声明
- LOCAL_VARIABLE 局部变量声明
- METHOD 方法声明
- PACKAGE 包声明
- PARAMETER 参数声明
- TYPE 类、接口(包括注解类型)或enum声明
- @Retention 用来定义注解在哪一个级别可以用,如源代码中、类文件中或运行时。
- SOURCE 注解将被编译器丢弃
- CLASS 注解在class文件中可用,但会被VM丢弃
- RUNTIME VM将在运行期也保留注解,因此可以通过反射机制读取注解的信息
- @Documented 将此注解包含在Javadoc中
- @Inherited 允许子类继承父类中的注解
注解中可以包含一些元素以表示某些值,如果没有元素,那么这种注解叫标记注解。注解的元素可以有默认值。
注解的定义和使用如下:
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
public int id();
public String description() default "no description";
}
import java.util.*;
public class PasswordUtils {
@UseCase(id = 47, description =
"Passwords must contain at least one numeric")
public boolean validatePassword(String password) {
return (password.matches("\\w*\\d\\w*"));
}
}
在设置了注解后,还需要创建注解处理器,对注解进行处理。可以使用类的getDeclaredMethods()获取本类除继承以外的所有方法,getAnnotation()方法获取指定类型的方法注解。
注解元素
注解中可以使用注解元素来记录注解信息。
注解中可以使用如下类型:
- 所有基本类型
- String
- Class
- enum
- Annotation
- 以上类型的数组
元素必须有默认值,否则就必须在使用注解时提供值。对于非基本类型元素,不能以null值为其值(但是空字符串""和特定含义的负值是允许的)
Java的apt工具
在Java中,可以使用apt工具调用注解处理器,使其进行针对源代码的注解处理。必须提供一个工厂类或者工厂类的路径。
但在Java7以上,推荐使用javax.annotation.processing工具进行注解处理器的开发。相关内容参考注解处理器详解
并发
并行的方法
使用runnable接口
线程可以驱动任务,任务可以通过实现Runnable接口并编写run()方法来描述。
将Runnable对象提交给Thread构造器,可以创建一个线程来驱动任务。然后使用Thread类的start()方法,就可以启动任务。
通过java.util.concurrent包中的Executor类,可以对Thread对象进行管理。
通过newCachedThreadPool()方法创建线程池,然后对ExecutorService对象调用execute()方法注册任务,即可进行并行处理。调用shutdown()方法即可停止接受新任务。
除了CachedThreadPool,还有FixedThreadPool等线程池可以使用,FixedThreadPool可以一次性预先执行代价高昂的线程分配任务,同时对线程数量也可以进行限制。SingleThreadExecutor同时只会运行一个线程,这个执行器会序列化提交的任务,同时维护一个任务队列,依次执行。
在所有线程池中,在现有线程可能的情况下,都会被自动复用。
使用Callable接口
通过实现Callable接口,类也可以并发执行,通过call()方法可以在任务完成后返回一个返回值。这种方法必须使用ExecutorService对象的submit()方法提交任务。
submit()方法会产生Future对象,该对象可以用isDone()方法查询是否已经完成,当任务完成时,可以使用get()方法获取返回结果。get()方法同时也是一个阻塞方法。
可以在类的内部,通过内部类的方法继承Thread类或者是实现runnable接口,达到多线程的效果。
并行的控制
通过Thread.sleep()方法或者是TimeUnit.MILLISECONDS.sleep()方法,可以使线程休眠。
在run()方法内(在构造器中设置无效)使用线程对象的setPriority()方法可以设置线程优先级,getPriority()方法可以获取线程优先级,JDK的优先级和操作系统优先级并不对应,可移植的方法是使用Thread.MIN_PRIORITY,Thread.MAX_PRIORITY和Thread.NORM_PRIORITY。
使用yield()可以暗示在线程调度中可以做出让步(并不保证实现)。
在线程启动前,使用Thread对象的setDeamon()方法可以设置线程为后台线程。通过实现ThreadFactory接口,可以在接口内的newThread()方法中对创建的Thread进行setDeamon()设置。可以通过isDeamon()查询线程是否为后台线程。
对线程对象调用join()方法,可以将本线程挂起,直到超时或目标线程t结束才恢复。interrupt()方法会中断join,并且设置中断标志。isInterrupted()方法根据中断标志返回值,然而中断时会抛出异常,异常被捕获时会清理这个标志。
并行的异常处理
可以通过在ThreadFactory类中通过setUncaughtExceptionHandler()方法将一个实现了Thread.UncaughtExceptionHandler接口的异常处理器附加到线程上,如果线程抛出异常,那就交由异常处理器进行处理。还可以使用setDefaultUncaughExceptionHandler()方法设置线程的默认异常处理器。
并行的竞争控制
可以通过synchronized关键词对共享资源进行修饰,Java即可自动检查资源是否有锁,是否可用。在使用synchronized关键字时,域应该时private的,否则关键字不能保证其他任务不会访问域造成冲突。
还可以通过synchronized关键词对代码块进行同步控制,语法如下,this可替换为进行同步的对象:
synchronized(this){
XXX;
}
还可以使用java.util.concurrent类库中的locks类显式地加锁。lock类可以通过lock();tryLock()和unlock()方法加锁去锁。ReentrantLock允许尝试获取但是最终没有获取锁,当没有获取到锁时可以离开进行其他处理而不是等待。
原子性和易变性
long和double以外的基本数据类型的简单操作具有原子性,对long和double变量加以volatile修饰,可以使其具有赋值和返回操作的原子性。
volatile保证所修饰的对象是在所有处理器缓存和主存中是同步的,这对于多个线程同时访问一个对象很有意义。
除非是真正的专家,否则不要依赖原子性。
使用原子类
AtomicInteger等相关类等特殊原子性变量类可以保证原子性,但是被设计用来构建java.util.concurrent中的类,因此尽可能不要用,用了也要确保不会出现其他问题,依赖于锁更安全。
线程本地储存
使用ThreadLocal类可以为每个线程储存其独一份的对象,因此不会出现竞争。ThreadLocal类常当作静态域储存。
结束任务
在进程任务中,有时候不得不突然性地打断任务,可以(但是最好不要)使用Thread的interrupt()或interrupted()方法,也可以使用Executor的shutdownNow()方法关闭执行器上的所有线程任务,或者通过持有任务的Future对象,调用其cancel()方法打断执行器上某一个线程的任务。
但是I/O任务和synchronized块上的等待不可打断。此时为了中断阻塞的线程,只能通过关闭底层资源来释放锁。使用ReentrantLock锁可以被打断。
并行任务的协作
可以通过wait()和notify()/notifyAll()来实现进程间的握手问题。Java还提供了具有await()和signal()方法的Condition对象,通过锁上的Condition对象进行协作通信,这是一种更安全的方式。
当线程wait()时,将会释放对象的锁。而搭配使用notifyAll()的时候,只有等待特定锁的任务才会被唤醒。
在进行协作时,必须对一个特定的检查条件来对wait()操作条件进行检查,否则可能因为操作顺序的原因导致死锁的发生。
while(Something){
wait()
}
同步队列
java.util.concurrent.BlockingQueue接口提供了同步队列,任何时刻都只允许一个任务插入或移除元素,这个接口有LinkedBlockingQueue和ArrayBlockingQueue(固定尺寸)等实现。
管道
通过使用PipedWriter(允许任务向管道写)和PipedReader(允许不同任务从管道中读)类,可以通过管道,让多个线程从中进行通信。
没有数据传输时,管道自动阻塞。管道I/O可以被interrupt中断,这一点不同与系统I/O。
一些关于并发的支持
CountDownLatch
这个类可以设置一个计数,线程每次调用countDown()方法都会使计数-1,而await()方法在计数归零前会持续阻塞。
CyclicBarrier
这个类提供一个计数功能,该类的对象进行await()可以使计数-1,当计数归零时,CyclicBarrier会执行一次某一特定任务,然后await()所在的线程被唤醒。
DelayQueue
该类可以存放实现了Delayed接口的对象,该对象有一个延迟时间,只有在到期时才能从队列中取走,到期时间越长的排的越前。如果没有到期的,那么队列就没有头元素,poll()将返回空。
PriorityBlockingQueue
优先级队列,可以进行阻塞性的读取操作。
ScheduledExecutor
ScheduledThreadPoolExecutor类提供了schedule()(在延迟后运行一次任务)和scheduleAtFixedRate()(每隔一段时间重复执行任务)方法。
Semaphore
信号量机制,最多可以签发n个许可,允许n个任务同时访问这个资源,当没有资源时会进行阻塞。可以通过acquire()方法获取许可,通过release()方法解锁许可。
Exchanger
该类可以对两个线程持有的对象进行互换。在执行exchange()方法之前两个进程是互斥等待的。
免锁容器
CopyOnWriteArrayList在写入的时候创建原数组的副本进行写入,写入完成后使用一个原子性的操作将新的数组换入,不影响在写的同时读数据。
CopyOnWriteArraySet基于CopyOnWriteArrayList实现免锁行为
ConcurrentHashMap和ConcurrentLinkedQueue使用了类似计数,可以并发读取和写入,但是容器中只有部分内容而不是整个容器能被复制和修改,在修改完成前,读取者依然不能看到它们。
乐观锁
针对Aromic对象,可以执行decreamentAndGet()这样的原子方法,但是某些类允许使用compareAndSet()这样的方法,该方法会乐观地认为在操作期间没有其他人修改当前对象,该方法会同时提交新值和旧值,如果旧值不同,那么就需要开发者进行一些相应的处理了。
ReadWriteLock
读写锁对不频繁写入但是频繁读取的情况进行了优惠,可以同时拥有多个读取者,只要他们不写入即可,但是如果有人获得了写锁,那么在写锁释放前所有读者都不可以访问这个对象。
极*Java速成教程 - (8)的更多相关文章
- 极*Java速成教程 - (1)
序言 众所周知,程序员需要快速学习新知识,所以就有了<21天精通C++>和<MySQL-从删库到跑路>这样的书籍,Java作为更"高级"的语言也不应该落后, ...
- 极*Java速成教程 - (2)
Java语言基础 Java的一切都是以对象为基础,对象是生是死的生命周期由虚拟机管理,但是在创生和消亡阶段,需要我们去管理这个类怎么生,怎么死.我们也以此为契机,慢慢接触Java的诸多细节和具体实现. ...
- 极*Java速成教程 - (7)
Java高级特性 数组 在Java中,数组是一串连续的,不可改变长度的,对象被固定的,类型固定的连续空间.数组中的随机访问非常迅速,但为了速度放弃了灵活性.而效率也是数组最大的优点. 在使用泛型的容器 ...
- 极*Java速成教程 - (6)
Java高级特性 String String是Java中的字符串类型,字符串类型在内存中是一个不可变的对象.如果要对字符串对象进行修改,如果是较少的修改可以使用+运算符,Java会自动进行优化,但如果 ...
- 极*Java速成教程 - (5)
Java语言基础 容器 这个世界是有序的,将Java对象零散地放到内存中是不符合世界常理的,特别是有一大组相似的甚至不知道有多少数据的时候.把Java对象装进盒子里可以有序收纳,这个盒子就叫容器. 初 ...
- 极*Java速成教程 - (4)
Java语言基础 多态 多态是面向对象的一大重要特性,如果说封装是隐藏一个类怎么做,继承是确定一系列的类做什么,那多态就是通过手段去分离做什么和怎么做. 向上转型与收窄 在开发者将一类事物封装成类以后 ...
- 极*Java速成教程 - (3)
Java语言基础 访问权限控制 Java是一个面向对象的语言,当你不是它所设计的要面向的对象时,它就不会给你看你不该看到的东西,也就是"访问权限控制". 亲疏有别,才能权限控制 包 ...
- 极·Java速成教程 - (1)
序言 众所周知,程序员需要快速学习新知识,所以就有了<21天精通C++>和<MySQL-从删库到跑路>这样的书籍,Java作为更"高级"的语言也不应该落后, ...
- Java IO教程
1 Java IO 教程 2 Java IO 概述 3 Java IO: 文件 4 Java IO: 管道 5 Java IO: 网络 6 Java IO: 字节和字符数组 7 Java IO: S ...
随机推荐
- chrome模拟慢速3G网络
谷歌调试控制台中network中可以设置,add为自定义
- 【shell】截取字符串前面文字
例如:有一个文件test.txt,里面有这些数据. meiguounix232 faguounix ribenunix zhongguounixtaobao hanguounixbaba 现在我想截取 ...
- Nginx之概念和简介
Nginx是什么? 代理服务器,处于客户端和服务器端之间的一台服务器,不负责处理请求. 主要作用是什么? 1.负载均衡: 高并发场景下,Nginx代理服务器按一定规则将请求分发,从而使服务器能有条不紊 ...
- 线程优先级队列( Queue)
Python的Queue模块中提供了同步的.线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue.这些队列都实现 ...
- java+web+批量下载文件
JavaWeb 文件下载功能 文件下载的实质就是文件拷贝,将文件从服务器端拷贝到浏览器端,所以文件下载需要IO技术将服务器端的文件读取到,然后写到response缓冲区中,然后再下载到个人客户端. 1 ...
- HDU1254--推箱子(BFS+DFS)
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s) ...
- Oracle-RAC sysdate和current_date时间不一致,导致客户端连接时间延迟
[oracle@oracle-db1 ~]$ dateTue Oct 10 14:20:56 CST 2017[oracle@oracle-db1 ~]$ cat /etc/sysconfig/clo ...
- ELK5+redhat7.4配置elasticsearch集群
ELK介绍 ELK是三个开源软件的缩写,即elasticsearch.logstack.kibana. Elasticsearch:开源分布式搜索引擎,提供搜集.分析.存储数据三大功能.它的特点有:分 ...
- 在xml文件中使用该控件
<yf.changsha.com.view.MyTextView android:layout_width="match_parent" android:layout_hei ...
- ACM ICPC 2011-2012 Northeastern European Regional Contest(NEERC)B Binary Encoding
B: 现在有一种新的2进制表示法,要你求出0~m-1的每个数的表示. 规则如下:n 是满足 m<=2n 最小数. 而0~m-1的数只能够用n-1个位和n个位来表示. 对于n个位表示的数来说不能有 ...