This is the 6th day of my participation in the August Challenge. For details, see: August Challenge ##

Meaning of multithreading

With the progress of industry and the rapid development of computer hardware, now the notebook, desktop and even commercial application server are at least dual-core, 4-core, 8-core and even 16-core are not uncommon. Therefore, when writing programs, it is necessary to write parallel programs in order to improve efficiency and give full play to the capabilities of the hardware.

Java language as the main language of Internet applications, it also supports Multithreading, but although Multithreading is good, it has higher requirements for programming. Although we often see single-core cpus say that they support multithreading, this is not accurate, the multithreading here is false, the processor will only process one piece of logic at a time, but the threads switch faster, so it looks like multiple threads are running “at the same time”. Multi-threading on a multi-core CPU can really take advantage of the CPU, so properly written programs and stable execution will duplicate much more.

Here we will combine examples to talk about how to achieve multithreaded safety procedures in the Java language. Here is an example of thread insecurity:

package com.example.learn;
public class Counter {
    private static int counter = 0;
    public static int getCount(a){
        return counter;
    }
    public static  void add(a){
        counter = counter + 1; }}Copy the code

This class has a static property counter for counting. The static method add() can be used to add 1 to counter, or getCount() can be used to get the current counter value. If it’s a single thread, this program is fine, say, loop 10 times, and the final counter is 10. But in multithreaded cases, this result may not be correct, it may be equal to 10, it may be less than 10, like 9. Here is an example of a multithreaded test:

package com.example.learn;
public class MyThread extends Thread{
    private String name ;
    public MyThread(String name){
        this.name = name ;
    }
    public void run(a){
        Counter.add();
        System.out.println("Thead["+this.name+"] Count is "+ Counter.getCount()); }}///////////////////////////////////////////////////////////
package com.example.learn;
public class Test01 {
    public static void main(String[] args) {
        for(int i=0; i<5000; i++){ MyThread mt1 =new MyThread("TCount"+i); mt1.start(); }}}Copy the code

So to reproduce the counting problem, let’s set the number of threads to a larger number, 5,000. Running this example produces the following possible output:

Thead[TCount5] Count is 4
Thead[TCount2] Count is 9
Thead[TCount4] Count is 4
Thead[TCount14] Count is 10. Thead[TCount4911] Count is4997
Thead[TCount4835] Count is 4998
Thead[TCount4962] Count is 4999
Copy the code

Note: In multithreaded scenarios, the output of a thread unsafe program is uncertain.

Synchronized methods

Based on the example above, the most straightforward way to make a thread-safe program is to add the synchronized keyword to the corresponding method to make it a synchronized method. It can decorate a class, a method, and a code block. Modify the above counting program, the code is as follows:

package com.example.learn;
public class Counter {
    private static int counter = 0;
    public static int getCount(a){
        return counter;
    }
    public static synchronized void add(a){
        counter = counter + 1; }}Copy the code

Run the program again, and the output is as follows:

. Thead[TCount1953] Count is4998
Thead[TCount3087] Count is 4999
Thead[TCount2425] Count is 5000
Copy the code

Locking mechanism

Another common synchronization method is locking. For example, In Java, ReentrantLock is a recursive non-blocking synchronization mechanism. Compared with synchronized, ReentrantLock can provide a more powerful and flexible locking mechanism and reduce the probability of deadlocks. Example code is as follows:

package com.example.learn;
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
    private  static int counter = 0;
    private static final ReentrantLock lock = new ReentrantLock(true);
    public static int getCount(a){
        return counter;
    }
    public static  void add(a){
        lock.lock();
        try {
            counter = counter + 1;
        } finally{ lock.unlock(); }}}Copy the code

Run the program again, and the output is as follows:

. Thead[TCount1953] Count is4998
Thead[TCount3087] Count is 4999
Thead[TCount2425] Count is 5000
Copy the code

Note: Java also provides the read and write lock ReentrantReadWriteLock, which enables read and write separation and is more efficient.

Atomic objects

Because the locking mechanism affects certain performance, it can be implemented in lock-free mode in some scenarios. Java has built-in classes for Atomic related Atomic operations, such as AtomicInteger, AtomicLong, AtomicBoolean, and AtomicReference, which can be selected for different scenarios. Sample code is shown below:

package com.example.learn;
import java.util.concurrent.atomic.AtomicInteger;
public class Counter {
    private static final AtomicInteger counter = new AtomicInteger();
    public static int getCount(a){
        return counter.get();
    }
    public static void add(a){ counter.incrementAndGet(); }}Copy the code

Run the program again, and the output is as follows:

. Thead[TCount1953] Count is4998
Thead[TCount3087] Count is 4999
Thead[TCount2425] Count is 5000
Copy the code

Stateless object

As mentioned above, one reason why threads are unsafe is that multiple threads access data in an object at the same time, and the data is shared. Therefore, if the data is made independent, that is, stateless, then it is naturally thread-safe. A stateless method is one that gives the same input and returns consistent results. Sample code is shown below:

package com.example.learn;
public class Counter {
    public static int sum (int n) {
        int ret = 0;
        for (int i = 1; i <= n; i++) {
            ret += i;
        }
        returnret; }}Copy the code

Immutable object

As mentioned earlier, if a piece of data needs to be shared across multiple threads, and the data is given a value that cannot be changed, it is also thread-safe, equivalent to a read-only property. In Java, attributes can be modified with the final keyword. Sample code is shown below:

package com.example.learn;
public class Counter {
    public final int count ;
    public Counter (int n) { count = n; }}Copy the code