单例模式和NIO

1. 单例模式

1.1 要求

当前类有且只有一个对象,一旦当前类存在一个对象之 后,无法在重新创建当前类的对象。就算是你要创建,代码返 回的对象依然是上一次创建的对象。

解决方案:将构造函数私有化 ,在类中实例化对象用static修饰,并向外提供一个开放的方法来获取该实例。

1.2懒汉式

public class Singleton {

    private static Singleton instance = null;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

}

懒汉式采用懒加载,但是该写法存在存在致命缺陷,线程不安全,如果多线程抢夺资源很有可能出现多个实例的bug。
解决方案:加锁

1.2.1同步方法
public class Demo1 {
    private static Demo1 demo1 = null ;

    private Demo1() {
    }
    /**
     *  同步方法实现单例模式
     */
    public static synchronized Demo1 getInstance() {
        if (null == demo1) {
            demo1 = new Demo1();
        }
        return demo1;
    }
}
1.2.2同步代码块
public class Demo2 {
    private static Demo2 demo2 = null ;

    private Demo2() {
    }
    public static Demo2 getInstance() {
        /**
         *  同步代码块实现单例
         */
        synchronized (Demo2.class) {
            if (null == demo2) {
                demo2 = new Demo2();
            }
        }
        return demo2;
    }
}

1.3饥汉式

直接在方法外实例化对象,方法只做返回操作。
缺陷:该方法虽然可以实现单例,但是在加载字节码文件时就new了对象,如果以后没有调用该对象就存在资源浪费问题。

public class Demo3 {
    private static final Demo3 demo3 = new Demo3() ;

    private Demo3() {
    }
    public static Demo3 getInstance() {
        return demo3;
    }
}

2.NIO

2.1 BIO概述

BIO
BIO ==> Basic IO (基本IO), Block IO(阻塞IO) Scanner操作,文件读写操作,Socket数据传输操作 … 都是BIO
比如TPC群聊,私聊聊天室
Socket涉及到的IO,也是BIO
资源浪费:
1. 多线程,每一个Socket会对应一个线程, 如果用户量巨大,会导致线程过多,资源处理过多 2. 采用阻塞状态,一旦进入阻塞,代码无法执行其他操作。
3. 承载量一般,吞吐量比较小,同时可靠性不佳

2.2 NIO概述

NIO
NIO ==> New IO(新IO), Non-Block IO(非阻塞IO) NIO非阻塞IO,运行当前程序在处理IO事务时 ,不会影响 其他程序的运行,可以在不使用多线程的情况下,满足IO操作要求。
三大核心部分:
通道
Channel
文件操作,网络数据传递操作使用的通道
缓冲
Buffer
缓冲使用可以提供操作效率,减少不必要的读 写次数
选择器
Selector
真·核心 老大 boss

2.3 Buffer Channel完成文件操作

2.3.1 常用API
java.nio.Buffer
Buffer缓冲区
ByteBuffer 字节缓冲 常用
ShortBuffer
IntBuffer
LongBuffer
CharBuffer 字节缓冲 常用
FloatBuffer
DoubleBuffer
常用方法:
public static ByteBuffer allocate(int capacity);
按照指定的字节数分配对应的缓冲区空间,保存字节数据
public byte get();
从字节缓冲区对象中读取一个byte类型数组
public final Buffer flip();
翻转缓冲区,回到缓冲区的开始位置。
public static ByteBuffer wrap(byte[] arr);
存入一个byte类型数组到缓冲区,会得到一个新的 ByteBuffer
public static ByteBuffer put(byte[] b);
将字节数组存入缓冲去
Channel接口,通道接口
FileChannel 文件操作通道
DatagramChannel UDP协议数据包操作的Channel
ServerSocketChannel TCP服务端ServerSocket对应
Channel
SocketChannel TCP客户端Socket对应Channel 首先操作文件,以FileChannel为例
public long read(ByteBuffer buffer);
从通道中读取数据到ByteBuffer中
public long write(ByteBuffer buffer);
从Buffer中写数据到通道中
public long transferFrom(ReadableByteChannel src, long position, long count)
从指定srcChannel中,指定位置position开始,读取 count个元素,到当前通道中文件复制操作。 public long transferTo(long position, long count, WritableByteChannel target)
将当前通道中的数据写入到target中,从当前通道的 position位置开始,计数count
NIO对文件的读写例子

package com.my.demo1;

import org.junit.Test;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class Demo4 {
    @Test
    public void testNioFileWrite() throws IOException {
        FileOutputStream fos = new FileOutputStream("D:/AAA/1.txt");
        FileChannel foc = fos.getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(1024 * 4);
        String str = "测试NIO";
        buffer.put(str.getBytes());
        buffer.flip();
        foc.write(buffer);
        fos.close();
    }
    @Test
   public void testNioFileReader() throws IOException {
       FileInputStream fis = new FileInputStream("D:/AAA/1.txt");
       FileChannel fileChannel = fis.getChannel();
       ByteBuffer buffer = ByteBuffer.allocate(4*1024);
       int count = fileChannel.read(buffer);
       System.out.println(count);
        System.out.println(new String(buffer.array() , 0 ,count));
        fis.close();
   }
   @Test
   public void testNioCopyFile() throws IOException {
       long start = System.currentTimeMillis();
       FileInputStream fis = new FileInputStream("D:/AAA/1.txt");
       FileOutputStream fos = new FileOutputStream("D:/AAA/1copy.txt") ;
       FileChannel fileChannel1 = fis.getChannel();
       FileChannel fileChannel2 = fos.getChannel();
       fileChannel1.transferTo(0 , fileChannel1.size() , fileChannel2);
       fos.close();
       fis.close();
       long end = System.currentTimeMillis();
       System.out.println(end - start);
   }
   @Test
   public void testUseBufferCopy() throws IOException {
       long start = System.currentTimeMillis();
       BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:/AAA/1.txt"));
       BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:/AAA/1copy2.txt"));
       int length = -1 ;
       byte[] bytes = new byte[1024] ;
       while ((length = bis.read(bytes)) != -1) {
           bos.write(bytes , 0  , length);
       }
       bos.close();
       bis.close();
       long end = System.currentTimeMillis();
       System.out.println(end - start);
   }
}

2.4 网络编程使用NIO【重点】

2.4.1 Selector选择器老大

Selector
选择器,网络编程使用NIO的大哥!!!
服务器可以执行一个线程,运行Selector程序,进行监听操作。
新连接, 已经连接, 读取数据,写入数据
Selector常用方法:
public static Selector Open();
得到一个选择器对象
public int select(long timeout);
监听所有注册通道,存在IO流操作是,会将对应的 信息SelectionKey存入到内部的集合中,参数是 一个超时时间
public Set selectionKeys();
返回当前Selector内部集合中保存的所有 SelectionKey

2.4.2 SelectionKey

SelectionKey
表示Selector和网络通道直接的关系
int OP_ACCEPT; 16 需要连接
int OP_CONNECT; 8 已经连接
int OP_READ; 1 读取操作
int OP_WRITE; 4 写入操作
SelectionKey
public abstract Selector selector(); 得到与之关联的 Selector 对象
public abstract SelectableChannel channel(); 得到与之关联的通道
public final Object attachment(); 得到与之关联的共享数据
public abstract SelectionKey interestOps(int ops); 设置或改变监听事件
public final boolean isAcceptable(); 是否可以 accept
public final boolean isReadable(); 是否可以读
public final boolean isWritable(); 是否可以写

2.4.3 ServerSocketChannel

ServerSocketChannel 服务端Socket程序对应的Channel通道
常用方法:
public static ServerSocketChannel open();
开启服务器ServerSocketChannel通道,等于开始 服务器程序
public final ServerSocketChannel bind(SocketAddress local);
设置服务器端端口号
public final SelectableChannel configureBlocking(boolean block);
设置阻塞或非阻塞模式, 取值 false 表示采用非 阻塞模式
public SocketChannel accept();
[非阻塞] 获取一个客户端连接,并且得到对应的操作通 道
public final SelectionKey register(Selector sel, int ops);
[重点方法] 注册当前选择器,并且选择监听什么事件

2.4.4 SocketChannel

SocketChannel 客户端Socket对应的Channel对象
常用方法:
public static SocketChannel open();
打卡一个Socket客户端Channel对象
public final SelectableChannel configureBlocking(boolean block)
这里可以设置是阻塞状态,还是非阻塞状态
false,表示非阻塞
public boolean connect(SocketAddress remote);
连接服务器
public boolean finishConnect();
如果connect连接失败,可以通过finishConnect 继续连接
public int write(ByteBuffer buf);
写入数据到缓冲流中
public int read(ByteBuffer buf);
从缓冲流中读取数据
public final SelectionKey register(Selector sel, int ops, Object attechment);
注册当前SocketChannel,选择对应的监听操作, 并且可以带有Object attachment参数
public final void close(); 关闭SocketChannel

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值