第三课 通道

第三课 通道

1 通道(Channel)

  • 通道(Channel):用于源节点与目标节点的连接。在Java NIO中负责缓冲区中数据的传输。Channel本身不存储数据,因此需要配合缓冲区进行传输。现代计算机中通道集成于专用的处理器中,专门用于处理IO请求,提高效率。

2 通道的主要实现类

  • java.nio.channels.Channel
    • FileChannel
    • SocketChannel
    • ServerSocketChannel
    • DatagramChannel

3 获取通道

  • Java针对支持通道的类提供了 getChannel() 方法

    • 本地IO
      • FileInputStream/FileOutputStream
      • RandomAccessFile
    • 网络IO
      • Socket
      • ServerSocket
      • DatagramSocket
  • 在 JDK1.7 中的 NIO.2 针对各个通道提供了静态方法 open()

  • 在 JDK1.7 中的 NIO.2 的Files工具类的newByteChannel()方法获取通道

package NIO;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class FileChannelTest {
	public static void main(String[] args) {
		try {
			test02();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/*
	 * 非直接缓冲区
	 */
	public static void test01() throws Exception {
		// 输入文件流
		FileInputStream fis = new FileInputStream("D:\\WorkSpace\\JavaBaseStu\\Notes\\99 Pic\\IO总结.jpg");
		// 输出文件流
		FileOutputStream fos = new FileOutputStream("D:\\WorkSpace\\JavaBaseStu\\Notes\\99 Pic\\复制.jpg");
		// 输入流管道
		FileChannel inChannel = fis.getChannel();
		// 输出流管道
		FileChannel outChannel = fos.getChannel();

		// 配合缓冲区进行文件的读写
		ByteBuffer dst = ByteBuffer.allocate(1024);
		// 循环读入写入文件
		while (inChannel.read(dst) != -1) { // 从输入管道中获取缓冲区,并读取其中的数据
			dst.flip(); // 转换成写入模式
			outChannel.write(dst); // 写入缓冲区,并加载到输出管道中
			dst.clear(); // 清空缓冲区中的数据,重新从输入管道中缓冲区获取数据
		}

		// 关闭资源
		fis.close();
		fos.close();
		inChannel.close();
		outChannel.close();
	}

	/*
	 * 直接缓冲区(内存映射文件,不需要缓冲区)
	 */
	public static void test02() throws Exception {
		long start = System.currentTimeMillis();
		
		// 获取输入管道
		FileChannel inChannel = FileChannel.open(Paths.get("E:\\Yorick\\01 学习\\01 基础阶段\\01 Java基础\\课件笔记源码资料.zip"), StandardOpenOption.READ);
		// 获取输出管道
		FileChannel outChannel = FileChannel.open(Paths.get("E:\\Yorick\\01 学习\\01 基础阶段\\01 Java基础\\复制.zip"), StandardOpenOption.READ, StandardOpenOption.WRITE,
				StandardOpenOption.CREATE);
		MappedByteBuffer inMap = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
		MappedByteBuffer outMap = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());

		byte[] dst = new byte[inMap.limit()];
		inMap.get(dst);
		outMap.put(dst);

		inChannel.close();
		outChannel.close();
		long end = System.currentTimeMillis();
		System.out.println(end = start);
	}
}

4 通道之间的数据传输(直接缓冲区)

  • 通道与通道之间的数据传输主要有以下两个方法
    • transferForm()
    • transferTo()
/*
 * 缓冲区间通信
 */
public static void test03() throws Exception {
	FileChannel inChannel = FileChannel.open(Paths.get("D:\\WorkSpace\\JavaBaseStu\\Notes\\99 Pic\\IO总结.jpg"),
			StandardOpenOption.READ);
	FileChannel outChannel = FileChannel.open(Paths.get("D:\\WorkSpace\\JavaBaseStu\\Notes\\99 Pic\\复制.jpg"),
			StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);

	outChannel.transferFrom(inChannel, 0, inChannel.size());
}

5 分散(Scatter)与聚集(Gather)

  • 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中,按照缓冲区的顺序,从Channel中读取的数据一次将Buffer填满
  • 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中,按照缓冲区的顺序,写入position和limit之间的数据到Channel
/*
 * 分散读取及聚集写入
 */
public static void test04() throws Exception {
	FileChannel inChannel = FileChannel.open(Paths.get("D:\\1.txt"), StandardOpenOption.READ);
	FileChannel outChannel = FileChannel.open(Paths.get("D:\\2.txt"), StandardOpenOption.READ, StandardOpenOption.WRITE,
			StandardOpenOption.CREATE);

	// 分散读取
	ByteBuffer buffer_1 = ByteBuffer.allocate(100);
	ByteBuffer buffer_2 = ByteBuffer.allocate(1024);
	ByteBuffer[] buffers = { buffer_1, buffer_2 };
	
	// 将数据读取到两个缓冲区中
	inChannel.read(buffers);
	
	
	// 转换缓冲区的读写模式
	for (ByteBuffer buffer : buffers) {
		buffer.flip();
	}

	byte[] dst = new byte[buffer_2.limit()];
	buffer_2.get(dst);
	System.out.println(new String(dst));
	
	// 聚集写入
	outChannel.write(buffers);

	inChannel.close();
	outChannel.close();
}

6 字符集(Charset)

  • 编码和解码的使用
public static void test06() throws Exception {
	Charset charSetGBK = Charset.forName("GBK");
	// 编码器
	CharsetEncoder GBKEncoder = charSetGBK.newEncoder();
	// 解码器
	CharsetDecoder GBKDecoder = charSetGBK.newDecoder();

	// 创建缓冲区
	CharBuffer inBuffer = CharBuffer.allocate(100);
	// 加载字符串
	inBuffer.put("编码器编码的字符串");
	// 将缓冲区(inBuffer)转换为读取模式
	inBuffer.flip();
	// 编码,此时编码缓冲区(encodeBuffer)为写入模式
	ByteBuffer encodeBuffer = GBKEncoder.encode(inBuffer);
	// 输出编码结果
	System.out.print("编码结果:");
	for (int i = 0; i < encodeBuffer.limit(); i++) {
		System.out.print(encodeBuffer.get() + "\t");
	}
	System.out.println();

	// 将编码缓冲区(encodeBuffer)转换为读取模式,并进行解码
	encodeBuffer.flip();
	CharBuffer decoderBuffer = GBKDecoder.decode(encodeBuffer);
	for (int i = 0; i < decoderBuffer.limit(); i++) {
		System.out.print(decoderBuffer.get());
	}
	System.out.println();
	
	// 用UTF-8对编码缓冲区进行解码
	encodeBuffer.flip();
	Charset UTF8Encoder = Charset.forName("UTF-8");
	CharBuffer decoderBufferUTF8 = UTF8Encoder.decode(encodeBuffer);
	for (int i = 0; i < decoderBufferUTF8.limit(); i++) {
		System.out.print(decoderBufferUTF8.get());
	}
}
  • 查看支持的字符集
/*
 * Charset支持的字符集
 */
public static void test05() {
	SortedMap<String, Charset> availableCharsetMap = Charset.availableCharsets();
	Set<String> keySet = availableCharsetMap.keySet();
	for (String tmp : keySet) {
		Charset charset = availableCharsetMap.get(tmp);
		System.out.println(tmp + " = " + charset);
	}
}

7 FileChannel的常用方法

方法描述
int read(ByteBuffer dst)从Channel 中读取数据到ByteBuffer
longread(ByteBuffer[] dsts)将Channel 中的数据“分散”到ByteBuffer[]
intwrite(ByteBuffer src)将ByteBuffer 中的数据写入到Channel
long write(ByteBuffer[] srcs)将ByteBuffer[] 中的数据“聚集”到Channel
longposition()返回此通道的文件位置
FileChannelposition(long p)设置此通道的文件位置
long size()返回此通道的文件的当前大小
FileChanneltruncate(long s)将此通道的文件截取为给定大小
voidforce(boolean metaData)强制将所有对此通道的文件更新写入到存储设备中

8 通道的原理(了解)

8.1 早期应用程序与计算机底层通信原理

应用程序和计算机底层硬件之间不能直接进行通信,需要使用底层硬件向上层操作系统提供的IO接口进行读写操作,应用程序将数据写入用户地址空间,并从用户地址空间中读取底层硬件运算好的数据。
用户地址空间会和内核地址空间进行数据交互,内核地址空间再和底层硬件进行交互,从而完成数据的读取操作。如下图展示的早期计算机,
这样做的缺点是:由CPU统一管理底层硬件的IO接口,这样会使得CPU的占用路较高,本能很好的发挥CPU的运算效率。

早期计算机由CPU统一管理IO接口

8.2 近代计算机应用程序与计算机底层通信原理

近代计算机不再使用CPU统一管理IO接口,加入了DMA总线(直接存储器)。DMA总线辅助CPU管理IO接口并与CPU等底层硬件进行数据传输
当应用程序向底层硬件发出读写请求后,内存会向CPU发出申请建立DMA总线,该总线一旦建立成功,则所有的IO操作全权由DMA总线进行控制。
这样做的缺点是:当有大量的读写请求发送到底层硬件时,内存会大量向CPU申请DMA总线,申请需要时间,从而造成了总线拥堵的情况,DMA总线依旧需要依赖CPU的指示。

近代计算机由DMA总线管理IO接口

8.3 现代计算机应用程序与计算机底层通信原理

现代计算机引入了通道的概念,是一个完全独立的处理器,专门用于处理IO请求,不再需要向CPU申请权限了,减少了与CPU之间的通信,提高了IO读取的性能

现代计算机由独立的通道处理器处理IO请求

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值