Jone Sun's Blog

用心发现,这个星球很美!

0%

Redis介绍

Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker.

C语言开发的、开源的、基于内存的数据结构存储器,可以用作数据库、缓存和消息中间件

一种 NoSQL(not-only sql,泛指非关系型数据库)的数据库,性能优秀,数据在内存中,读写速度非常快,支持并发 10W QPS

Redis 中文网站 的应用场景包括:缓存系统(“热点”数据:高频读、低频写)、计数器、消息队列系统、排行榜、社交网络和实时系统。

为什么要使用redis

性能

由于MySql数据存储在磁盘中,对于一些需要执行耗时非常长的,但结果不会频繁改动的SQL操作(经常是查询,如每日排行榜或者高频业务热数据),就适合将运行结果放到到redis中。
后面的请求优先去redis中获取,加快访问速度、提高性能

并发

mysql支持并发访问的能力有限(当然现在一般会使用一些数据库连接池的来加强并发能力),当有大量的并发请求,直接访问数据库的话,mysql会挂掉。所以可以使用redis作为缓冲,让请求先访问到redis,而不是直接访问数据库,提高系统的并发能力。

当然redis是基于内存的,存储容量肯定要比磁盘少很多,要存储大量数据,需升级内存,造成在一些不需要高性能的地方是相对比较浪费的,所以建议在需要性能的地方使用redis,在不需要高性能的地方使用mysql。不要一味的什么数据都丢到redis中。

阅读全文 »

概念

ThreadLocal是一个本地线程副本变量工具类。主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用,特别适用于各个线程依赖不同的变量值完成操作的场景

ThreadLocal是属于java.lang包下的,Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离

源码分析

set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}


ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}

void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}

可以看到实际上ThreadLocal中的值是存在其内部的ThreadLocalMap中的,而其key是ThreadLocal自身(注意不是Thread),但ThreadLocalMap的实例却是Thread中属性:

1
ThreadLocal.ThreadLocalMap threadLocals = null;

也就是说是把value保存到给当前线程Thread的ThreadLocalMap中,并以当前ThreadLocal的实例作为key

ThreadLocalMap本质是每个Thread内部各存一份,互不干扰

阅读全文 »

BlockingQueue

概念

阻塞队列BlockingQueue就是为线程之间共享数据而设计的

queue&deque

queue: 先进先出队列

deque: 双端队列,继承自queue,可以在首尾插入或删除元素

BlockingQueue&BlockingDeque

BlockingQueue: 阻塞队列

BlockingDeque: 阻塞双端队列,在不能够插入元素时,它将阻塞住试图插入元素的线程;在不能够抽取元素时,它将阻塞住试图抽取的线程

阻塞

阅读全文 »

前言

任何一个新引入的知识都是为了解决以往系统中出现的问题,否则新引入的将变得毫无价值

如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁。

但当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象,通过Lock就可以实现。

Lock接口, 提供了与synchronized一样的锁功能。虽然它失去了像synchronize关键字隐式加锁解锁的便捷性,但是却拥有了锁获取和释放的可操作性,可中断的获取锁以及超时获取锁等多种synchronized关键字所不具备的同步特性

Lock必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象

locks

一个线程获取多少次锁,就必须释放多少次锁。这对于内置锁也是适用的,每一次进入和离开synchronized方法(代码块),就是一次完整的锁获取和释放。

锁的分类

  • 悲观锁
    悲观锁,每次去请求数据的时候,都认为数据会被抢占更新(悲观的想法);所以每次操作数据时都要先加上锁,其他线程修改数据时就要等待获取锁。适用于写多读少的场景,synchronized就是一种悲观锁
阅读全文 »

前言

我们平常在开发SpringBoot+Mybatis项目时,有时会需要打印sql的执行语句

使用

application.yml

如果项目使用的时yml配置,则:

1
2
3
4
5
6
7
8
9
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

logging:
level:
com:
xxxxx:
dao: debug

application.properties

如果项目使用的时properties配置,则:

1
2
mybatis.configuration.log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging.level.com.xxxxx.dao=debug

注意包名路径应为mybatis对应的方法接口所在的包,而不是mapper.xml所在的包

阅读全文 »

日常开发中经常会需要计算一个方法执行了多久:

1
2
3
4
5
6
7
8
long startTime = System.currentTimeMillis();

//模拟方法执行,花费2s
TimeUnit.SECONDS.sleep(2);

long endTime = System.currentTimeMillis();

System.out.println("花费 " + (endTime - startTime) + " 毫秒");

java8之后提供了新的时间日期处理类,那么Java8之后就可以使用ChronoUnit:

1
2
3
4
5
6
7
8
LocalDateTime startTime = LocalDateTime.now();

//模拟方法执行,花费2s
TimeUnit.SECONDS.sleep(2);

LocalDateTime endTime = LocalDateTime.now();

System.out.println("花费 " + ChronoUnit.MILLIS.between(startTime, LocalDateTime.now()) + " 毫秒");
阅读全文 »

在JDK1.5之后,JDK的(concurrent包)并发包里提供了一些类来支持原子操作,如AtomicBoolean,AtomicInteger,AtomicLong都是用原子的方式来更新指定类型的值

结构

基本类型

AtomicInteger、AtomicLong、AtomicBoolean

主要利用 CAS (compare and swap) + volatile 和 native 方法来保证原子操作,从而避免 synchronized 的高开销,执行效率大为提升

基本类

常用方法

这三个类提供的方法几乎一样,AtomicBoolean少一些,因为boolean值就两个值,所以就是来回改,相对的很多增加减少的方法自然就没有了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/** 
* AtomicInteger常见的方法列表
* @see AtomicInteger#get() 直接返回值
* @see AtomicInteger#getAndAdd(int) 增加指定的数据,返回变化前的数据
* @see AtomicInteger#getAndDecrement() 减少1,返回减少前的数据
* @see AtomicInteger#getAndIncrement() 增加1,返回增加前的数据
* @see AtomicInteger#getAndSet(int) 设置指定的数据,返回设置前的数据
*
* @see AtomicInteger#addAndGet(int) 增加指定的数据后返回增加后的数据
* @see AtomicInteger#decrementAndGet() 减少1,返回减少后的值
* @see AtomicInteger#incrementAndGet() 增加1,返回增加后的值
* @see AtomicInteger#lazySet(int) 仅仅当get时才会set
*
* @see AtomicInteger#compareAndSet(int, int) 尝试新增后对比,若增加成功则返回true否则返回false
*/


/**
* AtomicBoolean主要方法:
* @see AtomicBoolean#compareAndSet(boolean, boolean) 第一个参数为原始值,第二个参数为要修改的新值,若修改成功则返回true,否则返回false
* @see AtomicBoolean#getAndSet(boolean) 尝试设置新的boolean值,直到成功为止,返回设置前的数据
*/

示例

阅读全文 »

map结构

前言

Map与List是我们日常开发中经常会用到的用于存放数据的容器,与list不同的是map采用key/value的数据结构。而map的实现类中最常用的就是HashMap了,对应的在多线程场景下一般会推荐使用ConcurrentHashMap

HashMap

hash

在看HashMap之前,我们先了解下什么是hash:

Hash: 一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入,通过散列算法,变换成固定长度的输出,该输出就是散列值。
散列函数:若关键字为k,则其值存放在f(k)的位置上。因此,不用比较就可以直接通过key找到value。
碰撞: 再拓展下不管采用什么散列算法,都会出现两个不同的输入值,算出来的散列值是一样的,如果k1≠k2,而f(k1)=f(k2),即对于不同的key得到了同一个散列地址,这种现象就叫做碰撞。一般碰撞的概率越小算法越优

对应的在java中Object对象都会有hashCode这个方法(java中默认所有的类都是继承于Object),就是为每个对象都保留生成hash的方法

HashMap原理

先简单了解下HashMap的原理:

阅读全文 »

CountDownLatch

倒计时锁: 一个或多个线程等待其他线程完成操作

概念

CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成一些任务,然后在CountDownLatch上等待的线程就可以恢复执行接下来的任务。

CountDownLatch可以解决那些一个或者多个线程在执行之前必须依赖于某些必要的前提业务先执行的场景

阅读全文 »

并发编程三要素

1. 原子性

即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行

只有简单的读取、赋值(而且必须是将数字赋值给某个变量,变量之间的相互赋值不是原子操作)才是原子操作

可以通过 synchronized 和 Lock 来实现。由于 synchronized 和 Lock 能够保证任一时刻只有一个线程执行该代码块,那么自然就不存在原子性问题了,从而保证了原子性

阅读全文 »