ARTS 打卡第十二周(2023.10.30~2023.11.5)

125 阅读6分钟

1. Algorithm 一道算法题

本周算法题为N 皇后问题 II

本题的难点是在放置皇后时,需要将哪些位置置为不可用,在回溯时,当将当前位置皇后置为不可用时,又需要将哪些位置置为可用哪些置为不可用

2. Review 阅读一篇英文文章

本周阅读的英文文章为 Virtual Thread,近期 jdk21 正式发版,里面个人认为史诗级的更新为 Virtual Thread,也就是我们常说的协程,下面以 Orcale 官方文档为例进行阅读

Virtual Threads

Virtual threads are lightweight threads that reduce the effort of writing, maintaining, and debugging high-throughput concurrent applications.

虚拟现成是轻量级的线程,可以减少编写、维护和调试高吞吐量并发程序的工作量

For background information about virtual threads, see JEP 444.

关于虚拟线程的背景可以查看JEP 444.提案

A thread is the smallest unit of processing that can be scheduled. It runs concurrently with—and largely independently of—other such units. It's an instance of java.lang.Thread. There are two kinds of threads, platform threads and virtual threads.

线程是最小的可被调度的运行单位,它与其他这样的单元并发运行,并在很大程度上独立于其他单元,它是 java.lang.Thread 的一个实例。线程分为两种类型:平台级线程和虚拟线程

What is a Platform Thread?

A platform thread is implemented as a thin wrapper around an operating system (OS) thread. A platform thread runs Java code on its underlying OS thread, and the platform thread captures its OS thread for the platform thread's entire lifetime. Consequently, the number of available platform threads is limited to the number of OS threads.

平台级线程可以看作操作系统线程的一个轻量级的封装实现,平台级线程通过其底层的操作系统线程运行 Java 代码,并且在平台级线程整个生命周期都捕获其操作系统线程。因此可用的平台级线程的数量被操作系统线程数量所限制

Platform threads typically have a large thread stack and other resources that are maintained by the operating system. They are suitable for running all types of tasks but may be a limited resource.

平台级线程通常有一个很大的线程栈以及其他被操作系统维护的资源。他们可以运行各种类型的任务但可能是受限的资源

What is a Virtual Thread?

Like a platform thread, a virtual thread is also an instance of java.lang.Thread. However, a virtual thread isn't tied to a specific OS thread. A virtual thread still runs code on an OS thread. However, when code running in a virtual thread calls a blocking I/O operation, the Java runtime suspends the virtual thread until it can be resumed. The OS thread associated with the suspended virtual thread is now free to perform operations for other virtual threads.

跟平台级线程一样,虚拟线程也是 java.lang.Thread 的实例,虚拟线程不跟特定的操作系统线程绑定。但是虚拟线程仍然在操作系统线程基础上运行 Java 代码。但是当运行在虚拟线程上的代码调用阻塞 IO 操作时,Java 的 runtime 会将虚拟线程挂起直至可恢复。跟被暂停的虚拟线程关联的操作系统线程现在可以为其他虚拟线程提供服务。

Virtual threads are implemented in a similar way to virtual memory. To simulate a lot of memory, an operating system maps a large virtual address space to a limited amount of RAM. Similarly, to simulate a lot of threads, the Java runtime maps a large number of virtual threads to a small number of OS threads.

虚拟线程的实现方式类似于虚拟内存。为了模拟大量的内存,操作系统将大量的虚拟地址空间映射到有限的内存。相似的,为了模拟大量的线程,Java runtime 将大量的虚拟线程映射到少数的操作系统线程

Unlike platform threads, virtual threads typically have a shallow call stack, performing as few as a single HTTP client call or a single JDBC query. Although virtual threads support thread-local variables and inheritable thread-local variables, you should carefully consider using them because a single JVM might support millions of virtual threads.

跟平台级线程不同的是,虚拟线程通常只有较浅的调用栈,可能只是执行单个 HTTP 客户端请求或者单个 JDBC 查询。尽管虚拟线程支持 threadlocal 变量以及可继承 threadlocal 变量,你应该谨慎的使用 threadlocal 变量因为一个 JVM 可能支持数以百万级的虚拟线程

Virtual threads are suitable for running tasks that spend most of the time blocked, often waiting for I/O operations to complete. However, they aren't intended for long-running CPU-intensive operations.

虚拟线程适用于运行长时间阻塞的任务(IO 密集型任务),通常是等待 IO 操作完成的任务,但是,他们并不适用于长时间执行 CPU 操作的任务(CPU 密集型任务)

Why Use Virtual Threads?

Use virtual threads in high-throughput concurrent applications, especially those that consist of a great number of concurrent tasks that spend much of their time waiting. Server applications are examples of high-throughput applications because they typically handle many client requests that perform blocking I/O operations such as fetching resources.

在高吞吐量的并发程序中使用虚拟线程,尤其是那些拥有大量并发任务并且任务大部分时间都在等待的应用程序,服务器应用程序就是一个高吞吐量的好例子,因为服务器程序处理大量的客户端请求,这些请求执行阻塞操作比如获取资源

Virtual threads are not faster threads; they do not run code any faster than platform threads. They exist to provide scale (higher throughput), not speed (lower latency).

虚拟线程并不是更快的线程,他们并不比平台级线程运行代码更快,他们的存在是为了提供规模(高吞吐)而不是速度(低延迟)

3. Techniques/Tips 分享一个小技巧

MySQL 中在 RR 隔离级别下,拥有消除幻读的问题(一个事物前后两次查询,后一次查询结果比前一次要多),幻读只出现在当前读情况下,MySQL 解决幻读的方法是间隙锁,MySQL 加锁的方式可以按照如下规则进行:

  • 加锁的基本单位是 next-key lock
  • 查找过程中访问到的对象才会加锁(MySQL 数据是由 B+ Tree 进行构建的,因此访问的对象本质就是索引,因此本质是对索引进行加锁)
  • 索引上的等值查询,给唯一索引加锁时,next-key lock 退化为行锁
  • 索引上的等值查询,向右遍历至第一个不满足条件的索引为止,next-key lock 退化为间隙锁

需要说明的一点是加锁的基本单位是 next-key lock,可以理解为先加间隙锁,再加行锁,由于间隙锁的存在,会导致系统并发性大大降低,并且容易产生死锁,原因是间隙锁之间是不互斥的,间隙锁的唯一功能是阻止其他事务在该间隙内插入数据,这就容易导致两个事务,事务 1 先获取了 next-key lock,事务 2 尝试获取相同的 next-key lock,但是发现在获取间隙锁成功后,获取行锁时被阻塞住了,此时事务 1 尝试在该间隙内插入数据,结果被事务 2 的间隙锁给阻塞,这就形成了死锁

因此很多公司在使用 MySQL 时一般采用的是 RC 隔离级别 + row binlog,这样能够提升系统并发度并且能够防止 binlog 产生二义性,但是需要业务自己解决幻读问题

4. 分享一个观点

MySQL 提升性能整体的设计思路主要分为:顺序写替换随机写(redo log),空间换时间(内存全序列排序、rowid 排序,临时文件、堆排序)