沧海一粟

天下事有难易乎?为之,则难者亦易矣;不为,则易者亦难矣。

0%

多线程设计模式是前人解决并发问题的经验总结,当我们试图解决一个并发问题时,首选方案往往是使用匹配的设计模式,这样能避免走弯路。
大家都熟悉设计模式,所以使用设计模式还能提升方案和代码的可理解性。

避免共享的设计模式
Immutability 模式Copy-on-Write 模式线程本地存储模式 本质上都是为了避免共享,只是实现手段不同而已。

多线程版本IF的设计模式
Guarded Suspension 模式Balking 模式 都可以简单地理解为“多线程版本的 if”,但它们的区别在于前者会等待 if 条件变为真,而后者则不需要等待。

三种最简单的分工模式
Thread-Per-Message模式Worker Thread 模式生产者-消费者模式 是三种最简单实用的多线程分工方法

(1) Immutability Pattern 不变性模式

解决并发问题,其实最简单的办法就是让共享变量只有读操作,而没有写操作。

不变性(Immutability)模式。所谓不变性,简单来讲,就是对象一旦被创建之后,状态就不再发生变化。换句话说,就是变量一旦被赋值,就不允许修改了(没有写操作);没有修改操作,也就是保持了不变性。

Java SDK 里很多类都具备不可变性。例如经常用到的 String 和 Long、Integer、Double 等基础类型的包装类都具备不可变性,这些对象的线程安全性都是靠不可变性来保证的。仔细翻看这些类的声明、属性和方法,你会发现它们都严格遵守不可变类的三点要求:类和属性都是 final 的,所有方法均是只读的。

Java所有的基础类型的包装类都不适合做锁,因为它们内部用到了享元模式,这会导致看上去私有的锁,其实是共有的。

使用 Immutability 模式的注意事项在使用 Immutability 模式的时候,需要注意以下两点:

  1. 对象的所有属性都是 final 的,并不能保证不可变性;
  2. 不可变对象也需要正确发布。

在使用 Immutability 模式的时候一定要确认保持不变性的边界在哪里,是否要求属性对象也具备不可变性。
Foo对象是不变的,但是Foo对象的属性是可以变化的

1
2
3
4
5
6
7
8
9
10
class Foo{
int age=0;
int name="abc";
}
final class Bar {
final Foo foo;
void setAge(int a){
foo.age=a;
}
}

阅读全文 »

在Java中,创建对象,仅仅是在 JVM 的堆里分配一块内存,而创建一个线程,需要调用操作系统内核的 API,然后操作系统要为线程分配一系列的资源,成本很高,所以线程是一个重量级的对象,应该避免频繁创建和销毁。

进程的运行空间一般分为用户态和内核态,用户态空间一般是进程应用运行空间,而内核态空间一般是指应用需要调用系统资源,应用不能再用户态空间直接调用系统资源,需要通过内核态来系统系统资源。

内核线程(Kernel-Level Thread, KLT)是由操作系统内核支持的线程,内核通过调度器对线程进行调度,并负责完成线程的切换。

阅读全文 »

https://docs.oracle.com/javase/tutorial/
https://docs.oracle.com/en/java/javase/index.html

https://blogs.oracle.com/java-platform-group/

javase14文档 https://docs.oracle.com/en/java/javase/14/index.html
https://docs.oracle.com/javacomponents/index.html
https://docs.oracle.com/en/java/javase/index.html
https://docs.oracle.com/en/java/javase/13/
https://docs.oracle.com/en/java/javase/12/
https://docs.oracle.com/en/java/javase/11/
https://docs.oracle.com/javase/10/
https://docs.oracle.com/javase/9/
https://docs.oracle.com/javase/8/
https://docs.oracle.com/javase/7/docs/index.html

https://dzone.com/articles/jdk-14-records-text-blocks-and-more

Java中的锁

偏向锁

偏向锁主要用来优化同一线程多次申请同一个锁的竞争。

在某些情况下,大部分时间是同一个线程竞争锁资源。

例如,在创建一个线程并在线程中执行循环监听的场景下,或单线程操作一个线程安全集合时,同一线程每次都需要获取和释放锁,每次操作都会发生用户态与内核态的切换。
偏向锁的作用就是,当一个线程再次访问这个同步代码或方法时,该线程只需去对象头的 Mark Word 中去判断一下是否有偏向锁指向它的 ID,无需再进入 Monitor 去竞争对象了。

当对象被当做同步锁并有一个线程抢到了锁时,锁标志位还是 01,“是否偏向锁”标志位设置为 1,并且记录抢到锁的线程 ID,表示进入偏向锁状态。一旦出现其它线程竞争锁资源时,偏向锁就会被撤销。偏向锁的撤销需要等待全局安全点,暂停持有该锁的线程,同时检查该线程是否还在执行该方法,如果是,则升级锁,反之则被其它线程抢占。

轻量级锁

轻量级锁适用于线程交替执行同步块的场景,绝大部分的锁在整个同步周期内都不存在长时间的竞争。
轻量级锁还使用了自旋锁来避免线程用户态与内核态的频繁切换,提高了系统性能;

自旋锁

在锁竞争不激烈且锁占用时间非常短的场景下,自旋锁可以提高系统性能。

JDK1.7 开始,自旋锁默认启用,自旋次数由 JVM 设置决定。
CAS 重试操作意味着长时间地占用 CPU。

乐观锁

乐观锁,在操作共享资源时,抱着乐观的态度进行,认为可以成功地完成操作。但实际上,当多个线程同时操作一个共享资源时,只有一个线程会成功,那么失败的线程呢?它们不会像悲观锁一样在操作系统中挂起,而仅仅是返回,并且系统允许失败的线程重试,也允许自动放弃退出操作。
乐观锁相比悲观锁来说,不会带来死锁、饥饿等活性故障问题,线程间的相互影响也远远比悲观锁要小。
乐观锁没有因竞争造成的系统开销,所以在性能上也是更胜一筹。

CAS 乐观锁在高并发写大于读的场景下,大部分线程的原子操作会失败,失败后的线程将会不断重试 CAS 原子操作,这样就会导致大量线程长时间地占用 CPU 资源,给系统带来很大的性能开销。
CAS 乐观锁在平常使用时比较受限,它只能保证单个变量操作的原子性

References

[1]12 | 多线程之锁优化(上):深入了解Synchronized同步锁的优化方法

读写锁,是一个广为使用的通用技术,无论是操作系统、数据库、编程语言、应用 等都有用到

所有的读写锁都遵守以下三条基本原则:

  1. 允许多个线程同时读共享变量;
  2. 只允许一个线程写共享变量;
  3. 如果一个写线程正在执行写操作,此时禁止读线程读共享变量。

读写锁与互斥锁的一个重要区别就是读写锁允许多个线程同时读共享变量,而互斥锁是不允许的,这是读写锁在读多写少场景下性能优于互斥锁的关键。但读写锁的写操作是互斥的,当一个线程在写共享变量的时候,是不允许其他线程执行写操作和读操作。

redis高性能分析

数据类型

redis为了达到高性能,对数据类型做了调整,更好的适用于高并发

redis的源码相关文件如下:
t_hash.c, t_list.c, t_set.c, t_string.c, t_zset.c and t_stream.c contains the implementation of the Redis data types. They implement both an API to access a given data type, and the client commands implementations for these data types.

sds.c is the Redis string library, check http://github.com/antirez/sds for more information.

dict.c is an implementation of a non-blocking hash table which rehashes incrementally.

SDS

SDS使用C语言编写。C语言的字符串是用一个以\0结尾的char[]表示。
但是为什么不直接使用C语言字符串。
C语言字符串的缺点:

  1. 获取字符串长度每次需要遍历char[]数组,时间复杂度O(n)
  2. 修改字符串忘记分配内存容易造成缓冲区溢出 (buffer overflow) 。
  3. 修改字符串需要重新分配内存,设计系统调用。

    SDS优点

  4. 获取字符串长度时间复杂度O(1) 。
  5. 通过封装,避免了直接操作C语言字符串忘记分配内存导致的内存泄露。
  6. 减少修改字符串时内存分配次数。

    空间预分配 惰性空间释放

    二进制安全
    SDS的buf保存的是二进制数组

1
2
3
4
5
struct sdshdr {
int len;
int free;
char buf[];
};

redis里的key是保存了字符串的SDS
redis里的value

阅读全文 »

在压测的时候看了一下缓存监控,看到监控上的数字震惊了,单分片每秒的出流量172MB。从来没想到过性能可以这么高。(优化前的)
分享一些高性能的知识点吧。

(1) 大多数情况存储的时候推荐使用byte

redis-data-types
redis-value-as-byte-vs-plain-string
jvm-serializers

string类型 redis底层存储的都是二进制,所以redis是二进制安全的(binary safe)
看代码的话你会发现redis存储的时候用的都是 byte[]
Redis Strings are binary safe, this means that a Redis string can contain any kind of data, for instance a JPEG image or a serialized Ruby object.

对于存取对象,大多数情况建议使用 set(final byte[] key, final byte[] value) get(final byte[] key)
对于直接存储字符串的,建议直接使用 set(final String key, final String value) get(final String key)

(2) 使用mset mget

mset
mget

(3) 批量处理时优化成一次请求

批处理时可以使用 Pipeline 一次处理,减少网络开销

阅读全文 »

Redis is not a plain key-value store, it is actually a data structures server, supporting different kinds of values. What this means is that, while in traditional key-value stores you associate string keys to string values, in Redis the value is not limited to a simple string, but can also hold more complex data structures. The following is the list of all the data structures supported by Redis, which will be covered separately in this tutorial:

redis data types

Binary-safe strings.
Lists: collections of string elements sorted according to the order of insertion. They are basically linked lists.
Sets: collections of unique, unsorted string elements.
Sorted sets, similar to Sets but where every string element is associated to a floating number value, called score. The elements are always taken sorted by their score, so unlike Sets it is possible to retrieve a range of elements (for example you may ask: give me the top 10, or the bottom 10).
Hashes, which are maps composed of fields associated with values. Both the field and the value are strings. This is very similar to Ruby or Python hashes.
Bit arrays (or simply bitmaps): it is possible, using special commands, to handle String values like an array of bits: you can set and clear individual bits, count all the bits set to 1, find the first set or unset bit, and so forth.
HyperLogLogs: this is a probabilistic data structure which is used in order to estimate the cardinality of a set. Don’t be scared, it is simpler than it seems… See later in the HyperLogLog section of this tutorial.
Streams: append-only collections of map-like entries that provide an abstract log data type. They are covered in depth in the Introduction to Redis Streams.

Redis keys

Redis的key可以是任意类型,可以是字符串,还可以是图片,还可以是空字符串
Redis的key是二进制安全的,底层存储用的byte[]
建议key不要太大
建议key不要太小,太小会比较省内存,但是可读性、可维护性较差。
尝试坚持一个模式。 建议使用 object-type:id,比如 user:1000
redis允许最大的key大小为 512MB

阅读全文 »

elasticsearch 配置笔记

Elasticsearch has three configuration files:

1
2
3
elasticsearch.yml for configuring Elasticsearch
jvm.options for configuring Elasticsearch JVM settings
log4j2.properties for configuring Elasticsearch logging

ES Server 配置

ES 配置

1
2
#自动创建索引
action.auto_create_index: .monitoring*,.watches,.triggered_watches,.watcher-history*,.ml*

可重新加载配置
Just like the settings values in elasticsearch.yml, changes to the keystore contents are not automatically applied to the running Elasticsearch node. Re-reading settings requires a node restart. However, certain secure settings are marked as reloadable.

1
2
POST _nodes/reload_secure_settings
POST _nodes/<node_id>/reload_secure_settings
1
2
3
4
5
curl -X POST "localhost:9200/_nodes/reload_secure_settings?pretty" -H 'Content-Type: application/json' -d'
{
"secure_settings_password": "s3cr3t"
}
'
阅读全文 »

Elastic Stack 是适用于数据采集、充实、存储、分析和可视化的一组开源工具。人们通常将 Elastic Stack 称为 ELK Stack(代指 Elasticsearch、Logstash 和 Kibana)

Elasticsearch 是一个分布式的开源搜索和分析引擎,适用于所有类型的数据,包括文本、数字、地理空间、结构化和非结构化数据。 类似于MySQL,用来存储和分析数据。
Logstash可用来对数据进行聚合和处理,并将数据发送到 Elasticsearch。Logstash 是一个开源的服务器端数据处理管道,允许您在将数据索引到 Elasticsearch 之前同时从多个来源采集数据,并对数据进行充实和转换。 类似于程序,抽取解析数据并储存到ES
Kibana 是一款适用于 Elasticsearch 的数据可视化和管理工具。类似于使用MySQL时用的Navicat

简单的说,LogStash用来收取解析日志并发消息写入ES,Elasticsearch用来存储和分析查询,Kibana用来查看

es下载地址 https://www.elastic.co/cn/downloads/elasticsearch
logstash下载地址 https://www.elastic.co/cn/downloads/logstash
kibana下载地址 https://www.elastic.co/cn/downloads/kibana
filebeats

阅读全文 »