一个典型的服务端启动操作大概是: public void run() throws Exception{ EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try{ ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new SimpleChatServerInitializer()) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); System.out.println(“SimpleChatServer 已启动”); ChannelFuture future = bootstrap.bind(port).sync(); future.channel().closeFuture().sync(); }finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); System.out.println(“SimpleChatServer 已关闭”); } } (以下内容请结合源码看, 善用 idea 的快捷键 ctrl + b / ctrl … Continue reading “Netty 一些组件的一些原理”
Author: ranchoswang
Netty 概念与名词
系列前言 Netty 作为使用最广泛的网络IO通信框架, 每一个合格的 Java 后台的开发者都应该对其了如指掌. 但是由于 Netty 项目涉及到比较多的版本迭代, API 也比较丰富. 源码在阅读上相对有一定难度, 所以本系列文章也无法确定更新计划与文章深度, 只能如同拾人牙慧, 外加自己一些浅显的见解, 拼凑出一个仅供自己复习的知识框架 本篇前言与其他的技术不同, 由于 Netty 的应用实在过于普遍又十分重要, 例如阿里广泛使用的 hsf 与 RocketMQ. 所以本系列文章会从如何使用开始, 接下来涉及到源码原理部分, 最后总结其特点与应用位置. 概念与名词 在上一篇文章中, 在 Java NIO 中引入了 Channel/Selector/Select 等概念, 在 Netty 中还需要了解以下概念 Reactor Reactor 并不是 Netty 特有的概念, 而是相对于 BIO 的 Acceptor 的概念提出的, Reactor 翻译叫反应堆, 其实更应该叫 Notifier/Dispatcher. 由于计算机中 CPU 的速度远大于 … Continue reading “Netty 概念与名词”
Java NIO 与 Selector
本篇总结一下 Java NIO相关的知识点与源码 Java NIO 属于比较基础的 Java 知识点, 但是出于一些不可名状的原因, Java NIO 成了长期没有关注的盲点. 由于与 netty 息息相关, 在不远的未来将会用到, 所以在这里特意总结一下. Channels 与 Buffers 在 Java 标准的输入输出控制时, 我们往往控制的是字节流或者字符流. 由于要满足 NIO (Non-block IO) 的需求, 所以我们需要使用 channels 和 buffers. channel 和流提供的功能相类似, 但是区别在于: i. channel 既可以用于读也可以用于写, 但是流一般只用于读或者写. ii. channel 可以异步的读写, 这是 NIO 最大的要求 iii. channel 总是往 Buffer 中读, 或者从 Buffer 中写. channel … Continue reading “Java NIO 与 Selector”
Java Lambda 表达式与函数式编程
本篇总结一下 Java 8 的特性之一 lambda 表达式与函数式编程 从匿名类到匿名函数再到 lambda 表达式 在很久以前(2013年前后) 第一次接触 Java 线程时, 线程是这么写的: Runnable r = new MyTask(); Thread t = new Thread(r); t.start(); class MyTask implements Runnable(){ @Override public void run(){ //do something } } (当然也可以使用扩展 Thread 类的方法新建 Thread 启动 Thread.) 出于对接口概念的不清晰, 这样的代码需要初学者比较长的时间才能熟悉. 后来, 如果只是为了简单执行多线程任务, 匿名类取代了这种明确定义接口的方法: Thread t = new Thread(new Runnable(){ … Continue reading “Java Lambda 表达式与函数式编程”
java.util.concurrent的执行器与线程池
本篇主要继续盘点 java.util.concurrent 中的执行器与线程池 ThreadPoolExecutor的构造 大量的线程创建与销毁是一个十分消耗系统资源的操作, 通过 线程池-线程工厂-线程执行器 可以高效的使用线程资源, 线程可以被重复利用, 适合大量短线程的情景. ThreadPoolExecutor 是线程池中非常重要的一个类. 其继承关系如下图所示 使用 TheadPoolExecutor 我们可以得到一个线程执行器,方便地执行各种大量重复的线程. ThreadPoolExecutor 有多种构造方法: public class ThreadPoolExecutor extends AbstractExecutorService { ….. public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long … Continue reading “java.util.concurrent的执行器与线程池”
java.util.concurrent的工具
本篇主要继续盘点 java.util.concurrent 中的工具包 ReentrantLock/Semaphore/CountDownLatch/CyclicBarrier/Phaser/Exchanger 如上一篇文章中写到 ReentrantLock 提供了重入锁的调用, 通过 lock()/unlock() 实现加锁/解锁的操作. 例如 class LockTest implements Runnable { ReentrantLock lock; public LockTest(ReentrantLock lock) { this.lock = lock; } @Override public void run(){ try{ lock.lock(); //do something }finally{ lock.unlock(); } } } /* * * * * * ReentrantLock lock = new ReentrantLock(); Thread t1 = new Thread(new … Continue reading “java.util.concurrent的工具”
AbstractQueuedSynchronizer
本篇主要继续盘点java.util.concurrent中的工具包 AbstractQueuedSynchronizer 曾在先前的文章里介绍过, ReentrantLock是基于API实现的重入锁的数据结构. 在API层面的具体实现而是依靠AbstractQueuedSynchronizer实现的. AbstractQueuedSynchronizer本质是一个修改后 CLH(Craig, Landin and Hagersten) 队列. CLH队列是由FIFO的队列实现的, 简单的 CLH 队列节点存在一个 locked 域, 每一个节点存放一个竞争资源的线程. 当一个节点尝试获取锁时, 会在队列尾端插入一个节点, 对队列的 tail 域进行 CAS 操作, 反复试图使将节点插入到队列尾端. 并获取其前驱节点的 locked 的域, 该线程会在其前驱节点的 locked 域旋转, 直到前驱节点释放锁将 locked 置为 false. Java AbstractQueuedSynchronizer 是一个Abstract类, ReentrantLock 中的 Sync 类继承了该类, Sync 又被 NonfairSync 和 FairSync 所继承, 分别用于公平锁与非公平锁. ReentrantLock 默认是非公平锁, 即加锁时无需考虑排队问题, 可以直接尝试获取锁, 获取失败会假如排队队列. … Continue reading “AbstractQueuedSynchronizer”
内存模型与线程
本篇主要用于盘点Java中常见的多线程知识 1.复杂的内存模型 何为Java的内存模型(JMM)? JMM实际上是为了避免C/C++在不同机器上内存适配的问题, 尝试定义程序中各个变量的访问规则, 而不是说简单的将内存划分为堆/栈. Java内存模型规定了两种内存: 工作内存(例如对应于虚拟机栈局部变量)与主内存(对应于堆的对象实例). 线程对变量的所有操作都必须在工作内存中进行, 而不能读写主内存中的变量. (以下内容摘自深入理解JVM虚拟机) 关于主内存与工作内存之间的交互协议, 即一个变量如何从主内存拷贝到工作内存. 如何从工作内存同步到主内存中的实现细节. java内存模型定义了8种操作来完成, 这8种操作每一种都是原子操作. 8种操作如下: i. lock(锁定):作用于主内存, 它把一个变量标记为一条线程独占状态. ii .unlock(解锁):作用于主内存, 它将一个处于锁定状态的变量释放出来,释放后的变量才能够被其他线程锁定. iii. read(读取):作用于主内存, 它把变量值从主内存传送到线程的工作内存中,以便随后的load动作使用. iv. load(载入):作用于工作内存, 它把read操作的值放入工作内存中的变量副本中. v. use(使用):作用于工作内存, 它把工作内存中的值传递给执行引擎,每当虚拟机遇到一个需要使用这个变量的指令时候,将会执行这个动作. vi. assign(赋值):作用于工作内存, 它把从执行引擎获取的值赋值给工作内存中的变量,每当虚拟机遇到一个给变量赋值的指令时候,执行该操作. vii. store(存储):作用于工作内存, 它把工作内存中的一个变量传送给主内存中,以备随后的write操作使用. vii. write(写入):作用于主内存, 它把store传送值放到主内存中的变量中. 并且必须满足如下规则: i. 不允许read和load、store和write操作之一单独出现, 以上两个操作必须按顺序执行, 但没有保证必须连续执行, 也就是说, read与load之间, store与write之间是可插入其他指令的. ii. 不允许一个线程丢弃它的最近的assign操作, 即变量在工作内存中改变了之后必须把该变化同步回主内存. iii. 不允许一个线程无原因地(没有发生过任何assign操作)把数据从线程的工作内存同步回主内存中. iv. … Continue reading “内存模型与线程”
Collection 与 HashMap
本篇主要总结集合框架/Hashmap相关的内容 介绍之前先记住这张图 虚线(点/短线)分别表示了 Interface/Abstract class, 实线表示了 Class, 箭头表示了实现或者继承. 1.Collection 最基本的集合接口, 实现了一些基础方法, 例如 size(), isEmpty(), add(), remove(), contains(), clear() 等等. Collection 有三个子接口: List, Set, Queue. List下有AbstractList接口, Vector, Stack, ArrayList, LinkedList都实现了该接口.(LinkedList还实现了Deque/Queue接口) Set下有SortedSet和AbstractSet虚类, HashSet实现了AbstractSet虚类, TreeSet二者都实现了. (HashSet 是用HashMap实现) 2. TreeSet/TreeMap TreeSet/TreeMap可以看作 HashSet和HashMap的排序版本, 例如TreeSet可以使用迭代器迭代输出排序之后的Set. (Set中的类型需要支持Comparable接口.) TreeMap则可以实现HashMap的排序. 其具体实现是红黑树. 简单介绍一下红黑树, 满足以下几个性质的二叉搜索树即为红黑树: i. 节点是红色或者是黑色 ii. 根节点是黑色 iii. 叶子(Nil)结点是黑色 iv. 红色节点的子节点都是黑色 v. 从任何一个结点向下出发的所有路径都应该包含数量相等的黑色节点 … Continue reading “Collection 与 HashMap”
JVM 垃圾回收
接上篇文章, 本篇文章主要总结 Garbage Collector 的相关知识点. 1. 存活判定 存活判定一般依据引用计数或者可达性分析, 引用计数时, 某一个对象每一个地方引用它, 计数器加一, 某引用失效, 计数器减一, 计数器为0的对象不可使用. 缺点在于难以解决对象互相引用的问题. 可达性分析是利用 GC Roots 从这些节点向下搜索, 当一个对象从 GC Roots 不可达, 则认为该回收. 可以当作 GC Roots 的为: i. 本地变量(虚拟机栈) ii. 静态属性引用(方法区) iii. 常量引用(方法区) iv. JNI(Native 方法引用) 当对象被标记为不可达时, 如果该对象没必要执行 finalize() 方法, 则会被认为死亡. 如果需要执行 finalize(), 那么会将该对象加入 F-Queue, 由 Finalize 线程执行该方法. 执行过 finalize() 的方法对象不会再次执行该方法. 此时对象真正死亡. PermGen 也会回收废弃的常量和无用的类, … Continue reading “JVM 垃圾回收”