I’ve been programming Java for 11 years, and I’m definitely a veteran; But for Java concurrent programming, I was a rookie. I’m sure some of the best people will laugh at me for saying this, but I’m not afraid.

Because I know that every year, many, many new people join the Java programming army and feel helpless for the problems encountered in “concurrent” programming. I would love to join them in a new round of learning the basics of concurrent program development using Java threads.

01. Why do we learn concurrency?

My head has not been tathagata Buddha opened light, so like one thing after another thing to think, can not do “one brain dual use”. But some leaders are different, such as Zhuge Liang, can think about the music while playing the qin, but also with sima Yi’s plan after the retreat.

Big guy of various ge has super “concurrent” ability. If I were to face Sima Yi’s army, I would not only be unable to play the piano, but also be scared to death.

Everyone has only one brain, just like a computer has only one CPU. However, one brain does not mean that one brain cannot be used in two ways. The key lies in the ability of the brain to be “concurrent”.

If the brain has concurrent ability, it is really fierce to fly ah, think sima Yi was calm zhuge away look know.

The efficiency of a program can be greatly improved if it is capable of concurrency. You must have registered a lot of websites, received a lot of verification code, if the server side of the website sent verification code, there is no special thread to deal with (concurrent), if the network is not blocked, the server side will not know from dawn until dark have you received verification code? It doesn’t matter if it’s just you, but what about a hundred users? These 100 users also want to wait in that silly silly, that really have to wait until the flowers are thanked.

You can imagine how important concurrent programming is! Furthermore, knowledge of Java virtual machines and concurrent programming is almost a rule of thumb for determining whether a Java developer is an expert or not. So if you want to make a lot of money, you have to know how to create!

Step 1: Create a thread

Usually, when you start a program, you start a process. Every computer runs many programs, so you’ll see many processes in the process manager. Isn’t that nonsense, you say?

No, no, no. For a long time when I was learning to program, I took it for granted that these processes were threads; But then I knew it wasn’t true. There may be many threads running in a process, or only one.

The main function is really just the main thread. We can create many other threads in this main thread. Take a look at this code.

public class Wanger { public static void main(String[] args) { for (int i = 0; i < 10; I++) {Thread t = new Thread(new Runnable() {@override public void run() {system.out.println (" I "+) Thread.currentthread ().getName() + "; }}); t.start(); }}}Copy the code

The most common way to create a thread is to declare an anonymous inner class that implements the Runnable interface; It is then used as an argument to create the Thread object; The Thread object’s start() method is then called to start it. The result of the run is as follows.

My name is Thread-1, and I love the writing style of King Silence ii. My name is Thread-3, and I love the writing style of King Silence II. My name is Thread-0. I love the writing style of King Silence ii and my name is Thread-4, I love the writing style of King Silence II and my name is Thread-6, I love the writing style of King Silence II and my name is Thread-7, I love the writing style of King Silence II and my name is Thread-8, I love the writing style of King Silence II and my name is Thread-9, I love the writing style of The Silent KingCopy the code

As can be seen from the results of the run, threads are not executed in order from 0 to 9, but have a certain degree of randomness. This is because Java’s concurrency is preemptive, and thread 0, although created first, has a less “competitive” ability and has a harder time getting ahead.

Step 2: Create a thread pool

Java. Util. Concurrent. Executors class provides a series of factory method is used to create a thread pool, can put multiple threads together for a more efficient management. The following is an example.

public class Wanger { public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 10; I++) {Runnable r = new Runnable() {@override public void run() {system.out.println (" I call "+ Thread.currentthread ().getName() + "; }}; executorService.execute(r); } executorService.shutdown(); }}Copy the code

The result of the run is as follows.

My name is Pool1-thread-2, and I like my writing style very much. My name is pool1-thread-4, and I like my writing style very much. My name is pool1-thread-3. I like the writing style of Silent King 2. My name is Pool1-thread-4. I like the writing style of Silent King 2. My name is Pool-1-thread-6, and MY writing style is pool-1-thread-5. My writing style is pool-1-thread-6. My writing style is pool-1-thread-6Copy the code

Executors’ newCachedThreadPool() method creates a cacheable thread pool. The execute() method can reuse previous threads as long as the thread is available. For example, pool-1-thread-4, pool-1-thread-5, and pool-1-thread-6 are reused. The best image spokesperson I can think of is empress Wu Zetian.

If no thread is available, a new thread is created and added to the pool. Of course, threads that have not been used in 60 seconds are removed from the cache.

In addition, the newFiexedThreadPool(int Num) method used by Executors creates a thread pool with a fixed number of threads. The newSingleThreadExecutor() method is used to create a single threaded thread pool (can you think of a situation where it should be used?). .

But here’s the twist. Alibaba’s Java development manual clearly states that the use of Executors is not allowed to create thread pools.


If you can’t create a thread pool by using Executors, how do you do it?

Create a thread pool by calling the constructor of ThreadPoolExecutor directly. Executors do exactly that, but don’t specify the BlockQueue capacity. All we need to do is specify the capacity at creation time. An example of this code is shown below.

ExecutorService executor = new ThreadPoolExecutor(10, 10,        60L, TimeUnit.SECONDS,        new ArrayBlockingQueue(10));Copy the code

The third step is to solve the problem of shared resource competition

Once, I accompanied my family to go shopping in the mall. When I was getting out of the elevator, a stupid guy insisted on getting into the elevator. My daughter’s cart fell on his foot, and he screamed and pointed at my nose. I punched him right in the nose, and we got tangled up.

What does this show? First, it is really troublesome to meet stupid people who are not civilized and do not know the “first out, last in” (LIFO) rule. Second, when competing for shared resources, they should fight each other.

In Java, the first solution to the problem of competing for shared resources is to use the keyword synchronized. When a thread executes a piece of synchronized protected code, it locks that code, and other threads calling that code block until the lock is released.

The following code uses ThreadPoolExecutor to create a pool of threads in which each thread performs a +1 operation on the shared resource count. Now, close your eyes and think, what is the value of count going to be when 1000 threads have finished executing?

public class Wanger { public static int count = 0; public static int getCount() { return count; } public static void addCount() { count++; } public static void main(String[] args) { ExecutorService executorService = new ThreadPoolExecutor(10, 1000, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10)); for (int i = 0; i < 1000; i++) { Runnable r = new Runnable() { @Override public void run() { Wanger.addCount(); }}; executorService.execute(r); } executorService.shutdown(); System.out.println(Wanger.count); }}Copy the code

In fact, the value of shared resource count is likely to be 996, 998, but rarely 1000. Why is that?

Because while one thread is writing the variable, another thread may be reading the variable or writing the variable. This variable becomes an “indeterminate state” of data. This variable must be protected.

A common practice is to add the synchronized keyword to the addCount() method that changes the variable — ensuring that threads queue up in an orderly fashion when accessing the variable.

The following is an example:

public synchronized static void addCount() { count++; }Copy the code

There is another common approach – read/write locks. Multiple read locks are not mutually exclusive. Read locks and write locks are mutually exclusive and controlled by the Java VM. If the code allows many threads to read at the same time, but cannot write at the same time, lock the read; If the code is not allowed to read at the same time and only one thread is writing, the write lock is applied.

The interface for reading and writing locks is ReadWriteLock, and the implementation class is ReentrantReadWriteLock. Synchronized is a mutex. Only one thread is allowed to read and write at any time, and other threads must wait. ReadWriteLock allows multiple threads to acquire read locks, but only one thread to acquire write locks, which is relatively efficient.

Let’s start by creating a singleton that reads and writes locks using enumerations. The code is as follows:

public enum Locker {    INSTANCE;    private static final ReadWriteLock lock = new ReentrantReadWriteLock();    public Lock writeLock() {        return lock.writeLock();    }}Copy the code

AddCount ++ in the addCount() method. Locked. The following is an example.

Public static void addCount () {/ / Lock Lock writeLock = Locker. INSTANCE. WriteLock (); writeLock.lock(); count++; // Unlock writelock.unlock (); }Copy the code

When using a read/write lock, remember to release the lock at the end.

05, finally

Is concurrent programming hard to learn? To be honest, it wasn’t easy. Take a look at the mind map summarized by Teacher Wang Baoling.


But as you know, Rome wasn’t built in a day and learning is a gradual thing. Once you’ve learned how to create a thread, how to create a thread pool, and how to solve the problem of competing for shared resources, you’ve made a big step in the world of concurrent programming.

Fill yourself up, will you?

Silent King 2, a programmer who not only writes code, but also writes interesting and helpful words for you who don’t like serious.

Author’s praise code


Java Geek technology public account, is set up by a group of technical people who love Java development, focus on sharing original, high quality Java articles. If you think our article is good, please help to appreciate, read, forward support, encourage us to share better articles.