thread locks for java

In Java, thread locks are implemented using the synchronized keyword and the java.util.concurrent.locks package.

The synchronized keyword is used to create a thread lock on a method or block of code. When a thread enters a synchronized method or block, it acquires the lock for the object or class associated with the method or block. Other threads that attempt to enter the same synchronized method or block are blocked until the lock is released.

For example, the following code demonstrates how to use the synchronized keyword to create a thread lock on a method:

In this example, the updateSharedResource() method is marked as synchronized, which means that only one thread can execute the method at a time.

The java.util.concurrent.locks package provides several classes for creating thread locks, such as ReentrantLock, ReadWriteLock, StampedLock and Semaphore. These classes provide more fine-grained control over locking and can be useful in situations where the basic functionality provided by the synchronized keyword is not sufficient.

For example, the following code demonstrates how to use the ReentrantLock class to create a thread lock:

In this example, the updateSharedResource() method acquires the lock using the lock() method before updating the shared resource, and releases the lock using the unlock() method when it’s done.

It’s important to note that, overuse of thread locks can lead to deadlocks, where multiple threads are blocked, each waiting for one of the others to release a lock. These can be difficult to debug and should be avoided by carefully designing the system.

How to install jdk on mac

To install the Java Development Kit (JDK) on a Mac, you can follow these steps:

  1. Download the JDK installer package from the Oracle website (
  2. Open the downloaded installer package by double-clicking on it.
  3. Follow the prompts to install the JDK.
  4. Once the installation is complete, open the Terminal application and run the command “java -version” to verify that the JDK was installed correctly and to see which version of Java is currently set as the default.
  5. To configure JDK on your mac, you can set the JAVA_HOME path in your .bash_profile or .bashrc file.
  6. You can also use package manager like brew to install JDK. Run the command brew cask install java to install JDK via brew.

How to use eclipse for java

Here are the general steps for using Eclipse for Java development:

  1. Download and install Eclipse on your computer.
  2. Launch Eclipse and create a new Java project by going to “File” > “New” > “Java Project”.
  3. In the new project, create a new Java class by right-clicking on the project in the “Package Explorer” and selecting “New” > “Class”.
  4. Write your Java code in the class file.
  5. Use Eclipse’s built-in compiler to check for errors by selecting “Project” > “Build Project” or using the keyboard shortcut “Ctrl + B”.
  6. Run your code by right-clicking on the class file in the “Package Explorer” and selecting “Run As” > “Java Application”.
  7. Debug your code by setting breakpoints and using the “Debug” perspective in Eclipse.

It’s also recommended to install some additional plugins for Eclipse as well like “Eclipse Marketplace” and “Eclipse Java Development Tools (JDT)”.

Please note that these are general steps and there may be some variations depending on your specific project setup and requirements.

How to code in java

Java is a popular programming language that is widely used for developing desktop, mobile, and web applications. Here are some basic steps for getting started with coding in Java:

  1. Download and install the Java Development Kit (JDK) from Oracle’s website. The JDK includes the Java Runtime Environment (JRE) as well as the Java compiler and other tools needed for development.
  2. Choose a code editor or integrated development environment (IDE) to write and edit your code. Some popular choices include Eclipse, IntelliJ IDEA, and NetBeans.
  3. Create a new project in your IDE and add a new class file. This is where you will write your Java code.
  4. Familiarize yourself with the basic syntax of the Java language. This includes concepts like variables, data types, control structures, and methods.
  5. Start coding! Write a simple program such as a “Hello World” application to get a feel for the language.
  6. Compile and run your code to see the results. Make sure to debug and troubleshoot any errors that may occur.
  7. Continue learning and experimenting with more advanced concepts such as object-oriented programming, collections, and concurrency.

It’s important to practice and build your coding skills by solving various problems and implementing different types of applications. Online resources such as tutorials, documentation, and forums can also be helpful for learning Java.

Operators in Java

Java offers many operators that can be used to perform tasks on variables. These operators can be divided into the following categories:

  1. Arithmetic Operators: These operators are used to perform mathematical operations on variables such as addition (+), subtraction (-), multiplication (*), division (/), and modulo (%).
  2. Assignment Operators: These operators are used to assign a value to a variable (=, +=, -=, *=, and /=).
  3. Relational Operators: These operators are used to compare two variables to create a Boolean expression and determine if the statement is true or false (<, >, <=, >=, ==, and !=).
  4. Logical Operators: These operators are used to evaluate boolean expressions and return either a true or false result (&&, ||, !).
  5. Increment/Decrement Operators: These operators are used to add or subtract one from a numeric value (++, –).
  6. Bitwise Operators: These operators are used to manipulate the binary values of a variable (~, &, |, ^).
  7. Miscellaneous Operators: These operators are used to perform miscellaneous operations such as type casting, instanceof operator, conditional operator, and others.

Using SLF4J and Logback

The good friends Commons Logging and Log4j are introduced earlier. One of them is responsible for the log API, and the other is responsible for implementing the bottom layer of the log. It is very easy to develop when used together.

Some children’s shoes may also have heard of SLF4J and Logback. These two things also look like logs, what are they?

In fact, SLF4J is similar to Commons Logging and is also a log interface, while Logback is similar to Log4j and is a log implementation.

Why with Commons Logging and Log4j, SLF4J and Logback pop up again? This is because Java has a very long history of open source. Not only is OpenJDK itself open source, but almost all of the third-party libraries we use are open source. A particular feature of the rich open source ecosystem is that for the same function, several competing open source libraries can be found.

Because of dissatisfaction with the interface of Commons Logging, some people engaged in SLF4J. Because of dissatisfaction with the performance of Log4j, some people engaged in Logback.

Let’s first take a look at how SLF4J improves the Commons Logging interface. In Commons Logging, we want to print the log, sometimes we have to write:

Spelling strings is a very troublesome thing, so the log interface of SLF4J has been improved as follows:

We can guess by guessing. The log interface of SLF4J passes in a string with placeholders, and the placeholders are automatically replaced with the following variables, so it looks more natural.

How to use SLF4J? Its interface is actually almost identical to Commons Logging:

Compare the interfaces of Commons Logging and SLF4J:

Commons LoggingSLF4J

The difference is that Log becomes Logger, and LogFactory becomes LoggerFactory.

Using SLF4J and Logback is similar to using Commons Logging and Log4j as mentioned earlier. First download SLF4J and Logback respectively , and then put the following jar packages on the classpath:

  • slf4j-api-1.7.x.jar
  • logback-classic-1.2.x.jar
  • logback-core-1.2.x.jar

Then use SLF4J’s Logger and LoggerFactory. Similar to Log4j, we still need a Logback configuration file, logback.xmlput it on the classpath, and configure it as follows:

Run it to get output similar to the following:

Judging from the current trend, more and more open source projects have shifted from Commons Logging plus Log4j to SLF4J plus Logback.


According to the configuration file, observe the log files written by Logback.



SLF4J and Logback can replace Commons Logging and Log4j;

Always use SLF4J’s interface to write logs. Using Logback only requires configuration and no code modification.

Java NullPointerException

Of all the RuntimeExceptionexceptions, Java programmers are probably most familiar with NullPointerException.

NullPointerException is the null pointer exception, commonly known as NPE. This exception is usually thrown by the JVM if an object is null called, its methods are called or its fields are accessed , for example:NullPointerException

The concept of pointers is actually derived from the C language, and there are no pointers in the Java language. The variables we define are actually references, Null Pointer is more precisely Null Reference, but the difference between the two is not much.

Handling NullPointerException

If encountered NullPointerException, how should we deal with it? First of all, it must be clear that it NullPointerExceptionis a code logic error. When encountered NullPointerException, follow the principle of early exposure and early repair. It is strictly prohibited to use catchto hide such coding errors:

Good coding practices can greatly reduce NullPointerExceptionthe occurrence of, for example:

Member variables are initialized when they are defined:

A lot can be avoided by using an empty string ""instead of the default , and when writing business logic, it is much safer to use an empty string for unfilled .nullNullPointerException""null

Returns an empty string "", an empty array instead of null:

This saves the caller from having to check whether the result is null.

If the caller must make nulljudgments, such as returning to nullindicate that the file does not exist, then consider returning Optional<T>:

In this way, the caller must pass the Optional.isPresent()judgment whether there is a result.

Locating NullPointerException

If generated NullPointerException, for example, a.b.c.x()when called, the NullPointerExceptionreason may be:

  • ayes null;
  • a.byes null;
  • a.b.cyes null;

Determining exactly which object was nullpreviously only able to print logs like this:

Starting from Java 14, if generated NullPointerException, the JVM can give detailed information to tell us nullwho the object is. Let’s look at an example:

You can NullPointerExceptionsee similar in the details of ... because "<local1>" is null, meaning the cityfield is null, so we can quickly locate the problem.

This enhanced NullPointerExceptiondetail is new in Java 14, but is off by default, we can -XX:+ShowCodeDetailsInExceptionMessagesenable it by adding a parameter to the JVM:


NullPointerExceptionIt is a common logic error in Java code, which should be exposed and repaired early;

NullPointerExceptionDetailed error information can be viewed by enabling Java 14’s Enhanced Exception Information.

How to use Log4j

Commons Logging was introduced earlier, which can be used as a “log interface”. And the real “logging implementation” can use Log4j.

Log4j is a very popular logging framework, the latest version is 2.x.

Log4j is a component-based log system, and its architecture is roughly as follows:

When we use Log4j to output a log, Log4j automatically outputs the same log to different destinations through different Appenders. E.g:

  • console: output to the screen;
  • file: output to a file;
  • socket: output to a remote computer through the network;
  • jdbc: output to database

In the process of outputting logs, Filter is used to filter which logs need to be output and which logs do not need to be output. For example, only output ERRORlevel logs.

Finally, format the log information through Layout, for example, automatically add information such as date, time, method name, etc.

Although the above structure is complex, when we actually use it, we do not need to care about the API of Log4j, but configure it through the configuration file.

Taking XML configuration as an example, when using Log4j, we can put a log4j2.xmlfile classpathdown to let Log4j read the configuration file and output the log according to our configuration. The following is an example of a configuration file:

Although configuring Log4j is tedious, once configured, it is very convenient to use. For the above configuration file, all INFOlevel logs will be automatically output to the screen, and ERRORlevel logs will not only be output to the screen, but also output to the file at the same time. And, once the log file reaches the specified size (1MB), Log4j will automatically cut the new log file and keep up to 10 copies.

It is not enough to have a configuration file, because Log4j is also a third-party library, we need to download Log4j from here , after decompression, put the following 3 jar packages into classpathit:

  • log4j-api-2.x.jar
  • log4j-core-2.x.jar
  • log4j-jcl-2.x.jar

Because Commons Logging will automatically discover and use Log4j, put the downloaded one in the previous commons-logging-1.2.jarsection classpath.

To print the log, you only need to write it in the way of Commons Logging, without changing any code, you can get the log output of Log4j, similar to:

Best Practices

In the development phase, the Commons Logging interface is always used to write logs, and there is no need to introduce Log4j in the development phase. If you need to write the log to a file, you only need to put the correct configuration file and the jar package related to Log4j classpath, and you can automatically switch the log to use Log4j to write without modifying any code.


According to the configuration file, observe the log file written by Log4j.

commons logging + log4j


Logs are implemented through Commons Logging, and Log4j can be used without modifying the code;

To use Log4j, you only need to put log4j2.xml and related jars into the classpath;

If you want to replace Log4j, you only need to remove log4j2.xml and related jars;

Only when you extend Log4j, you need to refer to the interface of Log4j (for example, the function of encrypting logs and writing them to the database needs to be developed by yourself).

Detailed WebSecurityConfigurerAdapter

This article will detail the WebSecurityConfigurerAdapter in security.

Today we are going to further learn how to customize Spring Security. We have mentioned WebSecurityConfigurerAdapter many times, and we know that the automatic configuration in Spring Boot is actually the Spring Boot Web Security imported through the SecurityAutoConfiguration general configuration class under the automatic configuration package. Configuration class SpringBootWebSecurityConfiguration to configure.

SpringBootWebSecurityConfiguration source code

Common method
WebSecurityConfigurerAdapter class diagram

Common configuration
Let’s paste the common configuration of WebSecurityConfigurerAdapter in the project

I believe someone has noticed that I have overridden (@Override) three configure methods in the WebSecurityConfigurerAdapter above. We generally customize our security access policy by customizing these three methods.

Authentication Manager Configuration Methods
AuthenticationManagerBuilder (Authentication Manager Builder)

void configure(AuthenticationManagerBuilder auth) is used to configure AuthenticationManager. To put it bluntly, it manages all UserDetails related, including PasswordEncoder passwords, etc. If you are not clear, you can find out through UserDetail in Spring Security. This article does not do a specific analysis and explanation of AuthenticationManager, and there will be a special article about this later.

Common usage

Core filter configuration method
WebSecurity (WEB security)

void configure(WebSecurity web) is used to configure WebSecurity . And WebSecurity is based on Servlet Filter to configure springSecurityFilterChain . The springSecurityFilterChain is delegated to the Spring Security core filter Bean DelegatingFilterProxy . The related logic can be found in WebSecurityConfiguration. We generally don’t customize WebSecurity too much, and use more of its ignoring() method to ignore Spring Security’s control of static resources.

Security filter chain configuration method
HttpSecurity (HTTP request security processing)

void configure(HttpSecurity http) This is what we use the most to configure HttpSecurity . HttpSecurity is used to build a security filter chain SecurityFilterChain . The SecurityFilterChain is eventually injected into the core filter. HttpSecurity has many configurations that we need. We can use it to customize the security access policy. So we have a separate chapter to explain this stuff.

HttpSecurity configuration
HttpSecurity is the focus of the following articles, and we will actually operate it to achieve some practical functions. So this article will focus on it.

HttpSecurity class diagram

Default Allocation

The above is the default configuration of Spring Security in Spring Boot. Through the above configuration, your application has the following functions:

  • All requested access requires authorization.
  • Use the form form to log in (the default path is /login), which is the login page we saw in the previous articles.
  • Prevent CSRF attack, XSS attack.
  • Enable HTTP Basic Authentication

Summary of this article
This article introduces the knowledge and content related to WebSecurityConfigurerAdapter in detail, and will introduce the knowledge related to HttpSecurity in detail later, which is also the most used place in normal development.

The difference between BIO, NIO, and AIO

Many articles just throw a bunch of definitions and some vivid examples when talking about BIO, NIO, and AIO. Seems to be well understood. However, the most basic essential principles are not revealed. If you do not start from the principle of IO, it is difficult to understand the difference between the three. In this article, you will get: the performance difference between synchronous/asynchronous + blocking/non-blocking; the difference between BIO, NIO, and AIO; understanding and implementing multiplexing when NIO operates Sockets; at the same time, master the bottom and core operation skills of IO.

IO principle in Java

First of all, IO in Java depends on the operating system kernel. The IO reading and writing in our program actually calls the two system calls of read and write in the operating system kernel.

How does the kernel interact with IO?

  1. The network card receives the network data transmitted through the network cable, and writes the network data to the memory.
  2. 2When the network card writes the data into the memory, the network card sends an interrupt signal to the cpu, and the operating system can know that there is new data coming, and then process the data through the network card interrupt program.
  3. Write the network data in the memory to the receive buffer of the corresponding socket.
  4. After the data in the receive buffer is written, the application starts data processing.

A simple example of socket code corresponding to abstraction to java is as follows:

It can be seen that this process is very similar to the network IO of the underlying kernel, which is mainly reflected in accept() waiting for a request from the network, and then the bytes[] array is used as a buffer to wait for the data to be filled before processing. The difference between BIO, NIO, and AIO is whether these operations are synchronous or asynchronous, blocking or non-blocking.

So we introduce the concepts of synchronous and asynchronous, blocking and non-blocking.

Synchronous and Asynchronous

Synchronization and asynchrony: Synchronization means that when the completion of a task needs to depend on another task, the dependent task can be completed only after waiting for the dependent task to complete. This is a reliable task sequence. Either all successes succeed, and all failures fail, and the status of the two tasks can be kept the same. Asynchronous means that there is no need to wait for the dependent task to complete, but only to notify the dependent task of what work to complete, and the dependent task is executed immediately, as long as the entire task is completed by itself. As for whether the dependent task is actually completed in the end, the task that depends on it cannot be determined, so it is an unreliable task sequence. We can use phone calls and text messages to describe synchronous versus asynchronous operations well.

Synchronization and asynchrony refer to whether each method in an execution flow must depend on the completion of the previous method before it can continue to execute. Suppose our execution process is: method one and method two in turn.

Synchronization means that once the call has started, the caller must wait until the method call returns before continuing the subsequent behavior. That is, method 2 must wait until method 1 is executed before it can be executed.

Asynchronous means that the call returns immediately, and the caller can continue the subsequent behavior without waiting for the execution of the code in the method to end. (The code in the specific method may be called back after it is executed by another thread.) That is, when method 1 is executed, it is directly handed over to other threads for execution, and it is not executed by the main thread, so it will not block the main thread, so method 2 does not have to wait until method 1 is completed to start execution.

Synchronization and asynchrony focus on whether the executor of the method is the main thread or another thread. The main thread needs to wait for the method execution to complete, and other threads do not need to wait for the method call to return immediately, and the main thread can directly execute the next code.

Synchronous and asynchronous is to achieve efficiency difference from the coordination between multiple threads.

Why do you need asynchrony? The author believes that the essence of asynchrony is to solve the blocking of the main thread, so many discussions on the Internet have made four combinations of synchronous asynchronous and blocking non-blocking. One of them has the situation of asynchronous blocking. If asynchronous is also blocking? So why do you want to perform asynchronous operations specifically?

Blocking and Non-Blocking
Blocking and non-blocking: Blocking and non-blocking are mainly in terms of CPU consumption. Blocking means that the CPU stops and waits for a slow operation to complete the CPU before completing other things. Non-blocking means that the CPU does other things while the slow operation is executing, and when the slow operation is completed, the CPU then completes the subsequent operations. Although the non-blocking method on the surface can significantly improve the utilization of the CPU, it also brings another consequence that the thread switching of the system increases. Whether the increased CPU usage time can compensate for the switching cost of the system needs to be carefully evaluated.

Blocking and non-blocking refer to whether to do nothing in place when a single thread encounters a synchronous wait.

Blocking means that after encountering a synchronization wait, it has been waiting for the synchronization method to complete the processing in place.

Non-blocking refers to encountering synchronous waiting, not waiting in place, do other operations first, and then observe whether the synchronization method is completed after a time interval.

Blocking and non-blocking concern whether the thread is waiting in place.

The author believes that blocking and non-blocking can only be combined with synchronization. Asynchronous is naturally non-blocking, and this non-blocking is for the main thread. (Some people may think that putting a blocking operation in an asynchronous method is asynchronous blocking, but think about it, it is precisely because it is a blocking operation that it is put into an asynchronous method, do not block the main thread)

Example to explain
Haidilao is delicious, but there are often queues. Let’s take this example from life to explain.

Customer A went to eat Haidilao and just sat and waited for an hour before eating hot pot. (BIO)
Customer B went to eat Haidilao. He saw that he had to wait for a long time, so he went to the mall, and every time he went shopping for a while, he ran back to see if he was in line. So he ended up shopping and eating Haidilao. (NIO)
Customer C went to eat Haidilao. Since he is a senior member, the store manager said, you can go to the mall to play casually, and I will call you immediately when there is a seat. So customer C doesn’t have to sit and wait, and don’t have to run back every once in a while to see if they can wait, and finally eat Haidilao (AIO)
Which way is more efficient? Is it obvious at a glance?

BIO is a traditional package. It is implemented based on the stream model. The interaction method is synchronous and blocking. That is to say, when reading the input stream or output stream, the thread will be blocked until the read and write actions are completed. There, calls between them are in a reliable linear order. Its advantage is that the code is relatively simple and intuitive; the disadvantage is that the efficiency and scalability of IO is very low, and it is easy to become an application performance bottleneck.

The full name of BIO is Blocking IO, which is the traditional IO model before JDK1.4, and itself is a synchronous blocking mode. After the thread initiates an IO request, it keeps blocking the IO until the buffer data is ready, and then goes to the next step. For network communication, it is a request-response method. Although the upper-layer application development is simplified, there is a huge bottleneck in terms of performance and reliability. Imagine if each request needs to create a new thread for special processing, then in high In a concurrent scenario, machine resources are quickly exhausted.

NIO is the java.nio package introduced in Java 1.4. It provides new abstractions such as Channel, Selector, Buffer, etc. It can build multiplexed, synchronous non-blocking IO programs, and at the same time provides high-performance data operations that are closer to the underlying operating system. Way.

NIO, also called Non-Blocking IO, is a synchronous non-blocking IO model. After the thread initiates an io request, it returns immediately (non-blocking io). Synchronization means that you must wait for the data in the IO buffer to be ready. Non-blocking means that the user thread does not wait for the IO buffer in place. You can do some other operations first, but you need to poll regularly to check whether the IO buffer data is ready. .

NIO in Java means new IO. In fact, it is NIO plus IO multiplexing technology. Ordinary NIO is thread polling to see if an IO buffer is ready, while new IO in Java refers to threads polling to see which ones are ready in a bunch of IO buffers, which is an idea of ​​IO multiplexing . In the IO multiplexing model, the task of checking whether the IO data is ready is handed over to the system-level select or epoll model, which is monitored by the system to reduce the burden on user threads.

NIO mainly integrates three technologies: buffer, channel, and selector. Data is obtained through zero-copy buffer, and each client registers on the selector (multiplexer) through channel. The server continuously polls the channel to obtain information about the client. There are four status flags on the channel: connect, accept (blocking), read (readable), and write (writable). Follow up with the identification. So a server can receive an infinite number of channels. No need to open a new thread. Greatly improved performance.

AIO is a package introduced after Java 1.7. It is an upgraded version of NIO. It provides an asynchronous non-blocking IO operation mode, so people call it AIO (Asynchronous IO). Asynchronous IO is implemented based on events and callback mechanisms, that is, application operations. After that, it will return directly and will not be blocked there. When the background processing is completed, the operating system will notify the corresponding thread to perform subsequent operations.

AIO is a true asynchronous non-blocking IO model. In the above NIO implementation, the user thread needs to poll regularly to check whether the IO buffer data is ready, which occupies the application thread resources. In fact, polling is equivalent to blocking, and does not really liberate the current thread, because it still needs to query which IO ready. The real ideal asynchronous non-blocking IO should let the kernel system complete, and the user thread only needs to tell the kernel that when the buffer is ready, notify me or execute the callback function I handed you.

AIO can perform real asynchronous operations, but it is more complicated to implement. There are very few operating systems that support pure asynchronous IO. Currently, windows are implemented by IOCP technology, while on Linux, the bottom layer is still implemented by epoll.