FileChannel文件通道
用于读取、写入、映射和操作文件的通道。
文件通道在其文件中有一个当前 position,可对其进行查询和修改。该文件本身包含一个可读写的长度可变的字节序列,并且可以查询该文件的当前大小。写入的字节超出文件的当前大小时,则增加文件的大小;截取该文件时,则减小文件的大小。文件可能还有某个相关联的元数据,如访问权限、内容类型和最后的修改时间;此类未定义访问元数据的方法。
/**FileChannel:文件通道
* 1.该通道不能独自存在,需要建立在FileInputStream或FileOutputStream
* 2.在输入流上得到文件通道只能执行读操作,如果执行写操作,
* 则抛出NonWritableChannelException
* 3.在输出流上得到的文件通道只能执行写操作,如果执行读操作,
* 则抛出NonReadableChannelException
* @throws Exception
*/
public classDemoFileChannel {
@Test
public voidtestFcRead() throws Exception{
//创建输入流对象
FileInputStream in = newFileInputStream(new File("1.txt"));
//从输入流对象上获取它对应的文件通道对象
FileChannel fc = in.getChannel();
//指定位置
fc.position(4);
//定义缓冲区对象
ByteBuffer buf = ByteBuffer.allocate(8);
//从输入流中通过文件通道读取:从下标为4的位置开始读,读取8个字节内容并
//保存到buf中
fc.read(buf);
System.out.println(fc.position());
//设置position的值12
fc.position(12);
fc.write(ByteBuffer.wrap("6789".getBytes()));
//输出读取到的内容
System.out.println(new String(buf.array()));
//关闭文件通道
fc.close();
//关闭输入流
in.close();
}
@Test
public voidtestFcWrite() throws Exception{
//创建输出流对象
FileOutputStream out = newFileOutputStream(new File("1.txt"),true);
//获取输出流上的文件通道
FileChannel fc = out.getChannel();
//创建缓冲区对象
ByteBuffer buf =ByteBuffer.wrap("hello".getBytes());
//设置从哪个位置开始写入
fc.position(21);
//执行写入操作
fc.write(buf);
/*fc.position(11);
ByteBuffer readBuf =ByteBuffer.allocate(10);
fc.read(readBuf);
System.out.println(newString(readBuf.array()));*/
//关闭文件通道
fc.close();
//关闭输出流
out.close();
}
}
并发工具包
1.阻塞队列BlockingQueue
BlockingQueue通常用于一个线程生产对象,另外一个线程消费这些对象的场景。
一个线程往里边放,另外一个线程从里边取的一个 BlockingQueue 。
一个线程将会持续生产新对象并将其插入到队列之中,直到队列达到它所能容纳的临界点。
也就是说,它是有限的。如果该阻塞队列到达了其临界点,负责生产的线程将会在往里边插
入新对象时发生阻塞。它会一直处于阻塞之中,直到负责消费的线程从队列中拿走一个对象。
负责消费的线程将会一直从该阻塞队列中拿出对象。如果消费线程尝试去从一个空的队列中
提取对象的话,这个消费线程将会处于阻塞之中,直到一个生产线程把一个对象丢进队列。BlockingQueue 的方法
BlockingQueue 具有 4 组不同的方法用于插入、移除以及对队列中的元素进行检查。如果
请求的操作不能得到立即执行的话,每个方法的表现也不同。这些方法如下:
四组不同的行为方式解释:
1. 抛异常:如果试图的操作无法立即执行,抛一个异常。
2. 特定值:如果试图的操作无法立即执行,返回一个特定的值(常常是 true / false)。
3. 阻塞:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行。
4. 超时:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行,但等
待时间不会超过给定值。返回一个特定值以告知该操作是否成功(典型的是 true / false)。
无法向一个 BlockingQueue 中插入 null。如果你试图插入 null,BlockingQueue将会抛出
一个 NullPointerException。
1.1数组阻塞队列ArrayBlockingQueue
ArrayBlockingQueue类实现了 BlockingQueue 接口。
ArrayBlockingQueue是一个有界的阻塞队列,其内部实现是将对象放到一个数组里。有界也就意味着,它不能够存储无限多数量的元素。它有一个同一时间能够存储元素数量的上限。你可以在对其初始化的时候设定这个上限,但之后就无法对这个上限进行修改了(因为它是基于数组实现的,也就具有数组的特性:一旦初始化,大小就无法修改)。
ArrayBlockingQueue内部以 FIFO(先进先出)的顺序对元素进行存储。队列中的头元素在所有元素之中是放入时间最久的那个,而尾元素则是最短的那个。
以下是在使用 ArrayBlockingQueue 的时候对其初始化的一个示例:
public void testProduce(){
//创建阻塞队列,ArrayBlockingQueue需要指定阻塞队列最大能够保存元素的个数
BlockingQueue<Integer> que = new ArrayBlockingQueue<Integer>(15);
for(int i =1;i<=15;i++){
que.add(i);
}
/*boolean result = que.add(16);
System.out.println(result);*/
/*boolean result =que.offer(16);
System.out.println(result);*/
/*try {
que.put(16);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
try {
boolean result = que.offer(16, 3, TimeUnit.SECONDS);
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void testConsumer(){
BlockingQueue<Integer> que = new ArrayBlockingQueue<Integer>(15);
//que.add(1);
/*Integer result = que.remove();
System.out.println(result);*/
/*boolean result = que.remove(new Integer(2));
System.out.println(result);*/
/*Integer result = que.poll();
System.out.println(result);*/
/*Integer result;
try {
result = que.poll(3,TimeUnit.SECONDS);
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
try {
Integer result = que.take();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
1.2延迟队列 DelayQueue
DelayQueue 实现了BlockingQueue 接口。
DelayQueue 对元素进行持有直到一个特定的延迟到期。注入其中的元素必须实现
java.util.concurrent.Delayed 接口,该接口定义:
public interface Delayed extendsComparable<Delayed< {
public long getDelay(TimeUnit timeUnit);
}
DelayQueue 将会在每个元素的getDelay() 方法返回的值的时间段之后才释放掉该元素。如
果返回的是 0 或者负值,延迟将被认为过期,该元素将会在 DelayQueue 的下一次 take
被调用的时候被释放掉。
传递给 getDelay 方法的getDelay 实例是一个枚举类型,它表明了将要延迟的时间段。
TimeUnit 枚举将会取以下值:
DAYS
HOURS
MINUTES
SECONDS
MILLISECONDS
MICROSECONDS
NANOSECONDS
正如所看到的,Delayed 接口也继承了 java.lang.Comparable 接口,这也就意味着 Delayed
对象之间可以进行对比。这个可能在对 DelayQueue 队列中的元素进行排序时有用,因此它
们可以根据过期时间进行有序释放。
以下是使用 DelayQueue 的例子:
public class DelayQueueExample {
public static void main(String[] args) {
DelayQueue queue = new DelayQueue();
Delayed element1 = new DelayedElement();
queue.put(element1);
Delayed element2 = queue.take();
}
}
1.3 链阻塞队列 LinkedBlockingQueue
LinkedBlockingQueue 类实现了 BlockingQueue 接口。
LinkedBlockingQueue 内部以一个链式结构(链接节点)对其元素进行存储。如果需要的话,
这一链式结构可以选择一个上限。如果没有定义上限,将使用Integer.MAX_VALUE 作为
上限。
LinkedBlockingQueue 内部以 FIFO(先进先出)的顺序对元素进行存储。队列中的头元素在所
有元素之中是放入时间最久的那个,而尾元素则是最短的那个。
以下是 LinkedBlockingQueue 的初始化和使用示例代码:
BlockingQueue<String> unbounded = newLinkedBlockingQueue<String>();
BlockingQueue<String> bounded = newLinkedBlockingQueue<String>(1024);
bounded.put("Value");
String value = bounded.take();
1.4 优 先 级 的 阻 塞 队 列PriorityBlockingQueue
PriorityBlockingQueue类实现了 BlockingQueue 接口。
PriorityBlockingQueue 是一个无界的并发队列。它使用了和类 java.util.PriorityQueue 一样的排序规则。你无法向这个队列中插入null 值。
所有插入到 PriorityBlockingQueue 的元素必须实现 java.lang.Comparable 接口。因此该队
列中元素的排序就取决于你自己的 Comparable 实现。
注意 PriorityBlockingQueue 对于具有相等优先级(compare() == 0)的元素并不强制任何特定行为。
同时注意,如果你从一个 PriorityBlockingQueue 获得一个 Iterator 的话,该 Iterator 并不
能保证它对元素的遍历是以优先级为序的。
以下是使用 PriorityBlockingQueue 的示例:
BlockingQueue queue = newPriorityBlockingQueue();
queue.put("Value");
String value = queue.take();
1.5同步队列 SynchronousQueue
SynchronousQueue 类实现了 BlockingQueue 接口。
SynchronousQueue 是一个特殊的队列,它的内部同时只能够容纳单个元素。如果该队列已
有一元素的话,试图向队列中插入一个新元素的线程将会阻塞,直到另一个线程将该元素从
队列中抽走。同样,如果该队列为空,试图向队列中抽取一个元素的线程将会阻塞,直到另
一个线程向队列中插入了一条新的元素。
据此,把这个类称作一个队列显然是夸大其词了。它更多像是一个汇合点。
1.6阻塞双端队列 BlockingDeque
BlockingDeque 类是一个双端队列,在不能够插入元素时,它将阻塞住试图插入元素的线程;
在不能够抽取元素时,它将阻塞住试图抽取的线程。
deque(双端队列) 是 "Double Ended Queue" 的缩写。因此,双端队列是一个你可以从任意一端插入或者抽取元素的队列.
BlockingDeque 的使用
在线程既是一个队列的生产者又是这个队列的消费者的时候可以使用到BlockingDeque。如
果生产者线程需要在队列的两端都可以插入数据,消费者线程需要在队列的两端都可以移除
数据,这个时候也可以使用 BlockingDeque。BlockingDeque 图解:
一个 BlockingDeque - 线程在双端队列的两端都可以插入和提取元素
一个线程生产元素,并把它们插入到队列的任意一端。如果双端队列已满,插入线程将被阻塞,直到一个移除线程从该队列中移出了一个元素。如果双端队列为空,移除线程将被阻塞,直到一个插入线程向该队列插入了一个新元素。
BlockingDeque的方法
四组不同的行为方式解释:
1. 抛异常:如果试图的操作无法立即执行,抛一个异常。
2. 特定值:如果试图的操作无法立即执行,返回一个特定的值(常常是 true / false)。
3. 阻塞:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行。
4. 超时:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行,但等
待时间不会超过给定值。返回一个特定值以告知该操作是否成功(典型的是 true / false)。
1.7链阻塞双端队列 LinkedBlockingDeque
LinkedBlockingDeque类实现了 BlockingDeque 接口。
deque(双端队列) 是 "Double Ended Queue" 的缩写。因此,双端队列是一个你可以从任意一端插入或者抽取元素的队列。
LinkedBlockingDeque是一个双端队列,在它为空的时候,一个试图从中抽取数据的线程将会阻塞,无论该线程是试图从哪一端抽取数据。
以下是LinkedBlockingDeque 实例化以及使用的示例:
BlockingDeque<String>deque = new LinkedBlockingDeque<String>();
deque.addFirst("1");
deque.addLast("2");
Stringtwo = deque.takeLast();
Stringone = deque.takeFirst();
2并发 Map ConcurrentMap
2.1ConcurrentHashMap
/**ConcurrentMap将map中的元素分别保存到16个分段桶中,
* 将来操作某一个元素时,只会锁定该元素所在的那个桶,其它
* 桶的元素不收到影响。效率时Hashtable的“16倍”。
*/
@Test
public void testConcurrentMap(){
ConcurrentMap<Integer, String> map =
new ConcurrentHashMap<Integer, String>();
map.put(1, "张飞");
map.put(2, "刘备");
System.out.println(map.get(1));
}
2.2并 发 导 航 映 射ConcurrentNavigableMap
public void testConcurrentNavigableMap(){
ConcurrentNavigableMap<Integer,String> map =
new ConcurrentSkipListMap<Integer,String>();
map.put(3, "三");
map.put(5, "五");
map.put(4, "四");
map.put(2, "二");
map.put(6, "六");
map.put(1, "一");
/**headMap(K toKey):获取key值小于toKey所有的键值对
*subMap(K fromKey, K toKey):获取key值>=fromKey,<toKey的所有键值对
*tailMap(K fromKey):获取key值>=fromKey的所有键值对。
*/
ConcurrentNavigableMap<Integer,String> headMap=map.headMap(2);
printMap(headMap);
ConcurrentNavigableMap<Integer,String>subMap=map.subMap(3, 4);
printMap(subMap);
ConcurrentNavigableMap<Integer,String> tailMap = map.tailMap(5);
printMap(tailMap);
}
3.0闭锁 CountDownLatch
java.util.concurrent.CountDownLatch是一个并发构造,它允许一个或多个线程等待一系列指定操作的完成。
CountDownLatch 以一个给定的数量初始化。countDown() 每被调用一次,这一数量就减一。
通过调用 await() 方法之一,线程可以阻塞等待这一数量到达零。
以下是一个简单示例。Decrementer 三次调用 countDown() 之后,等待中的 Waiter 才会从await() 调用中释放出来。
CountDownLatchlatch = new CountDownLatch(3);
Waiter waiter =new Waiter(latch);
Decrementerdecrementer = new Decrementer(latch);
newThread(waiter) .start();
newThread(decrementer).start();
Thread.sleep(4000);
public classWaiter implements Runnable{
CountDownLatchlatch = null;
publicWaiter(CountDownLatch latch) {
this.latch =latch;
}
public voidrun() {
try {
latch.await();
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("WaiterReleased");
}
}
public classDecrementer implements Runnable {
CountDownLatchlatch = null;
publicDecrementer(CountDownLatch latch) {
this.latch =latch;
}
public voidrun() {
try {
Thread.sleep(1000);
this.latch.countDown();
Thread.sleep(1000);
this.latch.countDown();
Thread.sleep(1000);
this.latch.countDown();
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
4.0栅栏CyclicBarrier
java.util.concurrent.CyclicBarrier类是一种同步机制,它能够对处理一些算法的线程实现同步。换句话讲,它就是一个所有线程必须等待的一个栅栏,直到所有线程都到达这里,然后所有线程才可以继续做其他事情。图示如下:
通过调用 CyclicBarrier 对象的 await() 方法,两个线程可以实现互相等待。一旦 N 个线程在等待 CyclicBarrier 达成,所有线程将被释放掉去继续运行。
public class DemoCyclicBarrier {
public static void main(String[] args) {
//创建栅栏对象,实例化时管理几个线程计数器的值就为几
CyclicBarrier cb = newCyclicBarrier(2);
new Thread(new CbHorse1(cb)).start();
new Thread(new CbHorse2(cb)).start();
}
}
class CbHorse1 implements Runnable{
private CyclicBarrier cb;
public CbHorse1(CyclicBarrier cb){
this.cb = cb;
}
public void run(){
System.out.println("马一在拉肚子....");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("马一来到了起跑线...");
try {//掉用该方法时,当前线程会产生阻塞,直到栅栏的计数器变为0时,阻塞
//才会被释放。
cb.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("马一开始比赛");
System.out.println("马一跑到了终点");
}
}
class CbHorse2 implements Runnable{
private CyclicBarrier cb;
public CbHorse2(CyclicBarrier cb){
this.cb = cb;
}
public void run(){
System.out.println("马二来到了起跑线...");
try {
cb.await();
} catch (InterruptedException |BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("马二开始比赛...");
System.out.println("马二跑到了终点...");
}
}
5.0交换机 Exchanger
java.util.concurrent.Exchanger类表示一种两个线程可以进行互相交换对象的会和点。这种机制图示如下:
public class DemoExchanger {
public static void main(String[] args) {
Exchanger<String> ex = new Exchanger<String>();
new Thread(new SpyRunner1(ex)).start();
new Thread(new SpyRunner2(ex)).start();
}
}
class SpyRunner1 implements Runnable{
private Exchanger<String> ex;
public SpyRunner1(Exchanger<String> ex){
this.ex = ex;
}
public void run(){
try {
String result = ex.exchange("天王盖地虎");
System.out.println("间谍二传给间谍一:"+result);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class SpyRunner2 implements Runnable{
private Exchanger<String> ex;
public SpyRunner2(Exchanger<String> ex){
this.ex = ex;
}
public void run(){
try {
String result = ex.exchange("锄禾日当午");
System.out.println("间谍一传给间谍二:"+result);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}