Slice是Go语言提供的一种常用的动态数据结构,相比数组,其长度并不固定,当我们向切片中添加元素时,如果容量不足就会自动扩容。
可以通过三种方式声明一个切片:
- 利用下标获取数组或切片的一部分
- 使用字面量初始化新的切片
- 使用关键字make创建切片
1 | //1 |
切片在编译期间生成的类型只会包含元素类型,如int
、interface{}
等。
记录学习历程
Slice是Go语言提供的一种常用的动态数据结构,相比数组,其长度并不固定,当我们向切片中添加元素时,如果容量不足就会自动扩容。
可以通过三种方式声明一个切片:
1 | //1 |
切片在编译期间生成的类型只会包含元素类型,如int
、interface{}
等。
对于实时性不是很高的业务,如积分增减、发送短信等非核心流程,可以放到消息队列中去,提升整个链路的响应时间。
对于秒杀等短时间大流量场景,可以将请求短暂的在消息队列中堆积,平滑的消费请求。
MQ可以实现服务的高内聚、低耦合,通过发布订阅模型实现系统/模块间的解耦。
MQ相当于一个依赖组件,一旦出问题会导致系统不可用。
引入MQ如何保证消息不重复消费?如何处理消息丢失?如何保证消息的顺序消费?
生产者消费者事务执行结果不一致造成的数据不一致问题。
秒杀系统是一种应用广泛的高并发读写场景,秒杀就是在同一个时刻有大量的请求争抢购买同一个商品并完成交易的过程,对高性能、一致性、高可用要求较高,本文主要记录个人学习秒杀系统设计的收获与思考。
秒杀系统主要就是解决两个问题,一个高并发读,一个高并发写。并发读的优化思路就是尽量减少用户到服务端来读数据,或者读更少的数据;并发写的优化思路也是一样,同时还需要针对系统设计一些保护措施和兜底方案。从架构上就是要保证用户请求的数据尽量少、请求数尽量少、路径尽量短、依赖尽量少、避免单点。
总所周知,HashMap
是非线程安全的,体现在rehash时的死循环,使用迭代器时的fast-fail和多线程环境下put操作出现丢失数据等。
虽然JDK也提供了一个安全版本的HashMap
:Collections.SynchronizedMap
,但因为其使用的是全局锁,并发性能较差。
因此,juc包下提供了线程安全且锁粒度较小的ConcurrentHashMap
。
ConcurrentHashMap
在JDK1.7和JDK1.8中有者不同的实现。
本篇文章主要介绍juc包下与锁相关的API和组件,包括Lock
接口、AQS
抽象队列同步器、重入锁、读写锁和Condition
接口。
锁是用来控制多个线程访问资源的方式,除了synchronized
之外,juc包下还提供了Lock
接口来实现锁功能。其提供了与synchronized
类似的同步功能,但需要显示的获取、释放锁,并且提供了更加强大的功能,如可中断的获取锁和超时获取锁等。
Lock
接口提供了synchronized
关键字不具备的特性:
特性 | 描述 |
---|---|
尝试非阻塞的获取锁 | 当前线程尝试获取锁,如果锁这一时刻没有被其他线程获取到,则成功获取并持有锁 |
可中断的获取锁 | 获取到锁的线程能响应中断,拥有锁的线程被中断时,会抛出中断异常并释放锁 |
超时获取锁 | 在指定时间内获取锁,如果超时仍未获取锁,则返回 |
Lock
接口的默认实现类为ReentrantLock
,部分API如下:
void lock() | 获取锁,调用该方法的线程尝试获取锁,获取成功后返回 |
---|---|
void lockInterruptibly() throws InterruptedException | 可中断的获取锁 |
boolean tryLock() | 尝试非阻塞的获取锁,调用此方法后立即返回 |
boolean tryLock(long var1, TimeUnit var3) throws InterruptedException | 超时获取锁,有三种可能会返回:超时、成功获取锁、被中断 |
void unlock() | 释放锁 |
Condition newCondition() | 获取等待通知组件,该组件和当前的锁绑定,只有获取了锁才能调用此方法,并且调用后释放锁 |
Java是自动内存管理的,但了解相关的内存分配与回收机制仍然很重要。
在Java运行时数据区域中,程序计数器、虚拟机栈和本地方法栈都随着线程创建而创建,随着线程消亡而消亡,栈中的栈帧也随着方法的进入和退出而入栈和出栈,这部分的内存回收在编译后就是确定的。而堆和方法区中则有明显的不确定性,只有在运行时才能确定,内存的分配和回收都是动态的。通常意义上的垃圾回收都是指的对堆和方法区的回收。