导航:首页 > 编程语言 > java非阻模式

java非阻模式

发布时间:2024-04-16 11:27:52

java 中 阻塞队列 非阻塞队列 和普通队列的区别是什么

阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来,如从队列中移除一个或者多个元素,或者完全清空队列.
1.ArrayDeque, (数组双端队列)
2.PriorityQueue, (优先级队列)
3.ConcurrentLinkedQueue, (基于链表的并发队列)
4.DelayQueue, (延期阻塞队列)(阻塞队列实现了BlockingQueue接口)
5.ArrayBlockingQueue, (基于数组的并发阻塞队列)
6.LinkedBlockingQueue, (基于链表的FIFO阻塞队列)
7.LinkedBlockingDeque, (基于链表的FIFO双端阻塞队列)
8.PriorityBlockingQueue, (带优先级的无界阻塞队列)
9.SynchronousQueue (并发同步阻塞队列)
阻塞队列和生产者-消费者模式
阻塞队列(Blocking queue)提供了可阻塞的put和take方法,它们与可定时的offer和poll是等价的。如果Queue已经满了,put方法会被阻塞直到有空间可用;如果Queue是空的,那么take方法会被阻塞,直到有元素可用。Queue的长度可以有限,也可以无限;无限的Queue永远不会充满,所以它的put方法永远不会阻塞。
阻塞队列支持生产者-消费者设计模式。一个生产者-消费者设计分离了“生产产品”和“消费产品”。该模式不会发现一个工作便立即处理,而是把工作置于一个任务(“to do”)清单中,以备后期处理。生产者-消费者模式简化了开发,因为它解除了生产者和消费者之间相互依赖的代码。生产者和消费者以不同的或者变化的速度生产和消费数据,生产者-消费者模式将这些活动解耦,因而简化了工作负荷的管理。
生产者-消费者设计是围绕阻塞队列展开的,生产者把数据放入队列,并使数据可用,当消费者为适当的行为做准备时会从队列中获取数据。生产者不需要知道消费者的省份或者数量,甚至根本没有消费者—它们只负责把数据放入队列。类似地,消费者也不需要知道生产者是谁,以及是谁给它们安排的工作。BlockingQueue可以使用任意数量的生产者和消费者,从而简化了生产者-消费者设计的实现。最常见的生产者-消费者设计是将线程池与工作队列相结合。
阻塞队列简化了消费者的编码,因为take会保持阻塞直到可用数据出现。如果生产者不能足够快地产生工作,让消费者忙碌起来,那么消费者只能一直等待,直到有工作可做。同时,put方法的阻塞特性也大大地简化了生产者的编码;如果使用一个有界队列,那么当队列充满的时候,生产者就会阻塞,暂不能生成更多的工作,从而给消费者时间来赶进进度。
有界队列是强大的资源管理工具,用来建立可靠的应用程序:它们遏制那些可以产生过多工作量、具有威胁的活动,从而让你的程序在面对超负荷工作时更加健壮。
虽然生产者-消费者模式可以把生产者和消费者的代码相互解耦合,但是它们的行为还是间接地通过共享队列耦合在一起了
类库中包含一些BlockingQueue的实现,其中LinkedBlockingQueue和ArrayBlockingQueue是FIFO队列,与 LinkedList和ArrayList相似,但是却拥有比同步List更好的并发性能。PriorityBlockingQueue是一个按优先级顺序排序的队列,当你不希望按照FIFO的属性处理元素时,这个PriorityBolckingQueue是非常有用的。正如其他排序的容器一样,PriorityBlockingQueue可以比较元素本身的自然顺序(如果它们实现了Comparable),也可以使用一个 Comparator进行排序。
最后一个BlockingQueue的实现是SynchronousQueue,它根本上不是一个真正的队列,因为它不会为队列元素维护任何存储空间。不过,它维护一个排队的线程清单,这些线程等待把元素加入(enqueue)队列或者移出(dequeue)队列。因为SynchronousQueue没有存储能力,所以除非另一个线程已经准备好参与移交工作,否则put和take会一直阻止。SynchronousQueue这类队列只有在消费者充足的时候比较合适,它们总能为下一个任务作好准备。
非阻塞算法
基于锁的算法会带来一些活跃度失败的风险。如果线程在持有锁的时候因为阻塞I/O,页面错误,或其他原因发生延迟,很可能所有的线程都不能前进了。
一个线程的失败或挂起不应该影响其他线程的失败或挂起,这样的算法成为非阻塞(nonblocking)算法;如果算法的每一个步骤中都有一些线程能够继续执行,那么这样的算法称为锁自由(lock-free)算法。在线程间使用CAS进行协调,这样的算法如果能构建正确的话,它既是非阻塞的,又是锁自由的。非竞争的CAS总是能够成功,如果多个线程以一个CAS竞争,总会有一个胜出并前进。非阻塞算法堆死锁和优先级倒置有“免疫性”(但它们可能会出现饥饿和活锁,因为它们允许重进入)。
非阻塞算法通过使用低层次的并发原语,比如比较交换,取代了锁。原子变量类向用户提供了这些底层级原语,也能够当做“更佳的volatile变量”使用,同时提供了整数类和对象引用的原子化更新操作

❷ java怎么写阻塞式io流,阻塞与非阻塞在写法上的有什么区别求大神

首先要明白什么是“阻塞”?
阻塞实际是针对“当前”线程的一个概念,当前线程可以往下走,就是没有阻塞,否则就可以说当前线程被阻塞了。
明白了概念就好处理了:
非阻塞:new Thread(){ public void run(){ /* 我的IO处理*/ } }.start()
阻塞:aInputStream.read()这样就可以了。最简单的验证:在main方法中加入这句“System.in.read();”看看你的程序是不是停在这句了?除非你在控制台输入东西,否则你的程序就“阻塞”在这里了。

❸ Java NIO怎么理解通道和非阻塞

nio引入了buffer、channel、selector等概念。
通道相当于之前的I/O流。
“通道”太抽象了。java解释不清的东西只能看它底层是怎么解释的——操作系统的I/O控制,通道控制方式?
I/O设备:CPU——通道——设备控制器——I/O设备
(通道和设备控制器的关系是多对多,设备控制器和I/O设备的关系也是多对多。)
I/O过程,参考http://www.nbrkb.net/lwt/jsjsj/asm/INTR&DMA.htm:
1.CPU在执行用户程序时遇到I/O请求,根据用户的I/O请求生成通道程序(也可以是事先编好的)。放到内存中,并把该通道程序首地址放入CAW中。
2.CPU执行“启动I/O”指令,启动通道工作。
3.通道接收“启动I/O”指令信号,从CAW(记录下一条通道指令存放的地址)中取出通道程序首地址,并根据此地址取出通道程序的第一条指令,放入CCW(记录正在执行的通道指令)中;同时向CPU发回答信号,通知“启动I/O”指令完成完毕,CPU可继续执行。
4.与此同时,通道开始执行通道程序,进行物理I/O操作。当执行完一条指令后,如果还有下一条指令则继续执行;否则表示传输完成,同时自行停止,通知CPU转去处理通道结束事件,并从CCW中得到有关通道状态。

如此一来,主处理器只要发出一个I/O操作命令,剩下的工作完全由通道负责。I/O操作结束后,I/O通道会发出一个中断请求,表示相应操作已完成。

通道控制方式是对数据块进行处理的,并非字节。

通道控制方式就是异步I/O,参考http://blog.csdn.net/historyasamirror/article/details/5778378:
I/O分两段:1.数据从I/O设备到内核缓冲区。2.数据从内核缓冲区到应用缓冲区

I/O类型:
1.异步I/O不会产生阻塞,程序不会等待I/O完成,继续执行代码,等I/O完成了再执行一个什么回调函数,代码执行效率高。很容易联想到ajax。这个一般用于I/O操作不影响之后的代码执行。
2.阻塞I/O,程序发起I/O操作后,进程阻塞,CPU转而执行其他进程,I/O的两个步骤完成后,向CPU发送中断信号,进程就绪,等待执行。
3.非阻塞I/O并非都不阻塞,其实是第一步不阻塞,第二部阻塞。程序发起I/O操作后,进程一直检查第一步是否完成,CPU一直在循环询问,完成后,进程阻塞直到完成第二步。明白了!这个是“站着茅坑不拉屎”,CPU利用率最低的。逻辑和操作系统的程序直接控制方式一样。
阻塞不阻塞描述的是发生I/O时当前线程的状态。
以上是操作系统的I/O,那么java的nio又是怎样的呢?
个人觉得是模仿了通道控制方式。
先看看nio的示例代码:
服务端TestReadServer.java

import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; public class TestReadServer { /*标识数字*/ private int flag = 0; /*缓冲区大小*/ private int BLOCK = 1024*1024*10; /*接受数据缓冲区*/ private ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK); /*发送数据缓冲区*/ private ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK); private Selector selector; public TestReadServer(int port) throws IOException { // 打开服务器套接字通道 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // 服务器配置为非阻塞 serverSocketChannel.configureBlocking(false); // 检索与此通道关联的服务器套接字 ServerSocket serverSocket = serverSocketChannel.socket(); // 进行服务的绑定 serverSocket.bind(new InetSocketAddress(port)); // 通过open()方法找到Selector selector = Selector.open(); // 注册到selector,等待连接 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("Server Start----"+port+":"); } // 监听 private void listen() throws IOException { while (true) { // 选择一组键,并且相应的通道已经打开 selector.select(); // 返回此选择器的已选择键集。 Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { SelectionKey selectionKey = iterator.next(); iterator.remove(); handleKey(selectionKey); } } } // 处理请求 private void handleKey(SelectionKey selectionKey) throws IOException { // 接受请求 ServerSocketChannel server = null; SocketChannel client = null; String receiveText; String sendText; int count=0; // 测试此键的通道是否已准备好接受新的套接字连接。 if (selectionKey.isAcceptable()) { // 返回为之创建此键的通道。 server = (ServerSocketChannel) selectionKey.channel(); // 接受到此通道套接字的连接。 // 此方法返回的套接字通道(如果有)将处于阻塞模式。 client = server.accept(); // 配置为非阻塞 client.configureBlocking(false); // 注册到selector,等待连接 client.register(selector, SelectionKey.OP_READ); } else if (selectionKey.isReadable()) { // 返回为之创建此键的通道。 client = (SocketChannel) selectionKey.channel(); //将缓冲区清空以备下次读取 receivebuffer.clear(); //读取服务器发送来的数据到缓冲区中 System.out.println(System.currentTimeMillis()); count = client.read(receivebuffer); System.out.println(System.currentTimeMillis() + "~"+count); } } /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // TODO Auto-generated method stub int port = 1234; TestReadServer server = new TestReadServer(port); server.listen(); } }客户端TestReadClient.javaimport java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; public class TestReadClient { /*标识数字*/ private static int flag = 0; /*缓冲区大小*/ private static int BLOCK = 1024*1024*10; /*接受数据缓冲区*/ private static ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK); /*发送数据缓冲区*/ private static ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK); /*服务器端地址*/ private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress( "localhost", 1234); public static void main(String[] args) throws IOException { // TODO Auto-generated method stub // 打开socket通道 SocketChannel socketChannel = SocketChannel.open(); // 设置为非阻塞方式 socketChannel.configureBlocking(false); // 打开选择器 Selector selector = Selector.open(); // 注册连接服务端socket动作 socketChannel.register(selector, SelectionKey.OP_CONNECT); // 连接 socketChannel.connect(SERVER_ADDRESS); // 分配缓冲区大小内存 Set<SelectionKey> selectionKeys; Iterator<SelectionKey> iterator; SelectionKey selectionKey; SocketChannel client; String receiveText; String sendText; int count=0; while (true) { //选择一组键,其相应的通道已为 I/O 操作准备就绪。 //此方法执行处于阻塞模式的选择操作。 selector.select(); //返回此选择器的已选择键集。 selectionKeys = selector.selectedKeys(); //System.out.println(selectionKeys.size()); iterator = selectionKeys.iterator(); while (iterator.hasNext()) { selectionKey = iterator.next(); if (selectionKey.isConnectable()) { System.out.println("client connect"); client = (SocketChannel) selectionKey.channel(); // 判断此通道上是否正在进行连接操作。 // 完成套接字通道的连接过程。 if (client.isConnectionPending()) { client.finishConnect(); System.out.println("完成连接!"); sendbuffer.clear(); BufferedInputStream br = new BufferedInputStream(new FileInputStream(new File("D:\BigData.zip"))); byte[] b = new byte[BLOCK]; br.read(b); sendbuffer.put(b); sendbuffer.flip(); System.out.println(System.currentTimeMillis()); client.write(sendbuffer); System.out.println(System.currentTimeMillis()); } client.register(selector, SelectionKey.OP_READ); } else if (selectionKey.isReadable()) { client = (SocketChannel) selectionKey.channel(); //将缓冲区清空以备下次读取 receivebuffer.clear(); //读取服务器发送来的数据到缓冲区中 count=client.read(receivebuffer); if(count>0){ receiveText = new String( receivebuffer.array(),0,count); System.out.println("客户端接受服务器端数据--:"+receiveText); client.register(selector, SelectionKey.OP_WRITE); } } } selectionKeys.clear(); } } }例子是TestReadClient向TestReadServer发送一个本地文件。TestReadServer收到后每次打印读取到的字节数。

如何体现异步I/O?
看看TestReadClient中的:

if (selectionKey.isConnectable()) { System.out.println("client connect"); client = (SocketChannel) selectionKey.channel(); // 判断此通道上是否正在进行连接操作。 // 完成套接字通道的连接过程。 if (client.isConnectionPending()) { client.finishConnect();如果没有client.finishConnect();这句等待完成socket连接,可能会报异常:java.nio.channels.NotYetConnectedException

异步的才不会管你有没有连接成功,都会执行下面的代码。这里需要人为的干预。
如果要证明是java的nio单独使用非阻塞I/O,真没办法!!!阻塞非阻塞要查看进程。。。
不过还有种说法,叫异步非阻塞。上面那段,是用异步方式创建连接,进程当然没有被阻塞。使用了finishConnect()这是人为将程序中止,等待连接创建完成(是模仿阻塞将当前进程阻塞掉,还是模仿非阻塞不断轮询访问,不重要了反正是程序卡住没往下执行)。
所以,创建连接的过程用异步非阻塞I/O可以解释的通。那read/write的过程呢?
根据上面例子的打印结果,可以知道这个过程是同步的,没执行完是不会执行下面的代码的。至于底下是使用阻塞I/O还是非阻塞I/O,对于应用级程序来说不重要了。
阻塞还是非阻塞,对于正常的开发(创立连接,从连接中读写数据)并没有多少的提升,操作过程都类似。
那NIO凭什么成为高性能架构的基础,比起IO,性能优越在哪里,接着猜。。。
java nio有意模仿操作系统的通道控制方式,那他的底层是不是就是直接使用操作系统的通道?
通道中的数据是以块为单位的,之前的流是以字节为单位的,同样的数据流操作外设的次数较多。代码中channel都是针对ByteBuffer对象进行read/write的,而ByteBuffer又是ByteBuffer.allocate(BLOCK);这样创建的,是一个连续的块空间。
那ByteBuffer是不是也是模拟操作系统的缓存?
缓存在io也有,如BufferedInputStream。CPU和外设的速度差很多,缓存为了提高CPU使用率,等外设将数据读入缓存后,CPU再统一操作,不用外设读一次,CPU操作一次,CPU的效率会被拉下来。。。

❹ Java NIO和IO的区别

JavaNIO和IO之间的主要差别,我会更详细地描述表中每部分的差异。
IONIO
面向流面向缓冲
阻塞IO非阻塞IO
无选择器
面向流与面向缓冲
JavaNIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。JavaIO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。JavaNIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。
阻塞与非阻塞IO
JavaIO的各种流是阻塞的。这意味着,当一个线程调用read()或write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。JavaNIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。
选择器(Selectors)
JavaNIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来逗选择地通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。
NIO和IO如何影响应用程序的设计
无论您选择IO或NIO工具箱,可能会影响您应用程序设计的以下几个方面:
1.对NIO或IO类的API调用。
2.数据处理。
3.用来处理数据的线程数。
API调用
当然,使用NIO的API调用时看起来与使用IO时有所不同,但这并不意外,因为并不是仅从一个InputStream逐字节读取,而是数据必须先读入缓冲区再处理。
数据处理
使用纯粹的NIO设计相较IO设计,数据处理也受到影响。
在IO设计中,我们从InputStream或Reader逐字节读取数据。假设你正在处理一基于行的文本数据流,例如:
Name:Anna
Age:25
Email:[email protected]
Phone:1234567890
该文本行的流可以这样处理:
BufferedReaderreader=newBufferedReader(newInputStreamReader(input));

StringnameLine=reader.readLine();
StringageLine=reader.readLine();
StringemailLine=reader.readLine();
StringphoneLine=reader.readLine();
请注意处理状态由程序执行多久决定。换句话说,一旦reader.readLine()方法返回,你就知道肯定文本行就已读完,readline()阻塞直到整行读完,这就是原因。你也知道此行包含名称;同样,第二个readline()调用返回的时候,你知道这行包含年龄等。正如你可以看到,该处理程序仅在有新数据读入时运行,并知道每步的数据是什么。一旦正在运行的线程已处理过读入的某些数据,该线程不会再回退数据(大多如此)。下图也说明了这条原则:
(JavaIO:从一个阻塞的流中读数据)而一个NIO的实现会有所不同,下面是一个简单的例子:
ByteBufferbuffer=ByteBuffer.allocate(48);

intbytesRead=inChannel.read(buffer);
注意第二行,从通道读取字节到ByteBuffer。当这个方法调用返回时,你不知道你所需的所有数据是否在缓冲区内。你所知道的是,该缓冲区包含一些字节,这使得处理有点困难。
假设第一次read(buffer)调用后,读入缓冲区的数据只有半行,例如,逗Name:An地,你能处理数据吗看显然不能,需要等待,直到整行数据读入缓存,在此之前,对数据的任何处理毫无意义。
所以,你怎么知道是否该缓冲区包含足够的数据可以处理呢看好了,你不知道。发现的方法只能查看缓冲区中的数据。其结果是,在你知道所有数据都在缓冲区里之前,你必须检查几次缓冲区的数据。这不仅效率低下,而且可以使程序设计方案杂乱不堪。例如:
ByteBufferbuffer=ByteBuffer.allocate(48);

intbytesRead=inChannel.read(buffer);

while(!bufferFull(bytesRead)){

bytesRead=inChannel.read(buffer);

}
bufferFull()方法必须跟踪有多少数据读入缓冲区,并返回真或假,这取决于缓冲区是否已满。换句话说,如果缓冲区准备好被处理,那么表示缓冲区满了。
bufferFull()方法扫描缓冲区,但必须保持在bufferFull()方法被调用之前状态相同。如果没有,下一个读入缓冲区的数据可能无法读到正确的位置。这是不可能的,但却是需要注意的又一问题。
如果缓冲区已满,它可以被处理。如果它不满,并且在你的实际案例中有意义,你或许能处理其中的部分数据。但是许多情况下并非如此。下图展示了逗缓冲区数据循环就绪地:
3)用来处理数据的线程数
NIO可让您只使用一个(或几个)单线程管理多个通道(网络连接或文件),但付出的代价是解析数据可能会比从一个阻塞流中读取数据更复杂。
如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,例如聊天服务器,实现NIO的服务器可能是一个优势。同样,如果你需要维持许多打开的连接到其他计算机上,如P2P网络中,使用一个单独的线程来管理你所有出站连接,可能是一个优势。一个线程多个连接的设计方案如
JavaNIO:单线程管理多个连接
如果你有少量的连接使用非常高的带宽,一次发送大量的数据,也许典型的IO服务器实现可能非常契合。下图说明了一个典型的IO服务器设计:

JavaIO:一个典型的IO服务器设计-一个连接通过一个线程处理

❺ java socket 阻塞和非阻塞 会对数据传输有什么影响

阻塞会导致你的程序停在这里不往下走 非阻塞时,你的程序可以一边干其他事情(比如显示个进度条),以便等数据过来。

❻ Java网络编程从入门到精通(33):非阻塞I/O的缓冲区(Buffer)

如果将同步I/O方式下的数据传输比做数据传输的零星方式(这里的零星是指在数据传输的过程中是以零星的字节方式进行的) 那么就可以将非阻塞I/O方式下的数据传输比做数据传输的集装箱方式(在字节和低层数据传输之间 多了一层缓冲区 因此 可以将缓冲区看做是装载字节的集装箱) 大家可以想象 如果我们要运送比较少的货物 用集装箱好象有点不太合算 而如果要运送上百吨的货物 用集装箱来运送的成本会更低 在数据传输过程中也是一样 如果数据量很小时 使用同步I/O方式会更适合 如果数据量很大时(一般以G为单位) 使用非阻塞I/O方式的效率会更高 因此 从理论上说 数据量越大 使用非阻塞I/O方式的单位成本就会越低 产生这种结果的原因和缓冲区的一些特性有着直接的关系 在本节中 将对缓冲区的一些主要特性进行讲解 使读者可以充分理解缓冲区的概念冲森 并能通过缓冲区来提高程序的执行效率

创建缓冲区

Java提供了七个基本的缓冲区 分别由七个类来扰判拍管理 它们都可以在java nio包中找到 这七个类如下所示

ByteBuffer

ShortBuffer

IntBuffer

CharBuffer

FloatBuffer

DoubleBuffer

LongBuffer

这七个类中的方法类似 只是它们的返回值或参数和相应的简单类型相对应 如ByteBuffer类的get方法返回了byte类型的数据 而put方法需要一个byte类型的参数 在CharBuffer类中的get和put方法返回和传递的数据类型就是char 这七个类都没有public构造方法 因此 它们不能通过new来创建相应的对象实例 这些类都可以通过两种方式来创建相应的对象实例

通过静态方法allocate来创建缓冲区

这七类都有一个静态的allocate方法 通过这个方法可以创建有最大容量限制的缓冲区对象 allocate的定义如下

ByteBuffer类中的allocate方法

(intcapacity)

IntBuffer类中的allocate方法

publicstaticIntBufferallocate(intcapacity)

其他五个缓冲区类中的allocate 方法定义和上面的定义类似 只是返回值的类型是相应的缓冲区缓羡类

allocate方法有一个参数capacity 用来指定缓冲区容量的最大值 capacity的不能小于 否则会抛出一个IllegalArgumentException异常 使用allocate来创建缓冲区 并不是一下子就分配给缓冲区capacity大小的空间 而是根据缓冲区中存储数据的情况来动态分配缓冲区的大小(实际上 在低层Java采用了数据结构中的堆来管理缓冲区的大小) 因此 这个capacity可以是一个很大的值 如 * ( M) allocate的使用方法如下

ByteBufferbyteBuffer=ByteBuffer allocate( );IntBufferintBuffer=IntBuffer allocate( );

在使用allocate创建缓冲区时应用注意 capacity的含义随着缓冲区的不同而不同 如创建字节缓冲区时 capacity指的是字节数 而在创建整型(int)缓冲区时 capacity指的是int型值的数目 如果转换成字数 capacity的值应该乘 如上面代码中的intBuffer缓冲区最大可容纳的字节数是 * = 个

通过静态方法wrap来创建缓冲区

使用allocate方法可以创建一个空的缓冲区 而wrap方法可以利用已经存在的数据来创建缓冲区 wrap方法可以将数组直接转换成相应类型的缓冲区 wrap方法有两种重载形式 它们的定义如下

ByteBuffer类中的wrap方法

publicstaticByteBufferwrap(byte[]array)publicstaticByteBufferwrap(byte[]array intoffset intlength)

IntBuffer类中的wrap方法

publicstaticIntBufferwrap(byte[]array)publicstaticIntBufferwrap(byte[]array intoffset intlength)

其他五个缓冲区类中的wrap 方法定义和上面的定义类似 只是返回值的类型是相应的缓冲区类

在wrap方法中的array参数是要转换的数组(如果是其他的缓冲区类 数组的类型就是相应的简单类型 如IntBuffer类中的wrap方法的array就是int[]类型) offset是要转换的子数组的偏移量 也就是子数组在array中的开始索引 length是要转换的子数组的长度 利用后两个参数可以将array数组中的一部分转换成缓冲区对象 它们的使用方法如下

byte[]myByte=newbyte[]{ };int[]myInt=newint[]{ };ByteBufferbyteBuffer=ByteBuffer wrap(myByte);IntBufferintBuffer=IntBuffer wrap(myInt );

可以通过缓冲区类的capacity方法来得到缓冲区的大小 capacity方法的定义如下

publicfinalintcapacity()

如果使用allocate方法来创建缓冲区 capacity方法的返回值就是capacity参数的值 而使用wrap方法来创建缓冲区 capacity方法的返回值是array数组的长度 但要注意 使用wrap来转换array的字数组时 capacity的长度仍然是原数组的长度 如上面代码中的intBuffer缓冲区的capacity值是 而不是

除了可以将数组转换成缓冲区外 也可以通过缓冲区类的array方法将缓冲区转换成相应类型的数组 IntBuffer类的array方法的定义方法如下(其他缓冲区类的array的定义类似)

publicfinalint[]array()

下面的代码演示了如何使用array方法将缓冲区转换成相应类型的数组

int[]myInt=newint[]{ };IntBufferintBuffer=IntBuffer wrap(myInt );for(intv:intBuffer array()) System out print(v+ );

在执行上面代码后 我们发现输出的结果是 而不是 这说明在将子数组转换成缓冲区的过程中实际上是将整个数组转换成了缓冲区 这就是用wrap包装子数组后 capacity的值仍然是原数组长度的真正原因 在使用array方法时应注意 在以下两种缓冲区中不能使用array方法

只读的缓冲区如果使用只读缓冲区的array方法 将会抛出一个ReadOnlyBufferException异常

使用allocateDirect方法创建的缓冲区

如果调用这种缓冲区中的array方法 将会抛出一个UnsupportedOperationException异常

可以通过缓冲区类的hasArray方法来判断这个缓冲区是否可以使用array方法 如果返回true 则说明这个缓冲区可以使用array方法 否则 使用array方法将会抛出上述的两种异常之一

注意 使用array方法返回的数组并不是缓冲区数据的副本 被返回的数组实际上就是缓冲区中的数据 也就是说 array方法只返回了缓冲区数据的引用 当数组中的数据被修改后 缓冲区中的数据也会被修改 返之也是如此 关于这方面内容将在下一节 读写缓冲区中的数据 中详细讲解

在上述的七个缓冲区类中 ByteBuffer类和CharBuffer类各自还有另外一种方法来创建缓冲区对象

ByteBuffer类

可以通过ByteBuffer类的allocateDirect方法来创建ByteBuffer对象 allocateDirect方法的定义如下

Direct(intcapacity)

使用allocateDirect方法可以一次性分配capacity大小的连续字节空间 通过allocateDirect方法来创建具有连续空间的ByteBuffer对象虽然可以在一定程度上提高效率 但这种方式并不是平台独立的 也就是说 在一些操作系统平台上使用allocateDirect方法来创建ByteBuffer对象会使效率大幅度提高 而在另一些操作系统平台上 性能会表现得非常差 而且allocateDirect方法需要较长的时间来分配内存空间 在释放空间时也较慢 因此 在使用allocateDirect方法时应谨慎

通过isDirect方法可以判断缓冲区对象(其他的缓冲区类也有isDirect方法 因为 ByteBuffer对象可以转换成其他的缓冲区对象 这部分内容将在后面讲解)是用哪种方式创建的 如果isDirect方法返回true 则这个缓冲区对象是用allocateDirect方法创建的 否则 就是用其他方法创建的缓冲区对象

CharBuffer类

我们可以发现 上述的七种缓冲区中并没有字符串缓冲区 而字符串在程序中却是最常用的一种数据类型 不过不要担心 虽然java nio包中并未提供字符串缓冲区 但却可以将字符串转换成字符缓冲区(就是CharBuffer对象) 在CharBuffer类中的wrap方法除了上述的两种重载形式外 又多了两种重载形式 它们的定义如下

publicstaticCharBufferwrap(CharSequencecsq)publicstaticCharBufferwrap(CharSequencecsq intstart intend)

其中csq参数表示要转换的字符串 但我们注意到csq的类型并不是String 而是CharSequence CharSequence类Java中四个可以表示字符串的类的父类 这四个类是String StringBuffer StringBuilder和CharBuffer(大家要注意 StringBuffer和本节讲的缓冲区类一点关系都没有 这个类在java lang包中) 也就是说 CharBuffer类的wrap方法可以将这四个类的对象转换成CharBuffer对象

另外两个参数start和end分别是子字符串的开始索引和结束索引的下一个位置 如将字符串 中的 转换成CharBuffer对象的语句如下

CharBuffercb=CharBuffer wrap( );

下面的代码演示了如何使用wrap方法将不同形式的字符串转换成CharBuffer对象

lishixin/Article/program/Java/hx/201311/26505

阅读全文

与java非阻模式相关的资料

热点内容
使用单片机的理由 浏览:949
解压3d模型 浏览:82
智能家装app编程 浏览:189
linux关闭端口命令是什么啊 浏览:165
基于比较的排序算法有哪几种 浏览:419
怎么下载广东体育app 浏览:84
如何搭建华为云服务器 浏览:220
尘埃4几个压缩包 浏览:69
四缸压缩机结构图 浏览:246
卡通版解压顶 浏览:897
文件夹拉链不透明 浏览:922
建个自己的服务器地址 浏览:205
pythonfilerw 浏览:411
python怎么断点调试 浏览:521
软件和编程开发 浏览:828
苹果电脑压缩文件夹 浏览:828
压缩机不转导致熄火 浏览:722
抖音订单加密打不了单子 浏览:636
python量化书籍 浏览:603
cadicl命令怎么用 浏览:527