Visibility between Java array threads

Volatile is usually used in concurrent programming to ensure the visibility between threads, but the elements in the array modified by volatile cannot ensure the visibility between threads. For example, ConcurrentHashMap uses Unsafe way to access u.getobjectvolatile (tab, ((long) I < < ashift) + abase);

The following code cannot guarantee the visibility of array elements between threads, and the program will be executed all the time.

public class VolatileArray {

    private volatile Object[] nums;

    public static void main(String[] args) throws InterruptedException {
        VolatileArray example = new VolatileArray();
        new Thread(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            example.nums = new Object[10];
        }).start();

        new Thread(() -> {
            while (true) {
                if (example.nums != null) {
                    System.out.println("nums set");
                    break;
                }
            }
        }).start();

        new Thread(() -> {
            while (true) {
                if (example.nums != null) {
                    Thread.yield();
                    if (example.nums[9] != null) {
                        System.out.println("index updated");
                        break;
                    }
                }
            }
        }).start();
    }

}

explain

In Java, the bottom layer of both basic type array and object type array is an object. The difference is that the array object has a 32-bit group length identifier in the object header.

Ordinary objects need to be visible between threads. Volatile keyword is usually modified. If members in the object are not modified with volatile keyword, members cannot be visible between threads. When initializing an array or creating an array, the volatile keyword cannot be modified for elements. The same explanation is as follows:

This is due to the lack of metadata design at the element level of Java array, which cannot express the semantics that the element is final and volatile. Therefore, a back door is opened. getObjectVolatile is used to fill the hole that the element cannot be expressed is volatile, and @ Stable is used to fill the hole that the element cannot be expressed is final. The array element is the same as the member field without volatile, which cannot ensure the visibility between threads.
Link: https://www.jianshu.com/p/5808db3e2ace

Several ways to ensure the visibility of array elements

public class VolatileArray {

    private volatile Object[] nums;
    private static Unsafe U;
    private static long NUMS;

    static {
        try {
            Class<?> clazz = Class.forName("sun.misc.Unsafe");
            Field theUnsafe = clazz.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            U = (Unsafe) theUnsafe.get(null);
            NUMS = U.objectFieldOffset(VolatileArray.class.getDeclaredField("nums"));
        } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {

        VolatileArray example = new VolatileArray();
        new Thread(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            example.nums = new Object[10];
            example.nums[9] = new Object();
        }).start();

        new Thread(() -> {
            while (true) {
                if (example.nums != null) {
                    System.out.println("nums set");
                    break;
                }
            }
        }).start();

        new Thread(() -> {
            while (true) {
                if (example.nums != null) {
                    Thread.yield();
                    Object[] objects = (Object[]) U.getObjectVolatile(example, NUMS);
                    if (objects[9] != null) {
                        System.out.println("index updated");
                        break;
                    }
                }
            }
        }).start();
    }

}

Posted by patch2112 on Wed, 25 May 2022 06:42:14 +0300