Deep understanding of Java Concurrent lock

Irving the procedural ape 2020-11-10 17:30:44
deep understanding java concurrent lock


In depth understanding of Java Concurrent locks

1. Introduction to concurrent lock

The most common way to ensure thread safety is to use the lock mechanism (Locksychronized) To synchronize shared data , So at the same time , Only one thread can execute a method or a block of code , So the operation must be atomic , Thread safe .

at work 、 During the interview , I often hear all kinds of locks , Listen to people in the clouds . There are many conceptual terms of lock , They are aimed at different problems , By simply combing , It's not hard to understand .

1.1. Reentrant lock

Reentrant lock , seeing the name of a thing one thinks of its function , It means that a thread can repeatedly acquire the same lock . That is, the same thread acquires the lock in the outer method , When entering the inner layer, the method will automatically acquire the lock .

Reentrant lock can avoid deadlock to some extent .

  • ReentrantLock 、ReentrantReadWriteLock  It's a reentrant lock . this , It's not hard to see from its name .
  • synchronized  It's also a reentrant lock .

【 Example 】synchronized  A reentrant example of

synchronized void setA() throws Exception{ Thread.sleep(1000); setB();}synchronized void setB() throws Exception{ Thread.sleep(1000);}

The above code is a typical scenario : If the lock used is not a reentrant lock ,setB  May not be executed by the current thread , And cause deadlock .

【 Example 】ReentrantLock  A reentrant example of

class Task { private int value; private final Lock lock = new ReentrantLock(); public Task() { this.value = 0; } public int get() { // Get the lock lock.lock(); try { return value; } finally { // Make sure the lock can be released lock.unlock(); } } public void addOne() { // Get the lock lock.lock(); try { // Be careful : The lock has been successfully acquired here , Get into get After the method , Try to get the lock again , // If the lock is not reentrant , Can cause deadlock value = 1 + get(); } finally { // Make sure the lock can be released lock.unlock(); } }}

1.2. Fair lock and unfair lock

  • Fair lock  - Fair lock means   Multithreads acquire locks in the order in which they apply for locks .
  • Not fair lock  - Unfair lock means   Multithreading does not acquire locks in the order in which they are requested  . This may cause priority reversal ( Later generations live in ) Or hunger ( One thread can't compete with other threads , Resulting in never being able to execute ).

Fair lock to ensure thread application sequence , There must be a performance cost , Therefore, its throughput is generally lower than the unfair lock .

Fair lock and unfair lock stay Java Typical implementation of :

  • synchronized  Only unfair locks are supported .
  • ReentrantLock 、ReentrantReadWriteLock, Default is unfair lock , But support fair locks .

1.3. Exclusive lock and shared lock

Exclusive lock and shared lock are a kind of broad sense , In terms of practical use , It is also often called mutually exclusive lock and read-write lock .

  • Exclusive lock  - Exclusive lock means   Locks can only be held by one thread at a time .
  • Shared lock  - Shared lock means   Locks can be held by multiple threads .

Exclusive lock and sharing lock in Java Typical implementation of :

  • synchronized 、ReentrantLock  Only exclusive locks are supported .
  • ReentrantReadWriteLock  Its writing lock is exclusive lock , The read lock is a shared lock . Read locks are shared locks that make concurrent reads very efficient , Reading and writing , Write to read , The process of writing is mutually exclusive .

1.4. Pessimistic lock and optimistic lock

Optimistic lock and pessimistic lock are not specific lock types , It's a strategy for dealing with concurrent synchronization .

  • Pessimistic locking  - Pessimistic lock takes a pessimistic attitude towards concurrency , Think : Concurrent operations without locks are bound to go wrong . Pessimistic lock is suitable for frequent write operations .
  • Optimism lock  - Optimistic lock is optimistic about concurrency , Think : There's no problem with concurrent operations without locks . For concurrent operations of the same data , It won't change . When updating data , The data will be updated in a continuous attempt to update . Optimistic lock is suitable for reading more and writing less .

Pessimistic lock and optimistic lock in Java Typical implementation of :

  • Pessimism is locked in Java The application in is through the use of  synchronized  and  Lock  Show lock for exclusive synchronization , This is a blocking synchronization .

  • Optimism is locked in Java The application in is to use  CAS  Mechanism (CAS  The operation passes  Unsafe  Class provides , But this class is not directly exposed as API, So it's all indirect use , Like all kinds of atoms ).

1.5. Biased locking 、 Lightweight lock 、 Heavyweight lock

So called lightweight lock and heavyweight lock , It refers to the thickness of lock control granularity . obviously , The finer the control granularity , The less blocking overhead , The more concurrent it is .

Java 1.6 before , A heavyweight lock generally means  synchronized , And lightweight locks mean  volatile.

Java 1.6 in the future , in the light of  synchronized  A lot of optimization , introduce 4 Lock state : No lock state 、 Biased locking 、 Lightweight lock and heavyweight lock . Locks can be upgraded from biased locks to lightweight locks in one direction , Then upgrade from lightweight lock to heavyweight lock .

  • Biased locking  - Biased lock means that a piece of synchronous code is always accessed by a thread , Then the thread will automatically acquire the lock . Reduce the cost of lock acquisition .

  • Lightweight lock  - When a lock is biased toward a lock , Accessed by another thread , Biased locks will be upgraded to lightweight locks , Other threads try to acquire the lock by spinning , It won't block , Improve performance .

  • Heavyweight lock  - When the lock is a lightweight lock , The other thread is spinning , But spin doesn't last , When you spin a certain number of times , We haven't got the lock yet , It's going to go into a block , The lock expands to a heavyweight lock . Heavyweight locks will block other threads applying for it , Performance degradation .

1.6. Section lock

The sectional lock is actually a kind of lock design , It's not a specific kind of lock . The so-called sectional lock , It is to divide the object of the lock into segments , Each segment is controlled independently , Make the lock finer , Reduce blocking overhead , To improve concurrency . It's actually quite understandable , It's like a toll booth on the highway , If there is only one toll gate , Then all the cars can only line up to pay ; If there are multiple toll gates , You can divert .

Hashtable  Use  synchronized  Decorate methods to ensure thread safety , So the access to the thread ,Hashtable Will lock the whole object , All other threads can only wait , The throughput of this blocking mode is obviously very low .

Java 1.7 Former  ConcurrentHashMap  It's a typical case of segmented lock .ConcurrentHashMap  One was maintained  Segment  Array , It's generally called segmented barrel .

final Segment<K,V>[] segments;

When there is thread access  ConcurrentHashMap  When the data is ,ConcurrentHashMap  According to hashCode Figure out which bucket the data is in ( Which one Segment), Then lock this  Segment.

1.7. Display locks and built-in locks

Java 1.5 Before , The only mechanism you can use to coordinate access to shared objects is  synchronized  and  volatile. Both are built-in locks , That is to say, the application and release of locks are made by JVM Controlled by .

Java 1.5 after , New mechanisms have been added :ReentrantLockReentrantReadWriteLock , The application and release of such locks can be controlled by procedures , So it's often called a display lock .

Be careful : If you don't need to  ReentrantLockReentrantReadWriteLock  Advanced synchronization features provided , Use should be a priority  synchronized . For the following reasons :

  • Java 1.6 in the future ,synchronized  A lot of optimization has been done , Its performance has been compared with  ReentrantLockReentrantReadWriteLock  Basically flat .
  • In terms of trend ,Java The future is more likely to be optimized  synchronized , instead of  ReentrantLockReentrantReadWriteLock , because  synchronized  yes JVM Built in properties , It can perform some optimizations .
  • ReentrantLockReentrantReadWriteLock  Application and release of lock are controlled by procedure , If not used properly , May cause deadlock , It's very dangerous .

The following comparison shows the difference between the lock and the built-in lock :

  • Actively acquire lock and release lock
    • synchronized  Can't actively acquire lock and release lock . Get lock and release lock are JVM The control of the .
    • ReentrantLock  Can actively acquire lock and release lock .( If you forget to release the lock , A deadlock may occur ).
  • In response to interrupt
    • synchronized  Unresponsive interrupt .
    • ReentrantLock  Can respond to interruptions .
  • Timeout mechanism
    • synchronized  There is no timeout mechanism .
    • ReentrantLock  There's a timeout mechanism .ReentrantLock  Timeout can be set , Automatically release the lock after timeout , Avoid waiting for .
  • Support fair locks
    • synchronized  Only unfair locks are supported .
    • ReentrantLock  Support unfair lock and fair lock .
  • Is sharing supported
    • By  synchronized  A decorated method or block of code , Can only be accessed by one thread ( exclusive ). If the thread is blocked , Other threads can only wait
    • ReentrantLock  Can be based on  Condition  Flexible control of synchronization conditions .
  • Is read-write separation supported
    • synchronized  Read / write lock separation is not supported ;
    • ReentrantReadWriteLock  Read and write lock support , So as to separate the blocking read and write operations , Effectively improve concurrency .

2. Lock and Condition

2.1. Why introduce Lock and Condition

Concurrent programming , There are two core issues : One is mutual exclusion , That is, only one thread is allowed to access shared resources at the same time ; The other is synchronization , That is, how threads communicate with each other 、 Collaboration . These two problems , Management can be solved .Java SDK And the contract is approved Lock and Condition Two interfaces to implement the tube , among Lock Used to solve the problem of mutual exclusion ,Condition Used to solve synchronization problems .

synchronized It's an implementation of tube engineering , In that case , Why offer more Lock and Condition.

JDK 1.6 before ,synchronized It hasn't been optimized yet , Performance is much lower than Lock. however , Performance is not the introduction of Lock The most important factor of . The real key is :synchronized Improper use , There may be deadlock .

synchronized Deadlock cannot be avoided by breaking the non preemptive condition . as a result of synchronized When you apply for resources , If the application does not arrive , Thread is directly in blocking state , And the thread goes into a blocked state , You can't do anything , It can't release the resources already occupied by the thread .

With built-in locks  synchronized  The difference is ,Lock  A set of unconditional 、 Pollable 、 Timed and interruptible lock operation , All access locks 、 Releasing locks is an explicit operation .

  • Be able to respond to interruptions .synchronized The problem is , Hold lock A after , If you try to acquire a lock B Failure , Then the thread goes into a blocked state , Once a deadlock occurs , There's no chance to wake up a blocked thread . But if the thread in the blocked state can respond to interrupt signals , That is to say, when we send an interrupt signal to a blocked thread , Can wake it up , Then it has a chance to release the lock it once held A. This will destroy the conditions that cannot be seized .
  • Support timeout . If the thread does not acquire the lock for a period of time , It's not blocking , It returns an error , Then this thread also has a chance to release the lock it once held . This can also destroy the conditions that cannot be seized .
  • Non blocking access to locks . If the attempt to acquire the lock fails , It doesn't get stuck , I'm going straight back , Then this thread also has a chance to release the lock it once held . This can also destroy the conditions that cannot be seized .

2.2. Lock Interface

Lock  The interface of is defined as follows :

public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition();}
  • lock() - Get the lock .
  • unlock() - Release the lock .
  • tryLock() - Attempt to acquire lock , Only if the lock is not held by another thread at the time of the call , To get the lock .
  • tryLock(long time, TimeUnit unit) - and  tryLock()  similar , The difference is only in time , If the lock is not acquired within the limited time , See it as a failure .
  • lockInterruptibly() - The lock is not held by another thread , And the thread is not interrupted , To get the lock .
  • newCondition() - Returns a binding to  Lock  On the object  Condition  example .

2.3. Condition

Condition The conditional variables in the pipe process model are realized .

As mentioned above  Lock  Interface There is one  newCondition()  Method to return a binding to  Lock  On the object  Condition  example .Condition  What is it? ? What's the role ? This section will explain one by one .

In a single thread , The execution of a piece of code may depend on a certain state , If the state conditions are not met , The code will not be executed ( Typical scenario , Such as :if ... else ...). In a concurrent environment , When a thread judges a state condition , Its state may be changed due to the operation of other threads , There needs to be a coordination mechanism to ensure that at the same time , Data can only be modified by one thread lock , And the modified data state is sensed by all threads .

Java 1.5 Before , Mainly to make use of  Object  Class  waitnotifynotifyAll  coordination  synchronized  To communicate between threads .

waitnotifynotifyAll  Need to cooperate with  synchronized  Use , Do not apply to  Lock. While using  Lock  The thread of , Communication with each other should use  Condition . This can be understood as , What kind of lock matches what kind of key . Built in lock (synchronized) With built-in conditional queues (waitnotifynotifyAll ), Explicit lock (Lock) With explicit conditional queues (Condition ).

Condition Characteristics of

Condition  The interface is defined as follows :

public interface Condition { void await() throws InterruptedException; void awaitUninterruptibly(); long awaitNanos(long nanosTimeout) throws InterruptedException; boolean await(long time, TimeUnit unit) throws InterruptedException; boolean awaitUntil(Date deadline) throws InterruptedException; void signal(); void signalAll();}

among ,awaitsignalsignalAll  And  waitnotifynotifyAll  Corresponding , Functions are similar . in addition to ,Condition  Compared to the built-in conditional queue ( waitnotifynotifyAll ), It provides more functions :

  • Each lock (Lock) There can be more than one  Condition, This means that there can be more than one lock state condition .
  • Support fair or unfair queue operations .
  • Supports interruptible conditional waiting , Related methods :awaitUninterruptibly() .
  • Support timed waiting , Related methods :awaitNanos(long) 、await(long, TimeUnit)awaitUntil(Date).

Condition Usage of

Here we use  Condition  To achieve a consumer 、 Producer mode .

Product class

class Message { private final Lock lock = new ReentrantLock(); private final Condition producedMsg = lock.newCondition(); private final Condition consumedMsg = lock.newCondition(); private String message; private boolean state; private boolean end; public void consume() { //lock lock.lock(); try { // no new message wait for new message while (!state) { producedMsg.await(); } System.out.println("consume message : " + message); state = false; // message consumed, notify waiting thread consumedMsg.signal(); } catch (InterruptedException ie) { System.out.println("Thread interrupted - viewMessage"); } finally { lock.unlock(); } } public void produce(String message) { lock.lock(); try { // last message not consumed, wait for it be consumed while (state) { consumedMsg.await(); } System.out.println("produce msg: " + message); this.message = message; state = true; // new message added, notify waiting thread producedMsg.signal(); } catch (InterruptedException ie) { System.out.println("Thread interrupted - publishMessage"); } finally { lock.unlock(); } } public boolean isEnd() { return end; } public void setEnd(boolean end) { this.end = end; }}

consumer

class MessageConsumer implements Runnable { private Message message; public MessageConsumer(Message msg) { message = msg; } @Override public void run() { while (!message.isEnd()) { message.consume(); } }}

producer

class MessageProducer implements Runnable { private Message message; public MessageProducer(Message msg) { message = msg; } @Override public void run() { produce(); } public void produce() { List<String> msgs = new ArrayList<>(); msgs.add("Begin"); msgs.add("Msg1"); msgs.add("Msg2"); for (String msg : msgs) { message.produce(msg); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } message.produce("End"); message.setEnd(true); }}

test

public class LockConditionDemo { public static void main(String[] args) { Message msg = new Message(); Thread producer = new Thread(new MessageProducer(msg)); Thread consumer = new Thread(new MessageConsumer(msg)); producer.start(); consumer.start(); }}

3. ReentrantLock

ReentrantLock  Class is  Lock  The concrete implementation of the interface , With built-in locks  synchronized  The same thing , It's a reentrant lock .

3.1. ReentrantLock Characteristics of

ReentrantLock  Its characteristics are as follows :

  • ReentrantLock  Provided with  synchronized  Same mutex 、 Memory visibility and reentrancy .
  • ReentrantLock  Support fair locks and unfair locks ( Default ) Two modes .
  • ReentrantLock  Realized  Lock  Interface , Support  synchronized  Flexibility that you don't have .
    • synchronized  Unable to interrupt a thread waiting to acquire a lock
    • synchronized  Can't wait endlessly while requesting a lock

3.2. ReentrantLock Usage of

I've learned about  ReentrantLock  Characteristics of , Next , Let's talk about its specific usage .

ReentrantLock Construction method of

ReentrantLock  There are two constructors :

public ReentrantLock() {}public ReentrantLock(boolean fair) {}
  • ReentrantLock() - The default constructor initializes an unfair lock (NonfairSync);
  • ReentrantLock(boolean) - new ReentrantLock(true)  A fair lock will be initialized (FairSync).

lock and unlock Method

  • lock() -  Acquire lock unconditionally . If the current thread cannot acquire the lock , Then the current thread is not available for hibernation , Until the current thread gets the lock . If the lock is not held by another thread , Then acquire the lock and return immediately , Set the hold count of the lock to 1.
  • unlock() - Used to release locks .

:bell: Be careful : Please remember , Acquire lock operation  lock()  Must be in  try catch  Out of the block , And will release the lock operation  unlock()  Put it in  finally  Carry out in block , To ensure that the lock must be released , Prevent deadlock .

Example :ReentrantLock  Basic operation

public class ReentrantLockDemo { public static void main(String[] args) { Task task = new Task(); MyThread tA = new MyThread("Thread-A", task); MyThread tB = new MyThread("Thread-B", task); MyThread tC = new MyThread("Thread-C", task); tA.start(); tB.start(); tC.start(); } static class MyThread extends Thread { private Task task; public MyThread(String name, Task task) { super(name); this.task = task; } @Override public void run() { task.execute(); } } static class Task { private ReentrantLock lock = new ReentrantLock(); public void execute() { lock.lock(); try { for (int i = 0; i < 3; i++) { System.out.println(lock.toString()); // Query the current thread hold The number of times to stay in this lock System.out.println("\t holdCount: " + lock.getHoldCount()); // Query the number of threads waiting to acquire this lock System.out.println("\t queuedLength: " + lock.getQueueLength()); // Is it a fair lock System.out.println("\t isFair: " + lock.isFair()); // Is it locked System.out.println("\t isLocked: " + lock.isLocked()); // Whether the lock is held by the current thread System.out.println("\t isHeldByCurrentThread: " + lock.isHeldByCurrentThread()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } finally { lock.unlock(); } } }}

Output results :

java.util.concurrent.locks.ReentrantLock@64fcd88a[Locked by thread Thread-A] holdCount: 1 queuedLength: 2 isFair: false isLocked: true isHeldByCurrentThread: truejava.util.concurrent.locks.ReentrantLock@64fcd88a[Locked by thread Thread-C] holdCount: 1 queuedLength: 1 isFair: false isLocked: true isHeldByCurrentThread: true// ...

tryLock Method

Compared with unconditional access lock ,tryLock There is a better fault tolerance mechanism .

  • tryLock() -  Can poll for lock . If it works , Then return to true; If you fail , Then return to false. in other words , Whether this method succeeds or fails, it will return immediately , Can't get lock ( The lock has been acquired by another thread ) Not always waiting for .
  • tryLock(long, TimeUnit) -  The lock can be acquired regularly . and  tryLock()  similar , The only difference is that this method will wait for a certain amount of time when the lock cannot be acquired , If the lock is not acquired within the time limit , Just go back to false. If you get the lock at the beginning or during the waiting period , Then return to true.

Example :ReentrantLock  Of  tryLock()  operation

Modify... In the previous example  execute()  Method

public void execute() { if (lock.tryLock()) { try { for (int i = 0; i < 3; i++) { // A little ... } } finally { lock.unlock(); } } else { System.out.println(Thread.currentThread().getName() + " Lock acquisition failed "); }}

Example :ReentrantLock  Of  tryLock(long, TimeUnit)  operation

Modify... In the previous example  execute()  Method

public void execute() { try { if (lock.tryLock(2, TimeUnit.SECONDS)) { try { for (int i = 0; i < 3; i++) { // A little ... } } finally { lock.unlock(); } } else { System.out.println(Thread.currentThread().getName() + " Lock acquisition failed "); } } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + " Acquire lock timeout "); e.printStackTrace(); }}

lockInterruptibly Method

  • lockInterruptibly() -  Interruptible access lock . The interruptible acquisition lock can maintain the response to the interrupt while acquiring the lock . Interruptible access lock is a little more complicated than other ways of access lock , You need two  try-catch  block ( If it is thrown during the lock acquisition operation  InterruptedException , Then you can use standard  try-finally  Lock mode ).
    • for instance : Suppose that two threads pass through at the same time  lock.lockInterruptibly()  When acquiring a lock , If a thread A Got the lock , The thread B Can only wait for . If at this time on the thread B call  threadB.interrupt()  Method to interrupt threads B The waiting process . because  lockInterruptibly()  An exception was thrown in the declaration of , therefore  lock.lockInterruptibly()  Must be on  try  Block or calling  lockInterruptibly()  The method declaration of  InterruptedException.

:bell: Be careful : When a thread gets a lock , Will not be  interrupt()  Method interrupt . Call alone  interrupt()  Method cannot interrupt a running thread , Only threads in blocked state can be interrupted . So when we pass  lockInterruptibly()  Method to acquire a lock , If the lock is not acquired , Only in the state of waiting , To respond to an interrupt .

Example :ReentrantLock  Of  lockInterruptibly()  operation

Modify... In the previous example  execute()  Method

public void execute() { try { lock.lockInterruptibly(); for (int i = 0; i < 3; i++) { // A little ... } } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + " Interrupted "); e.printStackTrace(); } finally { lock.unlock(); }}

newCondition Method

newCondition() - Returns a binding to  Lock  On the object  Condition  example .

3.3. ReentrantLock Principle

ReentrantLock The visibility of

class X { private final Lock rtl = new ReentrantLock(); int value; public void addOne() { // Get the lock rtl.lock(); try { value+=1; } finally { // Make sure the lock can be released rtl.unlock(); } }}

ReentrantLock, Hold one internally volatile Member variables of state, When getting the lock , Can read and write state Value ; When you unlock , I can read and write state Value ( The simplified code is shown below ). in other words , In execution value+=1 Before , The program reads and writes once volatile Variable state, In execution value+=1 after , Read and write again volatile Variable state. According to the relevant Happens-Before The rules :

  1. Sequential rules : For threads T1,value+=1 Happens-Before Release lock operation unlock();
  2. volatile Variable rule : because state = 1 Read first state, So threads T1 Of unlock() operation Happens-Before Threads T2 Of lock() operation ;
  3. Transitive rules : Threads T1 Of value+=1 Happens-Before Threads T2 Of lock() operation .

ReentrantLock Data structure of

read  ReentrantLock  Source code , You can see that it has a core field :

private final Sync sync;
  • sync - Internal abstract class  ReentrantLock.Sync  object ,Sync  Inherited from AQS. It has two subclasses :
  • ReentrantLock.FairSync - Fair lock .
  • ReentrantLock.NonfairSync - Not fair lock .

Check the source code to find out ,ReentrantLock  Realization  Lock  An interface is actually a call  ReentrantLock.FairSync  or  ReentrantLock.NonfairSync  The realization of each , Here's not a list .

ReentrantLock Get lock and release lock

ReentrantLock Get lock and release lock interface , From the appearance , Is to call  ReentrantLock.FairSync  or  ReentrantLock.NonfairSync  The realization of each ; In essence , Is based on AQS Of .........

版权声明
本文为[Irving the procedural ape]所创,转载请带上原文链接,感谢

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云