NIO — 通道间互传数据

transferFrom:

public void transferFrom(){
		RandomAccessFile fromFile;
		try {
			fromFile = new RandomAccessFile("E:\\study\\new.txt", "rw");
			FileChannel fromChannel = fromFile.getChannel();
			
			RandomAccessFile toFile = new RandomAccessFile("E:\\study\\output2.txt", "rw");
			FileChannel toChannel = toFile.getChannel();
			
			toChannel.transferFrom(fromChannel, 0, fromChannel.size());
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

 
position表示从position处开始向目标文件写入数据,count表示最多传输的字节数。如果源通道的剩余空间小于 count 个字节,则所传输的字节数要小于请求的字节数。

transferTo:

public void transferTo(){
		RandomAccessFile fromFile;
		try {
			fromFile = new RandomAccessFile("E:\\study\\new.txt", "rw");
			FileChannel fromChannel = fromFile.getChannel();
			
			RandomAccessFile toFile = new RandomAccessFile("E:\\study\\output3.txt", "rw");
			FileChannel toChannel = toFile.getChannel();
			
			fromChannel.transferTo(0, fromChannel.size(), toChannel);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

NIO — Scatter & Gather

分散(scatter):从Channel中读取是指在读操作时将读取的数据写入多个buffer中
4
scatter / gather经常用于需要将传输的数据分开处理的场合,例如传输一个由消息头和消息体组成的消息,你可能会将消息体和消息头分散到不同的buffer中,这样你可以方便的处理消息头和消息体。

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body   = ByteBuffer.allocate(1024);
 
ByteBuffer[] bufferArray = { header, body };
 
channel.read(bufferArray);

会依次写入header和body两个buffer,写完第一个buffer,才会写第二个buffer(意味着它不适用于动态消息)

聚集(gather):写操作时将多个buffer的数据写入同一个Channel
5

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body   = ByteBuffer.allocate(1024);
write()方法会按照buffer在数组中的顺序,将数据写入到channel,注意只有position和limit之间的数据才会被写入。因此,如果一个buffer的容量为128byte,但是仅仅包含58byte的数据,那么这58byte的数据将被写入到channel中。因此与Scattering Reads相反,Gathering Writes能较好的处理动态消息。
 
//write data into buffers
 
ByteBuffer[] bufferArray = { header, body };
 
channel.write(bufferArray);

NIO — Buffer

Buffer(缓冲区)是一块用来读写的内存,包装成了Buffer对象,提供了api方便访问。
Buffer读写的通常步骤:

写入数据到Buffer
调用flip()方法
从Buffer中读取数据
调用clear()方法或者compact()方法

当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式。在读模式下,可以读取之前写入到buffer的所有数据。

清空缓存区clear(),compact()区别:
clear()
方法会清空整个缓冲区。
position将被设回0,limit被设置成capacity的值。
如果Buffer中有一些未读的数据,调用clear()方法,数据将“被遗忘”,意味着不再有任何标记会告诉你哪些数据被读过,哪些还没有。
compact()
方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。
position设到最后一个未读元素正后面。limit属性依然像clear()方法一样,设置成capacity。
如果Buffer中仍有未读的数据,且后续还需要这些数据,但是此时想要先先写些数据,那么使用compact()方法。

Buffer的capacity,position和limit:
3
Capacity: 就是只Buffer里面的容量、内存块,里面可以装的byte、long,char的数目,一旦满了需要清空才可以重新写入,如就是ByteBuffer.allocate(1024)这个1024就是他的容量。
Position:
写数据到Buffer中时,position表示当前的位置。初始的position值为0.当一个byte、long等数据写到Buffer后, position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity – 1.
读取数据时,也是从某个特定位置读。当将Buffer从写模式切换到读模式,position会被重置为0. 当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。
Limit:
写模式下就是capacity,表示一共能写多少
读模式下就是position,表示已经读了多少了

Buffer的实例化和空间分配:

ByteBuffer buffer = ByteBuffer.allocate(1024);

写入Buffer:
(1)从channel写入Buffer:
int r = inFc.read(buffer);

(2)Buffer的put()
	buf.put(127);

flip()
flip方法将Buffer从写模式切换到读模式。调用flip()方法会将position设回0,并将limit设置成之前position的值。

读取Buffer:
(1)从Buffer读取数据到Channel。

outFc.write(buffer);

(2)Buffer的get()
byte aByte = buf.get();

rewind()
Buffer.rewind()将position设回0,可以重读Buffer中的所有数据。limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)。

make();reset()
类似于数据库的回滚功能,Buffer.mark()方法,可以标记Buffer中的一个特定position。之后可以通过调用Buffer.reset()方法恢复到这个position。

equals();compareTo()
equals()条件:

有相同的类型(byte、char、int等)。
Buffer中剩余的byte、char等的个数相等。
Buffer中所有剩余的byte、char等值都相同。
所以equals不比较所有元素,只是比较剩余元素

compareTo(认为一个Buffer“小于”另一个Buffer:)条件:

第一个不相等的元素小于另一个Buffer中对应的元素 。
所有元素都相等,但第一个Buffer比另一个先耗尽(第一个Buffer的元素个数比另一个少)。

NIO – Channel

特性:
1. 可从通道读取到buffer,也可以从buffer写到通道。
2. 异步读写

类型与协议:
FileChannel — File
DatagramChannel — UDP读写网络中的数据
SocketChannel — TCP读写网络中的数据
ServerSocketChannel — 监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel

public class NIOFileCopy {
	public void copyFile(){
		try {
			FileInputStream is = new FileInputStream("E:\\study\\yuan.txt");
			FileOutputStream os = new FileOutputStream("E:\\study\\output.txt");
			
			FileChannel inFc = is.getChannel();
			FileChannel outFc = os.getChannel();
			
			ByteBuffer buffer = ByteBuffer.allocate(1024);
			
			while(true){
				buffer.clear();
				int r = inFc.read(buffer);
				if(r == -1){
					break;
				}
				buffer.flip();
				outFc.write(buffer);
				
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		NIOFileCopy nioFileCopy = new NIOFileCopy();
		nioFileCopy.copyFile();
	}
}

NIO基本概念

Java NIO 由以下几个核心部分组成:

Channels
Buffers
Selectors
其他没那么常用的组件:Pipe和FileLock

Channel 和 Buffer
所有的 IO 在NIO 中都从一个Channel 开始。Channel 有点象流。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中
1

Channel类型:

FileChannel
DatagramChannel
SocketChannel
ServerSocketChannel

Buffer类型:

ByteBuffer
CharBuffer
DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer
MappedByteBuffer
Selector:
Selector允许单线程处理多个 Channel,如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便
一个单线程中使用一个Selector处理3个Channel的图示
2
要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等

多线程 start()和run()的区别

1.start()方法来启动线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码;通过调用Thread类的start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。 然后通过此Thread类调用方法run()来完成其运行操作的, 这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程。
2.run()方法当作普通方法的方式调用。程序还是要顺序执行,要等待run方法体执行完毕后,才可继续执行下面的代码; 程序中只有主线程——这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的。

循环start多个线程,真实线程执行不一定按照顺序,start表示就绪,真实的run是由cpu来控制。

面试提纲 Scrum

Scrum:

1. 角色

    Product owner, scrum master, team(每个角色的职责)

2. 会议

    启动会议, 故事时间会议,15分钟站立式会议,演示会议,sprint回顾会议

3. 产品

    Product backlog, sprint backlog, burn down chart

4. 特征

    (1)线型和非线性

    (2)文档

    (3)客户与开发的交互

    (4)拥抱变化

    (5)软件功能使用率

    (6)根据优先级制定sprint故事而不是成员的擅长技能

    (7)scrum通常人数7+-2 、,sprint周期2-3周

    (8)Sprint team的工作速率的自动调整

    (9)故事点值的计算规则

    (10)

垃圾回收机制原理

Java垃圾回收机制Java的垃圾回收器要负责完成3件任务:分配内存、确保被引用的对象的内存不被错误回收以及回收不再被引用的对象的内存空间。垃圾回收是一个复杂而且耗时的操作。如果JVM花费过多的时间在垃圾回收上,则势必会影响应用的运行性能。一般情况下,当垃圾回收器在进行回收操作的时候,整个应用的执行是被暂时中止(stop-the-world)的。这是因为垃圾回收器需要更新应用中所有对象引用的实际内存地址。不同的硬件平台所能支持的垃圾回收方式也不同。比如在多CPU的平台上,就可以通过并行的方式来回收垃圾。而单CPU平台则只能串行进行。不同的应用所期望的垃圾回收方式也会有所不同。服务器端应用可能希望在应用的整个运行时间中,花在垃圾回收上的时间总数越小越好。而对于与用户交互的应用来说,则可能希望所垃圾回收所带来的应用停顿的时间间隔越小越好。对于这种情况,JVM中提供了多种垃圾回收方法以及对应的性能调优参数,应用可以根据需要来进行定制。Java 垃圾回收机制最基本的做法是分代回收。内存中的区域被划分成不同的世代,对象根据其存活的时间被保存在对应世代的区域中。一般的实现是划分成3个世代:年轻、年老和永久。内存的分配是发生在年轻世代中的。当一个对象存活时间足够长的时候,它就会被复制到年老世代中。对于不同的世代可以使用不同的垃圾回收算法。进行世代划分的出发点是对应用中对象存活时间进行研究之后得出的统计规律。一般来说,一个应用中的大部分对象的存活时间都很短。比如局部变量的存活时间就只在方法的执行过程中。基于这一点,对于年轻世代的垃圾回收算法就可以很有针对性。

年轻世代的内存区域被进一步划分成伊甸园(Eden)和两个存活区(survivor space)。伊甸园是进行内存分配的地方,是一块连续的空闲内存区域。在上面进行内存分配速度非常快,因为不需要进行可用内存块的查找。两个存活区中始终有一个是空白的。在进行垃圾回收的时候,伊甸园和其中一个非空存活区中还存活的对象根据其存活时间被复制到当前空白的存活区或年老世代中。经过这一次的复制之后,之前非空的存活区中包含了当前还存活的对象,而伊甸园和另一个存活区中的内容已经不再需要了,只需要简单地把这两个区域清空即可。下一次垃圾回收的时候,这两个存活区的角色就发生了交换。一般来说,年轻世代区域较小,而且大部分对象都已经不再存活,因此在其中查找存活对象的效率较高。而对于年老和永久世代的内存区域,则采用的是不同的回收算法,称为“标记-清除-压缩(Mark-Sweep-Compact)”。标记的过程是找出当前还存活的对象,并进行标记;清除则遍历整个内存区域,找出其中需要进行回收的区域;而压缩则把存活对象的内存移动到整个内存区域的一端,使得另一端是一块连续的空闲区域,方便进行内存分配和复制。JDK 5中提供了4种不同的垃圾回收机制。最常用的是串行回收方式,即使用单个CPU回收年轻和年老世代的内存。在回收的过程中,应用程序被暂时中止。回收方式使用的是上面提到的最基本的分代回收。串行回收方式适合于一般的单CPU桌面平台。如果是多CPU的平台,则适合的是并行回收方式。这种方式在对年轻世代进行回收的时候,会使用多个CPU来并行处理,可以提升回收的性能。并发标记-清除回收方式适合于对应用的响应时间要求比较 高的情况,即需要减少垃圾回收所带来的应用暂时中止的时间。这种做法的优点在于可以在应用运行的同时标记存活对象与回收垃圾,而只需要暂时中止应用比较短的时间。
通过JDK中提供的JConsole可以很容易的查看当前应用的内存使用情况。在JVM启动的时候添加参数 -verbose:gc 可以查看垃圾回收器的运行结果。