BIO:即为同步阻塞式编程,最常用的就是Socket。
先从最简单的例子开始:
客户端与服务端进行通信
代码实现:
//服务端
public class Service {
public static void main(String[] args) {
try {
ServerSocket serversocket = new ServerSocket(8888);
Socket socket=serversocket.accept();
InputStream in = socket.getInputStream();
InputStreamReader read = new InputStreamReader(in);
BufferedReader br = new BufferedReader(read);
String info=null;
while((info=br.readLine())!=null) {
System.out.println(info);
}
socket.shutdownInput();
OutputStream out = socket.getOutputStream();
PrintWriter pw = new PrintWriter(out);
pw.write("哈哈哈");
pw.flush();
socket.shutdownOutput();
in.close();
br.close();
read.close();
pw.close();
out.close();
socket.close();
serversocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//用户端
public class Client {
public static void main(String[] args) {
try {
Socket socket = new Socket("localhost",8888);
OutputStream out = socket.getOutputStream();
PrintWriter pw =new PrintWriter(out);
pw.write("嘿嘿嘿");
pw.flush();
socket.shutdownOutput();
InputStream in = socket.getInputStream();
InputStreamReader read = new InputStreamReader(in);
BufferedReader bf = new BufferedReader(read);
String info=null;
while((info=bf.readLine())!=null) {
System.out.println(info);
}
socket.shutdownInput();
bf.close();
read.close();
in.close();
pw.close();
out.close();
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
说明:服务端监听一个地址,通accept()方法一直进行等待。直到有客户端进行连接。然后开始通过IO进行通信。这样一次通信就完成了。
缺点:如果我有多个用户请求连接服务端,并且IO操作时非常费时的,这样就会造成大量用户等待。
解决方法:通过采用多线程的方式去进行IO操作。
//服务端
public class Sercice {
public static void main(String[] args) throws IOException {
Socket socket;
ServerSocket service = new ServerSocket(8888);
while(true) {
socket=service.accept();
new ThreadService(socket).start();
}
}
}
//服务端IO处理
public class ThreadService extends Thread {
private Socket socket = null;
public ThreadService(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
InputStream in = null;
InputStreamReader ir = null;
BufferedReader br = null;
PrintWriter pw =null;
OutputStream out =null;
try {
in = socket.getInputStream();
ir = new InputStreamReader(in);
br = new BufferedReader(ir);
String str = null;
while ((str = br.readLine()) != null) {
System.out.println(str);
}
socket.shutdownInput();
out=socket.getOutputStream();
pw = new PrintWriter(out);
pw.write("欢迎你");
pw.flush();
socket.shutdownOutput();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
br.close();
ir.close();
in.close();
pw.close();
out.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//客户端
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("localhost",8888);
OutputStream out=socket.getOutputStream();
PrintWriter pw = new PrintWriter(out);
pw.write("嘿嘿");
pw.flush();
socket.shutdownOutput();
InputStream in = socket.getInputStream();
InputStreamReader ir = new InputStreamReader(in);
BufferedReader bf = new BufferedReader(ir);
String str=null;
while((str=bf.readLine())!=null) {
System.out.println(str);
}
socket.shutdownInput();
pw.close();
out.close();
bf.close();
ir.close();
in.close();
}
}
说明:通过把每次IO操作都采用开启新的线程去执行来减少一次通信的时间。
缺点:当我连接数很多时,这样每一个连接数都对应一个线程,window的最大线程1000左右,linux加倍,那么这样的消耗也是消耗不起的,并且线程的建立与销毁都是非常消耗性能的。
解决办法:采用连接池的当时,指定连接数,保持线程长期存活。
//客户端
public class Sercice {
public static void main(String[] args) throws IOException {
Socket socket;
ServerSocket service = new ServerSocket(8888);
ExecutorService pool = new ThreadPoolExecutor(50, 1000, 120L, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(1000));
while (true) {
socket = service.accept();
pool.execute(new ThreadService(socket));
}
}
}
//IO操作执行类
public class ThreadService implements Runnable{
private Socket socket = null;
public ThreadService(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
InputStream in = null;
InputStreamReader ir = null;
BufferedReader br = null;
PrintWriter pw =null;
OutputStream out =null;
try {
in = socket.getInputStream();
ir = new InputStreamReader(in);
br = new BufferedReader(ir);
String str = null;
while ((str = br.readLine()) != null) {
System.out.println(str);
}
socket.shutdownInput();
out=socket.getOutputStream();
pw = new PrintWriter(out);
pw.write("欢迎你");
pw.flush();
socket.shutdownOutput();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
br.close();
ir.close();
in.close();
pw.close();
out.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//客户端
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("localhost",8888);
OutputStream out=socket.getOutputStream();
PrintWriter pw = new PrintWriter(out);
pw.write("嘿嘿");
pw.flush();
socket.shutdownOutput();
InputStream in = socket.getInputStream();
InputStreamReader ir = new InputStreamReader(in);
BufferedReader bf = new BufferedReader(ir);
String str=null;
while((str=bf.readLine())!=null) {
System.out.println(str);
}
socket.shutdownInput();
pw.close();
out.close();
bf.close();
ir.close();
in.close();
}
}
说明:我们通过线程池的方式达到了伪异步的方式,但这并不是真正的异步。没有从根本上解决阻塞的问题。
那么阻塞到底阻塞到哪了?
阻塞在了IO的read()以及write()方法上了。
read方法除了下面三个情况会一直阻塞着:
1.有数据可读
2.可用数据读取完毕
3.发生空指针IO异常
比如我要向服务端发消息(10字节),那么当网络比较慢的时候,开始只发过来8个字节,那么服务器会一直等待我,直到我所有的消息发送完,才会进行下一步处理。
那么我们该怎么处理这个问题呢?请看一下篇。