高并发day01(NIO、ConCurrent包)

NIO
简介
一、概述
1.是JDK1.4提供的一套用于进行数据传输的机制
2. BIO - BlockingIO―同步阻塞式IO
NIO - NewIO - NonBlockingI0 ―同步非阻塞式IO
AlO - AsynchronousIO 异步非阻塞IO jdk1.8
AIO是在NIO的基础上进行改进,所以AIO又称为NIO.2
3. NIO的三大组件:Buffer、Channel、Selector

二、BIO的缺点
1.阻塞:相比非阻塞而言,阻塞的效率是相对较低的
2.一对一的连接:每过来一个客户端,就需要在服务器端去创建一个线程去处理这个请求。这个过程中伴随着大量的线程的创建和销毁浪费cPU;如果同一时间内,大量客户端产生连接,那么这个时候服务器就得创建大量线程去处理,导致服务器卡顿甚至崩溃
3.当客户端连接到服务器之后,即使客户端不发生任何操作,连接依然会保持,这就会导致服务器端的线程会一直被占用

三、Buffer 缓存区
1.用于存储数据
2.底层是基于数组来进行存储,只能存储基本类型。
但是没有针对boolean的实现类
3.因为数据的存储和传输形式都是以字节形式来实现,所以重点掌握ByteBuffer
4.重要位置:
a. capacity:容量位。标记当前缓冲区的容量,在缓冲区创建好之后
就不可变
b. limit:限制位。限制position所能达到的最大位置。如果limit和position重合,则表示所有的位置都已经用完。注意,在缓冲区刚创建好的时候,limit和capacity重合
c. position:操作位。position指向哪一位,就读写哪一位。作用类似于数组中的下标
d. mark:标记位。实际使用过程中,用于进行标记。mark默认不启动,默认值是-1

在这里插入图片描述
5.重要操作:
a. flip:翻转缓冲区。limit=>position,position=>0, mark=>-1
b. clear:清空缓冲区。position=>0,limit=>capacity,mark>-1
c. reset:重置缓冲区。position=>mark
d. rewind:重绕缓冲区。position=>0,mark=>-1

四大位置的顺序

Invariants: mark <= position <= limit <= capacity
package cn.tedu.nio;

import java.nio.ByteBuffer;

public class ByteBufferDemo {
	
	public static void main(String[] args) {
		//创建ByteBuffer
		//创建的时候需要指定容量,实际上就是给底层的数组来指定容量
		ByteBuffer buffer=ByteBuffer.allocate(10);
		//添加数据
		
		buffer.put("a111".getBytes());
		buffer.put("55555".getBytes());
		
		//buffer.position(0);//操作位回到0位
		//获取数据
//		byte b=buffer.get();
//		System.out.println(b);//
		
		//如果要遍历这个缓冲区,需要先挪动limit,然后挪动position
		//翻转缓存区
		//buffer.flip();
		//有无翻转用limit都可以,没翻转用position
		byte[] bs = buffer.array();
		System.out.println(new String(bs,0,buffer.limit()));
		//先将limit移动到position上
		buffer.limit(buffer.position());
		//将position归零
		buffer.position(0);
		//如果position和limit重合,就表示所有的元素已经取完
//		while(buffer.position()<buffer.limit()){
		while(buffer.hasRemaining()){
			System.out.println(buffer.get());
		}
		
	}
}

package cn.tedu.nio;

import java.nio.ByteBuffer;

public class ByteBufferDemo2 {
	
	public static void main(String[] args) {
		//创建缓存区的时候将数据直接传递进去
		//
		ByteBuffer buffer=ByteBuffer.wrap("hello big2021".getBytes());
		System.out.println(buffer.get());
		byte[] bs = buffer.array();
		System.out.println(new String(bs));
	}
}

四、Channel – 通道
1.用于进行数据的传输
2.不同于Blo,Channel可以实现双向传输
3.Channel默认是阻塞,可以设置为非阻塞
4.File: FileChannel
UDP: DatagramChannel
TCP:SocketChannel、ServerSocketChannel

案例客户端、服务器端的交互

package cn.tedu.channel;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class SocketClient {
	public static void main(String[] args) throws IOException, InterruptedException {
		//开启客户端通道	
		SocketChannel sc = SocketChannel.open();
		//设置为非阻塞
		sc.configureBlocking(false);
		//发起连接
		sc.connect(new InetSocketAddress("localhost",8090));
		
		while(!sc.isConnected()){
			//试图再次建立连接,并且自动计数
			//当达到底层的计算阈值之后,就会认为这个连接无法连接
			//这个时候就会报错
			sc.finishConnect();
		}
		sc.write(ByteBuffer.wrap("hello server".getBytes()));
		ByteBuffer buffer=ByteBuffer.allocate(1024);
		//休眠
		Thread.sleep(5);
		sc.read(buffer);
		System.out.println(new String(buffer.array(),0,buffer.position()));
		//关流
		sc.close();
}
}


package cn.tedu.channel;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class SocketServer {
	public static void main(String[] args) throws IOException {
		//开启服务器端的通道
		ServerSocketChannel ssc = ServerSocketChannel.open();
		//绑定端口
		ssc.bind(new InetSocketAddress(8090));
		ssc.configureBlocking(false);
		//接受连接
		SocketChannel sc = ssc.accept();
		//
		while(sc==null)
			sc=ssc.accept();
		//读取数据
		ByteBuffer buffer=ByteBuffer.allocate(1024);
		sc.read(buffer);
		System.out.println(new String(buffer.array(),0,buffer.position()));
		
		//
		sc.write(ByteBuffer.wrap("get ~~".getBytes()));
		
		//关流
		ssc.close();
	}
}


五、Selector 多路复用选择器
1.面向Channel进行选择,并且要求channel必须是非阻塞
2. Selector基于事件选择机制来实现的-即有事件才会被处理
案例:多对一

package cn.tedu.selector;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class ClientSocket {
	public static void main(String[] args) throws IOException {
		SocketChannel sc = SocketChannel.open();
		sc.connect(new InetSocketAddress("localhost", 8070));
		sc.write(ByteBuffer.wrap("hello server ~~".getBytes()));
		ByteBuffer buffer=ByteBuffer.allocate(1024);
		sc.read(buffer);
		System.out.println(new String(buffer.array(),0,buffer.position()));
		sc.close();
	}
}

package cn.tedu.selector;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class ServerSocket {
	public static void main(String[] args) throws IOException {
		//开启服务器的通道
		ServerSocketChannel ssc = ServerSocketChannel.open();
		//绑定端口
		ssc.bind(new InetSocketAddress(8070));
		//设置非阻塞
		ssc.configureBlocking(false);
		//开启选择器
		Selector selc=Selector.open();
		//将服务器注册到选择器
		ssc.register(selc, SelectionKey.OP_ACCEPT);
	
		while(true){
			
			//进行选择
			selc.select();
			//获取选择出来的事件
			Set<SelectionKey> keys=selc.selectedKeys();
			//遍历集合,根据不同的事件类型进行对应的处理
			Iterator<SelectionKey> it = keys.iterator();
			while(it.hasNext()){
				SelectionKey key=(SelectionKey)it.next();
				//判断事件类型
				//可接受事件
				if(key.isAcceptable()){
					//从这个事件中获取通道
					ServerSocketChannel sscx=(ServerSocketChannel)key.channel();
					
					//接受连接
					SocketChannel sc = sscx.accept();
					//
					//System.out.println("获取到一个连接");
					//设置非阻塞
					sc.configureBlocking(false);
					//注册读写事件
					//后一次的注册事件会覆盖前一次的注册事件
					sc.register(selc, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
				}
				//可读事件
				if(key.isReadable()){
					//获取通道
					SocketChannel sc=(SocketChannel)key.channel();
					ByteBuffer buffer=ByteBuffer.allocate(1024);
					sc.read(buffer);
					System.out.println(new String(buffer.array(),0,buffer.position()));
					//移除可读事件
					//获取到这个通道身上的所有事件
					sc.register(selc, key.interestOps()-SelectionKey.OP_READ);//或者^
				}
				//可写事件
				if(key.isWritable()){
					//获取通道
					SocketChannel sc=(SocketChannel)key.channel();
					sc.write(ByteBuffer.wrap("hello client ~~~".getBytes()));
					
					//移除可写事件
					//获取到这个通道身上的所有事件
					sc.register(selc, key.interestOps()-SelectionKey.OP_WRITE);
				}
			}
			it.remove();
		}
		
	}
	
}

框架
Netty-----Trustin Lee----韩国人 实现了异步非阻塞
Mina----Trustin Lee
Grizzly

常见框架
一、通信框架
1.Mina出身于开源界的大牛Apache组织
2. Netty出身于商业开源大亨Jboss
3. Grizzly则出身于Sun公司

ConCurrent包
一、概述
1.是JDK1.5提供的应对高并发的基础包
2.主要包含: BlockingQueue、ConcurrentMap、ExecutorService、Lock、原子性操作

BlockingQueue -阻塞式队列
1.本质是队列,满足队列的原则(FIFO)
2.所有的阻塞式队列都是有界的-当队列定义好之后,大小就不可变
3.阻塞:当队列已满的时候,再试图放入的线程会被阻塞;当卧列为空的时候,再试图拿去的线程会被阻塞

二、ArrayBlockingQueue -阻塞式顺序队列
1.底层是基手数组进行存储,定义好之后容量不可变
在这里插入图片描述

package cn.tedu.queue;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

public class BlockingQueueDemo {
	public static void main(String[] args) throws Exception {
		//创建一个阻塞式队列
		ArrayBlockingQueue<String> queue=new ArrayBlockingQueue<>(5);
		
		
		//存放元素
		queue.add("a");
		queue.add("b");
		queue.add("c");
		queue.add("d");
		queue.add("e");
		
		
		//获取元素
		//队列为空,remove抛异常
		System.out.println(queue.remove());
		//队列为空,返回null.poll
		System.out.println(queue.poll());
		//队列为空,产生阻塞。take
		System.out.println(queue.take());
		//定时阻塞
		System.out.println(queue.poll(5, TimeUnit.SECONDS));
		
		
		
		//队列已满,抛出异常,add
		//queue.add("f");
		
		//offer---返回false
//		boolean b=queue.offer("f");
//		System.out.println(b);
		//put---产生阻塞
//		queue.put("f");
		
		//定时阻塞
//		queue.offer("f",5,TimeUnit.SECONDS);//5秒,放不进去就不放了
		System.out.println(queue);

	}
}

三、LinkedBlockingQueue—阻塞式链式队列
1.底层是基于节点实现的
2.在使用的时候可以不指定容量。如果指定了容量,指定多大就是多大;如果不指定容量,默认容量是Integer.MAX_VALUE-> 2^31-1
因为在实际生产环境中,一般不会向一个队列中添加21亿个值,所以一般会认为这个队列如果不指定容量就是无界的

四、PriorityBlockingQueue—具有优先级的阻塞式队列
1.在使用的时候可以指定容量也可以不指定。如果不指定容量,默认容量是11
2.在拿取元素的时候,会对元素进行排序(自然排序—一般是升序)。要求元素所对应的类必须实现Comparable接口,重写compareTo方法指定比较规则
3.如果使用迭代遍历的方式,则此时排序规则无效

package cn.tedu.queue;

import java.util.concurrent.PriorityBlockingQueue;

public class PriorityBlockingQueueDemo {
	public static void main(String[] args) throws Exception {
//		PriorityBlockingQueue<String> queue=new PriorityBlockingQueue<>();
//		
//		queue.put("d");
//		queue.put("a");
//		queue.put("h");
//		queue.put("v");
//		queue.put("x");
//		queue.put("y");
//		queue.put("o");
//		queue.put("r");
//		for (int i = 0; i < 7; i++) {
//			System.out.println(queue.take());//排序
//		}
		
		PriorityBlockingQueue<Student> queue=new PriorityBlockingQueue<>(5);
		queue.put(new Student("小哥",40,70));
		queue.put(new Student("小红",36,35));
		queue.put(new Student("丽丽",50,69));
		queue.put(new Student("兰格",20,99));
		queue.put(new Student("凯哥",3,5));
//		for (int i = 0; i < 7; i++) {
//			System.out.println(queue.take());//排序
//		}
		
		for (Student s : queue) {
			System.out.println(s);
		}
	}
	
}
class Student implements Comparable<Student>{
	private String name;
	private int age;
	private int score;
	
	public Student(String name, int age, int score) {
		super();
		this.name = name;
		this.age = age;
		this.score = score;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public int getScore() {
		return score;
	}
	public void setScore(int score) {
		this.score = score;
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + ", score=" + score + "]";
	}
	//如果返回正数,则o排前
	//如果返回负数,则this排前
	//规律总结:
	//如果升序排序-> this - o
	//如果降序排序-> o - this
	@Override
	public int compareTo(Student o) {
		// TODO Auto-generated method stub
		return o.score-this.score;
	}
}


五、SynchronousQueue—同步队列

1.使用的时候不需要指定容量,默认容量为1

六、BlockingDeque -阻塞式双向队列
1.允许两端添加或者获取元素
2.继承了BlockingQueue

ConcurrentMap -并发映射
一、概述
1.本质上是一个Map,存储的元素依然是键值对结构
2.保证映射的并发能力以及线程安全的能力

二、 ConcurrentHashMap----并发哈希映射
1.底层是基于数组+链表结构来存储数据
2.默认初始容量是16,默认加载因子是0.75,扩容的时倾默认每次增加一倍

在这里插入图片描述
hashmap线程安全但是非高并发

3.才用了分段(桶)锁机制。在后续的版本中,ConcurrentHashMap为了提高效率,在分段锁的基础上, 引入读写锁机制
a.读锁:允许多个线程读,不允许线程写
b.写锁:只允许一个线程写,不允许线程多

4.在JDK1.8中,引入了CAS(Compare And Swap,比较和交换)无锁算法保
证线程安全性

在这里插入图片描述
在这里插入图片描述

CAS的语义︰我认为V的值应该是A,如果是,那么将V的值更新为B,否则不修改并告诉V的值实际为多少
V∶内存值
A:旧的预期值
B︰新的预期值

三、ConcurrentNavigableMap

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值