裸泳的猪

沾沾自喜其实最可悲

0%

ThreadLocal原理和用法

用法:

  • ThreadLocal提供了线程的局部变量,每个线程都可以通过set()和get()来对这个局部变量进行操作,
    但不会和其他线程的局部变量进行冲突,实现了线程的数据隔离
  • 实现线程级别的全局变量,避免使用一层层的传参。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Requestcontext {
    private static final ThreadLocal<String> traceldLocal = new ThreadLocal<>();

    public static String getTraceId() {
    return threadIdLocal.get();
    }
    public static String setTraceId (String traceld){
    threadldLocal.set(traceld);
    }
    }
    1
    2
    3
    4
    /*一处设置traceid*/
    Requestcontext.setTraceId(UUID.randomUUID().toString());
    /*另一处读取traceId,不在同一个类中调用*/
    RequestContext.getTraceId();

使用举例:

java8之前的日期组件,SimpleDataFormat。当我们使用SimpleDataFormat的parse()方法,内部有一个Calendar对象,调用SimpleDataFormat的parse()方法会先调用Calendar.clear(),然后调用Calendar.add(),如果一个线程先调用了add()然后另一个线程又调用了clear(),这时候parse()方法解析的时间就不对了。从而导致了线程安全问题。当然我们可以每用一次new一个新对象出来,不过这样效率太低。

所以我们可以使用了线程池加上ThreadLocal包装SimpleDataFormat,再调用initialValue让每个线程有一个SimpleDataFormat的副本,从而解决了线程安全的问题,也提高了性能。

1
static ThreadLocal<DateFormat> threadLocal = ThreadLocal.withInitial(SimpleDateFormat::new);

不过Java8之后可以用java.time.format.DateTimeFormatter了。

注意(弱引用引起的内存泄露问题):

当线程没有结束,但是 ThreadLocal 已经被回收,则可能导致线程中存在ThreadLocalMap<null, Object> 的键值对,造成内存泄露。(ThreadLocal 被回收,ThreadLocal 关联的线程共享变量还存在。

解决方案:

  1. 使用完线程共享变量后,显式调用 ThreadLocalMap.remove 方法清除线程共享变量。

原理

简单说 ThreadLocal 就是一种以空间换时间的做法,在每个 Thread 里面维护了一个以开放定址法(区别于hashmap,没有链表,发生冲突时往后找空位就行了)实现的ThreadLocal.ThreadLocalMap,把数据进行隔离,数据不共享,自然就没有线程安全方面的问题了。

结构图:

ThreadLocal
每个Thread对象中都维护了一个ThreadLocalMap,这个Map使用ThreadLocal对象作为key。

可以看到的是,ThreadLocal对象虽然是同一个,但是每个线程中的ThreadLocalMap都是单独的,所以各个线程之间是互不影响的。具体数据则时放在ThreadLocalMap的entry数组下的。

父子线程怎么共享数据

可以使用interitableThreadLocals,子线程能继承到父线程的数据,子线程中的修改不影响父线程。

private

经典面试题:

synchronized 和 ReentrantLock 区别是什么?

相同点:

它们都是加锁方式同步,而且都是阻塞式的同步,也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的。可重入锁。可重入锁是指同一个线程可以多次获取同一把锁。ReentrantLock和synchronized都是可重入锁。

区别:

  1. synchronized 竞争锁时会一直等待;ReentrantLock 可以尝试获取锁,并得到获取结果
  2. synchronized 获取锁无法设置超时;ReentrantLock 可以设置获取锁的超时时间
  3. synchronized 无法实现公平锁;ReentrantLock 可以满足公平锁,即先等待先获取到锁
  4. synchronized 控制等待和唤醒需要结合加锁对象的 wait() 和 notify()、notifyAll();ReentrantLock 控制等待和唤醒需要结合 Condition 的 await() 和 signal()、signalAll() 方法
  5. synchronized 是 JVM 层面实现的;ReentrantLock 是 JDK 代码层面实现
  6. synchronized 在加锁代码块执行完或者出现异常,自动释放锁;ReentrantLock 不会自动释放锁,需要在 finally{} 代码块显示释放
  7. 锁的细粒度和灵活度,很明显ReenTrantLock优于Synchronized。

ReentrantLock主要利用CAS+AQS队列来实现。它支持公平锁和非公平锁,两者的实现类似。

  • AQS(AbstractQueuedSynchronizer)抽象的队列式的同步器。是一个用于构建锁和同步容器的框架。事实上concurrent包内许多类都是基于AQS构建。
    AQS使用一个FIFO的队列表示排队等待锁的线程,队列头节点称作“哨兵节点”或者“哑节点”,它不与任何线程关联。其他的节点与等待线程关联,每个节点维护一个等待状态waitStatus

ReentrantLock的基本实现可以概括为:先通过CAS尝试获取锁。如果此时已经有线程占据了锁,那就加入AQS队列并且被挂起。当锁被释放之后,排在CLH队列队首的线程会被唤醒,然后CAS再次尝试获取锁。在这个时候,如果:

非公平锁:如果同时还有另一个线程进来尝试获取,那么有可能会让这个线程抢先获取;

公平锁:如果同时还有另一个线程进来尝试获取,当它发现自己不是在队首的话,就会排到队尾,由队首的线程获取到锁。

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
private Lock lock = new ReentrantLock();

public void test(){
lock.lock();
try{
doSomeThing();
}catch (Exception e){
// ignored
}finally {
lock.unlock();
}
}

ReentrantLock原理

在研究java的IO模型前有必要先对操作系统的IO模型有个认识。

操作系统层面的IO模型

在Linux(UNIX)操作系统中,共有五种IO模型,分别是:

  1. 阻塞IO模型

    • 阻塞 I/O 是最简单的 I/O 模型,一般表现为进程或线程等待某个条件,如果条件不满足,则一直等下去。条件满足,则进行下一步操作。
  2. 非阻塞IO模型

    • 应用进程与内核交互,目的未达到之前,不再一味的等着,而是直接返回。然后通过轮询的方式,不停的去问内核数据准备有没有准备好。如果某一次轮询发现数据已经准备好了,那就把数据拷贝到用户空间中。
  3. IO复用模型

    • 多个进程的IO可以注册到同一个管道上,这个管道会统一和内核进行交互。当管道中的某一个请求需要的数据准备好之后,进程再把对应的数据拷贝到用户空间中。
  4. 信号驱动IO模型

    • 应用进程在读取文件时通知内核,如果某个 socket 的某个事件发生时,请向我发一个信号。在收到信号后,信号对应的处理函数会进行后续处理。

以上模型都是同步的,原因是因为,无论以上那种模型,真正的数据拷贝过程,都是同步进行的。信号驱动IO模型只能说 数据准备阶段是异步,数据拷贝操作还是同步的。只有系统将数据已经全都从内核空间拷贝到用户空间然后再发信号通知线程已经完成

  1. 异步IO模型
    • 应用进程把IO请求传给内核后,完全由内核去操作文件拷贝。内核完成相关操作后,会发信号告诉应用进程本次IO已经完成。

阅读全文 »

定义

单例模式是比较常见的一种设计模式,目的是保证一个类只能有一个实例,而且自行实例化(不对外开放new )并向整个系统提供这个实例,避免频繁创建对象,节约内存。

典型应用场景

  • 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。
  • 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
  • 网站的计数器,一般也是采用单例模式实现,否则难以同步

    饿汉式和懒汉式,

    饿汉式:提前创建对象。在类加载时就完成了初始化,所以类加载比较慢,但获取对象的速度快。

    懒汉式:延迟创建对象。在类加载时不初始化,等到第一次被使用时才初始化。

饿汉式

在类加载的时候就完成了实例化,避免了多线程的同步问题。缺点就是浪费内存。

1
2
3
4
5
6
7
8
9
10
11
public class Singleton {

private final static Singleton INSTANCE = new Singleton();

private Singleton(){}

public static Singleton getInstance(){
return INSTANCE;
}

}
阅读全文 »

IP分类(IPV4)

IP地址由四段组成,每个字段是一个字节,8位,最大值是255。

IP地址由两部分组成,即网络地址和主机地址。网络地址表示其属于互联网的哪一个网络,主机地址表示其属于该网络中的哪一台主机。二者是主从关系。

IP地址的四大类型标识的是网络中的某台主机。IPv4的地址长度为32位,共4个字节,但实际中我们用点分十进制记法。

IP分类

IP地址根据网络号和主机号来分,分为A、B、C三类及特殊地址D、E。 全0和全1的都保留不用。

A类:(1.0.0.0-126.0.0.0)(默认子网掩码:255.0.0.0或 0xFF000000)第一个字节为网络号,后三个字节为主机号。该类IP地址的最前面为“0”,所以地址的网络号取值于1~126之间。一般用于大型网络。

B类:(128.0.0.0-191.255.0.0)(默认子网掩码:255.255.0.0或0xFFFF0000)前两个字节为网络号,后两个字节为主机号。该类IP地址的最前面为“10”,所以地址的网络号取值于128~191之间。一般用于中等规模网络。

C类:(192.0.0.0-223.255.255.0)(子网掩码:255.255.255.0或 0xFFFFFF00)前三个字节为网络号,最后一个字节为主机号。该类IP地址的最前面为“110”,所以地址的网络号取值于192~223之间。一般用于小型网络。

D类:是多播地址。该类IP地址的最前面为“1110”,所以地址的网络号取值于224~239之间。一般用于多路广播用户[1] 。

E类:是保留地址。该类IP地址的最前面为“1111”,所以地址的网络号取值于240~255之间。

子网掩码

  • 作用:首相IP地址是以网络号和主机号来标示网络上的主机,子网掩码就是用来标记ip中哪些位是表示的网络号,哪些是主机位。

  • 表示:同IP地址一样,子网掩码是由长度为32位二进制数组成的一个地址,IP地址如果某位是网络地址,则子网掩码为1,主机地址则为0。

  • 子网掩码的表示方式:

    例子:192.168.1.100/24,其子网掩码表示为255.255.255.0,二进制表示为11111111.11111111.11111111.00000000

    - **点分十进制表示法**(”例子中的255.255.255.0“)
    - **CIDR斜线记法**(例子中的”/24“)
    

特殊地址

在IP地址3种主要类型里,各保留了3个区域作为私有地址,其地址范围如下:
A类地址:10.0.0.0~10.255.255.255
B类地址:172.16.0.0~172.31.255.255
C类地址:192.168.0.0~192.168.255.255

回送地址:127.0.0.1。 也是本机地址,等效于localhost或本机IP。一般用于测试使用。例如:ping 127.0.0.1来测试本机TCP/IP是否正常。

MA

Moving Average - 移动平均线 ,将一定时期内的证券价格(指数)加以平均,并把不同时间的平均值连接起来,形成一根MA,用以观察证券价格变动趋势的一种技术指标。

  • MA(X,N),X的N日简单移动平均,算法为(X1+X2+X3+…+Xn)/N。

SMA

Simple Moving Average-简单移动平均, 对近期数值可以赋予更高权重,这也是SMA和MA最明显的区别。

  • SMA(x,n,m),X的N日移动平均,M为权重,如Y=(XM+Y’(N-M))/N。其中Y’表示上一周期Y值,N必须大于M

  • SMA=p1+(1-a)*p2+(1-a)² *p3+(1-a)³ *p4+…/1+(1-a)+(1-a)²+(1-a)³+…

    随着时间的回推,数值的权重呈现指数级缩小,由此SMA可以达到给予近期数值更高的权重,更贴合市场价格的表现。(p1当天价格,p2昨天价格。。。,a为平滑指数)

EMA

Exponential Moving Average-指数移动平均值

  • EMA(x,n)-指数移动平均,又名EXPMA,各数值的加权影响力随时间而指数式递减,越近期的数据加权影响力越重。

  • 数学本质上SMA和EMA同属于指数移动平均,只不过EMA属于SMA的一个特例,赋予了今日更高的权重,相同数值天数条件下比SMA均线表现更加敏感。

    EMA(X,N)就相当于SMA(X,N+1,2)。

WMA

weighted moving average - 加权移动平均,指计算平均值时将个别数据以不同数值。

one write;read everywhere.

  • Algorithm 算法
  • Array 数组
  • Atomic 原子的
  • Capacity 容量
  • Constructor 构造器
  • Diagram 图解
  • Dispatcher 调度器 分发器
  • Duplicate 完全一样的; 复制的;
  • Element 要素; 基本部分
  • Enterprise 企业
  • Exclusively 独占 isHeldExclusively()
  • fetch 拿来
  • filter 过滤
  • Generic 泛型
  • Hierarchical 分等级的, 等级的, 分层, 层次, 分层的 hierarchy 等级制度(尤指社会或组织); 统治集团; 层次体系;
  • Implements 实现
  • Inherited 继承(金钱、财产等), 经遗传获得(品质、身体特征等), 接替(责任等), 继任 inherit的过去分词和过去式
  • interpreter 翻译人员
  • isolation 隔离
  • priority 优先级
  • propagation 传播
  • protocol 协议
  • Qualifier 预选赛
  • reference 参考
  • Retention 维持
  • Serializable 可序列化的
  • stamped 盖章
  • Statement 说明
  • transient 短暂的 临时工; java中 不可反序列
  • vector 向量
  • mandatory 强制的
  • nested 嵌套的