0x01 volatile 变量
volatile关键字的两层语义
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
2)禁止进行指令重排序。
⚠️注意:volatile只能保证变量的可见性,但不保证修饰变量的原子性
1 | public class Test { |
Quoth the raven, "Nevermore"
volatile关键字的两层语义
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
2)禁止进行指令重排序。
⚠️注意:volatile只能保证变量的可见性,但不保证修饰变量的原子性
1 | public class Test { |
[TOC]
(1)ArrayList
是一种变长的集合类,基于定长数组实现,使用默认构造方法初始化出来的容量是10(1.7之后都是延迟初始化,即第一次调用add方法添加元素的时候才将elementData容量初始化为10)。
(2)ArrayList
允许空值和重复元素,当往 ArrayList 中添加的元素数量大于其底层数组容量时,其会通过扩容机制重新生成一个更大的数组。ArrayList
扩容的长度是原长度的1.5倍
(3)由于 ArrayList
底层基于数组实现,所以其可以保证在 O(1)
复杂度下完成随机查找操作。
(4)ArrayList
是非线程安全类,并发环境下,多个线程同时操作 ArrayList,会引发不可预知的异常或错误。
(5)顺序添加很方便
(6)删除和插入需要复制数组,性能差(可以使用LinkindList)
(7)Integer.MAX_VALUE - 8 :主要是考虑到不同的JVM,有的JVM会在加入一些数据头,当扩容后的容量大于MAX_ARRAY_SIZE,我们会去比较最小需要容量和MAX_ARRAY_SIZE做比较,如果比它大, 只能取Integer.MAX_VALUE,否则是Integer.MAX_VALUE -8。 这个是从jdk1.7开始才有的
more >>[TOC]
自动装箱和拆箱从Java 1.5开始引入,目的是将原始类型值转自动地转换成对应的对象。自动装箱与拆箱的机制可以让我们在Java的变量赋值或者是方法调用等情况下使用原始类型或者对象类型更加简单直接。
详细的解释可以参考: Java中的自动装箱与拆箱
值类型(int,char,long,boolean等)的话
对象引用的话
== 判断引用所指的对象是否是同一个。
equals 方法,是 Object 的成员函数,有些类会覆盖(override
) 这个方法,用于判断对象的等价性。
例如 String 类,两个引用所指向的 String 都是
"abc"
,但可能出现他们实际对应的对象并不是同一个(和 JVM 实现方式有关),因此用 == 判断他们可能不相等,但用 equals 方法判断一定是相等的。
[TOC]
在Java8之前,接口中只能包含抽象方法。那么这有什么样弊端呢?比如,想再Collection接口中添加一个spliterator抽象方法,那么也就意味着之前所有实现Collection接口的实现类,都要重新实现spliterator这个方法才行。而接口的默认方法就是为了解决接口的修改与接口实现类不兼容的问题,作为代码向前兼容的一个方法。
那么如何在接口中定义一个默认方法呢?来看下JDK中Collection中如何定义spliterator方法的:
1 | default Spliterator<E> spliterator() { |
可以看到定义接口的默认方法是通过default关键字。因此,在Java8中接口能够包含抽象方法外还能够包含若干个默认方法(即有完整逻辑的实例方法)。
1 | public interface IAnimal { |
注意⚠️:因为class可以继承多个Interface,所以如果一个class集成了多个含有相同名称的default方法的接口,就会报错,这个时候我们需要通过override来重写它。
1
2
3
4
5
6
7
8
9
10
11
12 > public class DefaultMethodTest implements IAnimal,IDonkey,IHorse {
> public static void main(String[] args) {
> DefaultMethodTest defaultMethod = new DefaultMethodTest();
> defaultMethod.run();
> }
>
> @Override
> public void run() {
> IHorse.super.run();
> }
> }
>
静态方法
在Java8中还有一个特性就是,接口中还可以声明静态方法并且可以实现,如下例:
1 | public interface IAnimal { |
[demo 版本 ]
[TOC]
首先通过资源定位来找到配置文件(xml注入),然后使用BeanDefinition载入和解析、注册BeanDefinition、接着对Bean进行实例化、在Bean被使用的时候(getBean()调用时再注入熟悉,懒加载的特性)进行依赖注入
其实很简单,@ComponentScan主要就是定义扫描的路径从中找出标识了需要装配的类自动装配到spring的bean容器中
做过web开发的同学一定都有用过@Controller,@Service,@Repository注解,查看其源码你会发现,他们中有一个共同的注解@Component,没错@ComponentScan注解默认就会装配标识了@Controller,@Service,@Repository,@Component注解的类到spring容器中,好下面咱们就先来简单演示一下这个例子
所以在Spring中我们需要一个Config类来启动组件扫描
1 | // DEMO |
代理模式UML类图
这种代理方式需要代理对象和目标对象实现一样的接口。
优点:可以在不修改目标对象的前提下扩展目标对象的功能。
缺点:
使用Characterization of Encrypted and VPN Traffic using Time-related Features
这个特征来检测VPN流量,并将其分类。主要使用的机器学习算法为(C4.5 and KNN)
整片文章因为已经是16年的上古文章了,idea在19年看起来已经很过时(就是很水的意思),主要贡献在于提出时序特征可以作为加密流量分类的特征使用,同时通过简单的ML算法得出使用较短的超时value可以让分类器的效果更好。
提出时序特征可以作为加密流量分类的特征使用
提供一个流量分类的数据集
[TOC]
高性能: 用户第一次访问数据库中的数据,因为需要从硬盘读取,这个过程会比较慢。所以将经常被访问的数据存在缓存中(redis使用内存存储),会加快访问速度。
高并发:Redis通过主从架构,实现读写分离,主节点负责写,并将数据同步给其他从节点,从节点负责读,从而实现高并发。
more >>直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。
Question:无论是那种类型的 Reactor 模型,都需要在 Reactor 所在的线程中,进行读写操作。那么此时就会有一个问题,如果我们读取到数据,需要进行业务逻辑处理,并且这个业务逻辑需要对数据库、缓存等等进行操作,会有什么问题呢?假设这个数据库操作需要 5 ms ,那就意味着这个 Reactor 线程在这 5 ms 无法进行注册在这个 Reactor 的 Channel 进行读写操作。也就是说,多个 Channel 的所有读写操作都变成了串行。势必,这样的效率会非常非常非常的低。
解决:那么怎么解决呢?创建业务线程池,将读取到的数据,提交到业务线程池中进行处理。这样,Reactor 的 Channel 就不会被阻塞,而 Channel 的所有读写操作都变成了并行了。
案例: 《Dubbo 用户指南 —— 线程模型》就是使用线程池来处理IO线程中时间处理逻辑较慢 or 需要发起新请求的情况(查询数据库、连接事件中发起登陆请求.etc)
more >>
缺失模块。
1、请确保node版本大于6.2
2、在博客根目录(注意不是yilia-plus根目录)执行以下命令:
npm i hexo-generator-json-content --save
3、在根目录_config.yml里添加配置:
jsonContent: meta: false pages: false posts: title: true date: true path: true text: false raw: false content: false slug: false updated: false comments: false link: false permalink: false excerpt: false categories: false tags: true