JVM的多线程通常是不依赖于操作系统来实现的;JVM自动分配线程到os,os自动分配线程到CPU;
对单核CPU而言,多线程主要用在出现IO阻塞时才能提高效率,否则它会比单线程慢(多线程切换);
CPU密集型:顾名思义就是应用需要非常多的CPU计算资源,在多核CPU时代,我们要让每一个CPU核心都参与计算,将CPU的性能充分利用起来,这样才算是没有浪费服务器配置,如果在非常好的服务器配置上还运行着单线程程序那将是多么重大的浪费。对于计算密集型的应用,完全是靠CPU的核数来工作,所以为了让它的优势完全发挥出来,避免过多的线程上下文切换,比较理想方案是:线程数 = CPU核数+1,也可以设置成CPU核数*2,这还是要看JDK的使用版本,以及CPU配置(服务器的CPU有超线程)。对于JDK1.8来说,里面增加了一个并行计算,计算密集型的较理想线程数 = CPU内核线程数*2;
IO密集型:对于IO密集型的应用,就很好理解了,我们现在做的开发大部分都是WEB应用,涉及到大量的网络传输,不仅如此,与数据库,与缓存间的交互也涉及到IO,一旦发生IO,线程就会处于等待状态,当IO结束,数据准备好后,线程才会继续执行。因此从这里可以发现,对于IO密集型的应用,我们可以多设置一些线程池中线程的数量,这样就能让在等待IO的这段时间内,线程可以去做其它事,提高并发处理效率;
io流
Java的IO操作中有面向字节(Byte)和面向字符(Character)两种方式。
面向字节的操作为以8位为单位对二进制的数据进行操作,对数据不进行转换,这些类都是InputStream和OutputStream的子类。
面向字符的操作为以字符为单位对数据进行操作,在读的时候将二进制数据转为字符,在写的时候将字符转为二进制数据,这些类都是Reader和Writer的子类。
以InputStream(输入)/OutputStream(输出)为后缀的是字节流;
以Reader(输入)/Writer(输出)为后缀的是字符流。
读取ByteBuffer有效的数据
说道 ByteBuffer的缓冲区,就需要知道缓冲区的的三个状态:
1)capacit,即容量,也就是缓冲区的大小。它的值是在创建缓冲区的时候指定的,设定后无法对其进行修改。
2)limit,读写限制,也就是缓冲区可以利用(进行读写)的范围的最大值,可以调用ByteBuffer对象的limit(int)方法进行设置。调用ByteBuffer对象的limit()获取缓冲区读写限制的大小;在刚开始创建缓冲区的时候limit的大小等于capacity。
3)position 读写位置,表示当前进行读写操作时的位置,初始化为0,即缓冲区开始读或者写数据的位置。可以通过调用ByteBuffer对象的positon(int)方法设置它的值,通过position()可以获取它的值。
这三个状态的数值满足一下关系:0≤position≤limit≤capacity
在初始化缓冲区的情况下,position=0;limit = capacity
缓冲区是通过put或者putXXX来向缓冲区添加数据,也可以通过put(index,vlaue)方法在指定的position和limit之间指定的地方添加数据;
通过get()或者getXXX()来获取数据,也可以通过get(int index)方法来获取position到limit之间指定的数据。
注意position的位置会随着数据的写入或者读取而移动(比如添加一个byte类型的数据position会+1,添加一个int类型的数据position会+4),直到position=limit为止。如果position==limit时,再添加数据会报java.nio.BufferOverflowException、再获取数据会报java.lang.IndexOutOfBoundsException。
正因为向缓冲区写数据的时候position会变化,如果想获取缓冲区的数据直接调用get等相关方法获取数据是肯定不行的,因为此时数据在缓冲区存在数据的部分是0~~position之间,而读数据开始的位置position在此区间之外。所以要获取此区间的数据需要重新设置position的位置,ByteBuffer提供了一个flip方法,该方法主要进行两步操作 limit = position;(将limit设为当前position) position =0; 此时通过get方法就可以获取到0到limit之间的数据了