Java geek

Related reading:

Concurrent Programming in Java (a) knowledge map

Java Concurrent programming (2) Atomic Java concurrent programming (3) Visibility Java concurrent programming (4) Sequential Java concurrent programming (5) Introduction to Creating threads Java concurrent programming (6) Synchronized usage Java Concurrency programming Introduction (7) Easy to understand wait and Notify and use scenarios Java concurrency programming Introduction (8) Thread lifecycle Java concurrency programming Introduction (9) Deadlock and deadlock bit Java concurrency programming Introduction (10) lock optimization Introduction to Concurrent Programming in Java (11) Flow limiting scenarios and Spring Flow Limiting scenarios Introduction to Concurrent programming in Java (12) Producer and Consumer Patterns – Introduction to Concurrent programming in Java with code templates (13) Read/write lock and cache templates (14) CountDownLatch application scenarios Introduction to Concurrent Programming in Java (15) CyclicBarrier Application Scenarios Introduction to Concurrent programming in Java (16) Understanding the difference between thread pools Introduction to Concurrent programming in Java (17) Figure Mastering common classes and interfaces in threads Introduction to Concurrent programming in Java (19) Asynchronous task scheduling tool CompleteFeature Java Concurrent programming introduction (20) Common locking scenarios and locking tools


1. Thread safety scenario without locking

There are several scenarios to achieve thread-safety without locking:

1. Immutable objects

2. The thread is closed

3. The stack is closed

4.ThreadLocal

1.1 Immutable Objects

Classical concurrent programming describes that the object satisfies the following conditions:

1. The state of the object does not change after it is created.

2. All fields of the object are final.

3. The this reference does not overflow during object creation.

In fact, the description of point 2 is not completely accurate:

1. As long as member variables are private and only provide read-only operations, it is possible to be thread-safe, and final modifications are not necessarily required. Note that this is possible, for reasons shown in Point 2.

If a member variable is an object and is writable externally, there is no guarantee of thread-safety. For example:

public class Apple {
    public static void main(String[] args) {
        Dictionary dictionary = new Dictionary();
        Map<String, String> map = dictionary.getMap();
        // After this operation, the result of the next operation does not match the expected, which is not thread safe
        map.clear();
        System.out.println(dictionary.translate(The word "apple")); }}class Dictionary {

    private final Map<String, String> map = new HashMap<String, String>();

    public Dictionary(a) {
        map.put(The word "apple"."Apple");
        map.put("Orange"."Orange");
    }

    public String translate(String cn) {
        if (map.containsKey(cn)) {
            return map.get(cn);
        }
        return "UNKONWN";
    }

    public Map<String, String> getMap(a) {
        returnmap; }}Copy the code

Therefore, the correct understanding of immutable objects is:

1. The state of the object does not change after creation (all member variables do not change)

2. The operation is read-only only.

3. Members of an object are never overflowed (members are not written by other external objects) at any time, not just at build time.

In addition, some books and training say that immutable classes should be modified with final to prevent unsafe subclasses if the class is inherited. Personally, I feel that subclasses and superclasses are not objects themselves. When we say whether a class is thread-safe, we are talking about the class itself, not whether the subclass is safe.

1.2 Thread Blocking

If the object is only used in a single thread and not shared across multiple threads, this is thread closure.

For example, in a Web application, each Web request is an independent thread. Once a request obtains a database connection, it is not used by other requests until the database connection is closed (back to the connection pool).

1.3 stack closed

Objects are used only in local code blocks and are stack closed, for example:

     public void print(Vector v) {
         int size = v.size();
         for (int i = 0; i < size; i++) { System.out.println(v.get(i)); }}Copy the code

Size is local (stack-closed) and Vector is thread-safe, so it is thread-safe for this method.

1.4 ThreadLocal

Objects stored through ThreadLocal are visible only to the current thread and are therefore thread-safe.

2. Common misunderstood thread safety scenarios

2.1 Thread-safe Containers are always safe

Some container thread-safe operations refer to atomic thread-safe operations. Not all operations are safe. Non-thread-safe operations such as if-and-set, container iteration, for example:

public class VectorDemo {

    public static void main(String[] args) {
        Vector<String> tasks = new Vector<String>();
        for (int i = 0; i < 10; i++) {
            tasks.add("task" + i);
        }

        Thread worker1 = new Thread(new Worker(tasks));
        Thread worker2 = new Thread(new Worker(tasks));
        Thread worker3 = new Thread(newWorker(tasks)); worker1.start(); worker2.start(); worker3.start(); }}class Worker implements Runnable {

    private Vector<String> tasks;

    public Worker(Vector<String> tasks) {
        this.tasks = tasks;
    }

    public void run(a) {
        The following operations are not thread-safe and are executed by multiple threads at the same time. They may meet the conditions when judged, but may no longer meet the conditions when actually processed
        while (tasks.size() > 0) {
            // Simulate business processing
            sleep(100);
            Tasks.size () > 0
            System.out.println(Thread.currentThread().getName() + "" + tasks.remove(0)); }}private void sleep(long millis) {
        try {
            TimeUnit.MILLISECONDS.sleep(millis);
        } catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code

Output log:

Thread-0 task0
Thread-1 task2
Thread-2 task1
Thread-1 task3
Thread-2 task5
Thread-0 task4
Thread-0 task6
Thread-1 task8
Thread-2 task7
Thread-1 task9
Exception in thread "Thread-0" Exception in thread "Thread-2" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 0
	at java.util.Vector.remove(Vector.java:831)
	at com.javashizhan.concurrent.demo.safe.Worker.run(VectorDemo.java:46)
	at java.lang.Thread.run(Thread.java:745)
java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 0
	at java.util.Vector.remove(Vector.java:831)
	at com.javashizhan.concurrent.demo.safe.Worker.run(VectorDemo.java:46)
	at java.lang.Thread.run(Thread.java:745)
Copy the code

You can see that one of the worker threads on tasks.remove(0) throws an exception because there is no more data in the collection. To be thread-safe, lock non-atomic operations as follows:

    public void run(a) {
        // Lock non-atomic operations
        synchronized (tasks) {
            while (tasks.size() > 0) {
                sleep(100);
                System.out.println(Thread.currentThread().getName() + "" + tasks.remove(0)); }}}Copy the code

2.2 Final modified objects are thread-safe

In the example above, modifying a Vector with final is not thread-safe. Final does not mean that the modified object is thread-safe immutable.

2.3 Volatile modifies the thread-safe object

Volatile objects are only guaranteed to be visible. These variables are not cached in the CPU cache. This ensures that if thread A changes the volatile value first, thread B will see the latest value when it reads it.

3. Narrow thread safety and broad thread safety

We say that Vector is thread-safe, but as the above example shows, not all operations on Vector are thread-safe, yet Vector is considered thread-safe.

From this, we can define narrow thread safety and generalized thread safety:

1. Narrow sense: Every single operation of an object is thread-safe

2. Generalized: Every single operation and combined operation of an object is thread-safe

If there are no objects in the container, null is returned. The method signature can be changed to existsAndRemove. Of course, this is not the only method that can be modified to achieve generalized thread-safety.

4. To summarize

This article has described thread-safe scenarios without locking, as well as the easily misunderstood thread-safe scenarios, to narrow thread safety and general thread safety. Understanding these will help us better understand when locking is appropriate and when it is not, so that we can write thread-safe code more effectively.


<– Read the mark, left like!