I’ve been programming Java for 11 years, so I’m definitely a veteran; But WHEN it comes to Java concurrent programming, I’m a rookie. I’m going to get some sneer from some good people for saying this, but I’m not afraid.

Because I know that many, many new people will join the Java programming army every year, and there will be times when they feel helpless about the problems of “concurrent” programming. I would love to work with them on a new round of learning the basics of concurrent program development using Java threads.

01. Why should we learn concurrency?

My head has not been the Buddha opened light, so I like to think of one thing after another, can not do “one brain at a time”. However, some leaders were different, such as Zhuge Liang, who was able to think of the music while talking and playing the piano, and also to figure out sima Yi’s plans after the army’s withdrawal.

Zhuge big guy has super “concurrent” ability. If I were to face Sima Yi’s tens of thousands of troops, I would not only be unable to play the instrument, but I would be scared out of my wits.

Everyone has only one brain, just like a computer has only one CPU. But just because you have one brain doesn’t mean you can’t do both, the key is that your brain is capable of concurrency.

If the brain has concurrent ability, it is really severe to fly, think of Sima Yi by calm and calm zhuge big guy scared away look know.

For programs, the ability to have concurrency can greatly improve efficiency. You must have registered a lot of websites, received a lot of verification code, if the server of the website in the send verification code, no special up a thread to deal with (concurrent), if the network is not good blocking, then the server will not know from dawn until dark you have received the verification code? Not if you’re the only user, but what if you have 100 users? This 100 users also want to be in that silly to wait, that really want to wait until the flowers are all withered.

You can imagine how important concurrent programming is! What’s more, knowing the Java virtual machine and concurrent programming is almost the rule of three to determine whether a Java developer is a master. So if you want to earn more, you have to be able to concurrently!

The first step is to create a thread

In general, when you start a program, you start a process. Every computer runs many programs, so you will see many processes in the process manager. Don’t you think that’s nonsense?

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 there may be only one.

The main function is actually the main thread. We can create many other threads in this main thread. Look at the following 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(a) {
					System.out.println("My name is" + Thread.currentThread().getName() + "I love the writing style of King Silence ii."); }}); 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. Then use it as an argument to create the Thread object; The Thread object’s start() method is then called to start it. The result of this run is as follows.

My name is Thread -1I love the writing style of King Silence II. My name is Thread-3I love the writing style of King Silence II. My name is Thread-2I love the writing style of King Silence II. My name is Thread-0I love the writing style of King Silence II. My name is Thread-5I love the writing style of King Silence II. My name is Thread-4I love the writing style of King Silence II. My name is Thread-6I love the writing style of King Silence II. My name is Thread-7I love the writing style of King Silence II. My name is Thread-8I love the writing style of King Silence II. My name is Thread-9I love the writing style of King Silence iiCopy the code

From the results of the run, you can see that the execution order of the threads is not from 0 to 9, but a certain degree of randomness. This is because Java’s concurrency is preemptive, and although thread 0 was the first to be created, its ability to compete for favor was mediocre and it had a hard 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(a) {
					System.out.println("My name is" + Thread.currentThread().getName() + "I love the writing style of King Silence ii."); }}; executorService.execute(r); } executorService.shutdown(); }}Copy the code

The result of this run is as follows.

I called the pool -1-thread-2I love the writing style of King Silence II. My name is Pool1-thread-4I love the writing style of King Silence II. My name is Pool1-thread-5I love the writing style of King Silence II. My name is Pool1-thread-3I love the writing style of King Silence II. My name is Pool1-thread-4I love the writing style of King Silence II. My name is Pool1-thread-1I love the writing style of King Silence II. My name is Pool1-thread-7I love the writing style of King Silence II. My name is Pool1-thread-6I love the writing style of King Silence II. My name is Pool1-thread-5I love the writing style of King Silence II. My name is Pool1-thread-6I love the writing style of King Silence iiCopy the code

* The newCachedThreadPool() method used by Executors to create a cacheable thread pool. Calling the pool method execute() can reuse the previous thread as long as the thread becomes available; For example, pool-1-thread-4, pool-1-thread-5, and Pool-1-thread-6 are available for reuse. The best spokesperson I can think of is empress Wu Zetian.

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

* * * Create a fixed number of thread pools by using the newFiexedThreadPool(int Num) method; The newSingleThreadExecutor() method is used to create a single threaded pool (can you think of a situation where it should be used?). .

But the story is about to change. Alibaba’s Java development manual (available from the “Silent King 2” public account’s background reply keyword “Java”) clearly states that using Executors to create the thread pool is not allowed.

* How do I create a thread pool if I can’t use Executors to create the pool?

Create a thread pool by calling the ThreadPoolExecutor constructor directly. So, that’s exactly what Executors do, except they don’t have the capacity for the BlockQueue. All we need to do is specify the capacity at creation time. The following is a code example:

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

The third step of concurrency, to solve the problem of sharing resource competition

Once, I accompany my family to go shopping in the mall, when the elevator there is a silly fork to grab into the elevator. My daughter’s wheelbarrow fell on his feet, and he pointed at my nose and screamed. I punched him straight in the nose, and the next thing I knew, we were tangled.

What does this show? First, it’s difficult to meet people who don’t know the rules of “first out, last in” (LIFO). Second, when competing for shared resources, you might want to fight.

In Java, the first solution to the shared resource contention problem is to use the synchronized keyword. When a thread executes a snippet protected by synchronized, it locks the code, and other threads calling the code block until the lock is released.

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

public class Wanger {
	public static int count = 0;
	
	public static int getCount(a) {
		return count;
	}
	
	public static void addCount(a) {
		 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(a) { Wanger.addCount(); }}; executorService.execute(r); } executorService.shutdown(); System.out.println(Wanger.count); }}Copy the code

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

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

The usual approach is to add the synchronized keyword to the addCount() method that changes the variable — ensuring that threads are queued in order when accessing the variable.

The following is an example:

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

Another common method is the read-write lock. Multiple read locks are not mutually exclusive. Read locks and write locks are mutually exclusive and are controlled by the Java VIRTUAL machine. If the code allows many threads to read at the same time, but not write at the same time, the read lock is applied. If the code does not allow concurrent reading and only one thread can write, a write lock is applied.

The read-write lock interface is ReadWriteLock, and the implementation class is ReentrantReadWriteLock. Synchronized is a mutex that allows only one thread to read or write at any time; the other threads must wait. ReadWriteLock, on the other hand, allows multiple threads to acquire a read lock but only one thread to acquire a write lock, which is relatively efficient.

Let’s first create a singleton of read/write locks using an enumeration. The code is as follows:

public enum Locker {

	INSTANCE;

	private static final ReadWriteLock lock = new ReentrantReadWriteLock();

	public Lock writeLock(a) {
		returnlock.writeLock(); }}Copy the code

In the addCount() method, addCount ++; Locked. The following is an example.

public static void addCount(a) {
	/ / lock
	Lock writeLock = Locker.INSTANCE.writeLock();
	writeLock.lock();
	count++;
	/ / releases the lock
	writeLock.unlock();
}
Copy the code

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

05, finally

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

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

Get yourself some gas, will you?


Before: Java I/O Introduction

Next: Concurrent Programming in Java (PART 1) : An introduction

Wechat search “King of Silence three” public account, after following the reply “free video” to obtain 500G high-quality teaching videos (categorized).