0%

线程池

JDK为我们提供了4种构造线程池的方式,分别是

  • newFixedThreadPool
    • 固定数量的线程
  • newSingleThreadExecutor
    • 只有一个线程的线程池
  • newCachedThreadPool
    • 线程数量不固定,不停的创建线程,用完了以后又回收
  • newScheduledThreadPool
    • 提交任务后,在指定的时间去执行

我们也可以构造自己的线程池,来实现一些我们想要的一些功能。

阅读全文 »

ThreadLocal

1
2
3
4
5
6
7
public class ThreadLocalDemo {
public static void main(String[] args) {
ThreadLocal<Long> requestId = new ThreadLocal<Long>();
requestId.set(1L);
System.out.println(requestId.get());
}
}

ThreadLocal在并发编程里,非常常用,每个线程执行的时候,都保存一个变量的副本,每个线程自己用自己的,互不影响,经常用于保存一些上下文信息,或者请求的id之类的。

阅读全文 »

synchronized

synchronized就是加锁,一旦加锁以后,就只能当前线程访问和修改加锁的变量,其他线程只能阻塞等待,这就保证了原子性。

synchronized可以锁两种对象,一种是对某个实例对象进行加锁,一种是对类进行加锁。对类加锁,本质上也是对实例加锁,只不过是对class对象进行加锁。

如果用synchronized修饰普通方法,那就是对当前类的实例进行加锁。

阅读全文 »

volatile

先说结论,volatile是非常常用的东西,他保证了可见性和有序性。这2个特性分别涉及了JVM的底层原理,最常用的场景,就是共享变量加volatile修饰,这样不同的线程来修改的时候,才能即时的识别到改变,比如标志位、开关等。

阅读全文 »

AtomicInteger

AtomicInteger,可实现原子化的操作,不需要加锁,他底层是通过CAS实现的,看下使用方式

阅读全文 »

线程

线程篇知识,主要包括并发编程方面,JUC并发包下的类的源码学习。

Thread

执行main方法后,就会开启一个jvm进程了,进程里,又有很多线程,main线程就是执行程序的第一个线程,然后我们又创建了一个子线程,来执行另外的任务。

1
2
3
4
5
6
7
8
9
10
11
12
public class HelloWorld {
public static void main(String[] args) {
new Thread() {

@Override
public void run() {
System.out.println("子线程。。");
}

}.start();
}
}
阅读全文 »

Map源码

Map是集合里一个非常重要的数据结构,面试也是会经常问到源码的。

HashMap

简单描述一下HashMap的原理,它的结构就是数组+链表+红黑树。

put的时候,对key进行hash,找到对应的数组位置放在里面,然后hash冲突了就组成链表往后追加。查询的时候也是一样,对key进行hash,然后用equals去比较链表上key的值。

JDK1.8优化了hashmap的数据结构,如果链表过长,达到8以后,就会转变成红黑树。

数据结构

这个数组的初始化长度,是16,和ArrayList不一样。

1
2
3
4
5
6
7
// 数组变量
transient Node<K,V>[] table;
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
// 初始数组大小,默认是16
int threshold;
// 数组长度
transient int size;
阅读全文 »

JDK集合源码

准备写一些文章,把集合、并发、网络相关的JDK工具的使用和源码都在这里记录一下。首先从简单的集合源码开始。

List

ArrayList

基于数组的集合,默认的构造函数,给了一个空数组,Object[], {},默认的初始化大小为10。

1
2
3
4
5
6
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

一般在构造ArrayList,建议指定一个大小,避免频繁扩容带来的开销。

每次往ArrayList中插入数据的时候,都会判断当前数组的元素是否塞满了,如果塞满的话,此时就会扩容这个数组,然后将老数组中的元素拷贝到新数组中去,确保说数组一定是可以承受足够多的元素的。

阅读全文 »

服务注册生产实践

服务注册的时效性(毫秒级)

服务在启动后,会向eureka server发起注册,应该是在1秒以内的。

会通过SpringCloud额外封装的EurekaAutoServiceRegistration#start()发起注册,这个类的实例被EurekaClientAutoConfiguration定义。

服务发现的时效性(毫秒级和分钟级)

一个服务启动后,发现其他所有的服务需要多长时间?如果其他服务新增了机器,那么又需要多长时间才能发现。

  1. 服务启动后,会主动拉取全量注册表,可以发现已经注册的服务列表(毫秒级)
  2. 服务每隔30秒会拉取增量注册表(先走ALL_APPS_DELTA缓存,读不到查queue)
  3. eureka server 二级缓存,定时任务30秒从readWriterCacheMap同步到readOnlyCacheMap
  4. 一个服务启动注册后,要30秒才会被其他服务发现(1分钟以内)
阅读全文 »