In-depth understanding of Java singleton pattern and optimized multi-threaded task processing

The singleton mode ensures that there is only one instance of a class in the program, without creating multiple instances, and provides a global access point.

Hungry mode

When the class is loaded, an instance is created.
Insert image description here

class  Singleton {
    
    
    private static final Singleton instance = new Singleton();
    //将构造方法设为私有,以防止外部通过new关键字创建新的实例。
    private Singleton() {
    
    }
    public static Singleton getInstance() {
    
    
        return instance;
    }
}
  • The above code defines a class called Singleton.
  • A private static constant instance is defined in the class, which is the only instance of the Singleton class.
  • A public static method getInstance() is provided to obtain the only instance of the Singleton class.

lazy mode

An instance is not created when a class is loaded, but is created the first time it is used.
Insert image description here

Single-threaded version

class Singleton {
    
    
    private static Singleton instance = null;
    private Singleton() {
    
    
    }
    public static Singleton getInstance() {
    
    
        if (instance == null) {
    
    
            instance = new Singleton();
        }
        return instance;
    }
}

Multi-threaded version

The above single-threaded code will cause errors in multi-threads. getInstance()When multiple threads call methods at the same time, it may cause multiple instances to be created, which is unsafe. Here we only need to getInstance()add synchronizedkeywords to the method to solve the problem.

class Singleton {
    
    
    private static Singleton instance = null;
    private Singleton() {
    
    
    }
    public synchronized static Singleton getInstance() {
    
    
        if (instance == null) {
    
    
            instance = new Singleton();
        }
        return instance;
    }
}

double check lock

class Singleton {
    
    
    //volatile关键字保证了instance变量在多线程环境下的可见性。
    private static volatile Singleton instance = null;
    private Singleton() {
    
    }
    public synchronized static Singleton getInstance() {
    
    
        if (instance == null) {
    
    
            synchronized (Singleton.class){
    
    
                if (instance == null ){
    
    
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

The double check can be understood like this:
the first if first determines whether the instance has been created. If it has not been created, it enters the first if, so that one thread successfully acquires the lock (the remaining threads block and wait), and the thread acquires the lock again. Make a judgment to determine whether the instance is created, and create it if it is not created. After this instance is created, other threads competing for the lock will be blocked by the inner if, and other instances will not be created.

blocking queue

The blocking queue can be a thread-safe data structure and has the following characteristics:

  • When the queue is full, continuing to enter the queue will block until other threads take elements from the queue.
  • When the queue is empty, continuing to dequeue will also block until other threads insert elements into the queue.

A typical application scenario of blocking queue is the producer-consumer model.

Blocking queues are built into the Java standard library. If we need to use blocking queues in some programs, we can directly use the ones in the standard library.

  • BlockingQueue is an interface, and the actual class is LinkedBlockingQueue
  • The put method is used for blocking enqueueing, and take is used for blocking dequeuing.
  • BlockingQueue also has methods such as offer, poll, peek, etc., but these methods do not have blocking characteristics.

Let's implement a blocking queue:

  • Through circular queue
  • Use synchronized for locking control
public class BlockingQueue {
    
    
    private int[] arr = new int[1000];
    private volatile int size = 0;
    private int tail = 0;
    private int head = 0;

    public void put(int value) throws InterruptedException {
    
    
        synchronized (this) {
    
    
            while (size == arr.length) {
    
    
                wait();
            }
            arr[tail] = value;
            tail = (tail + 1) % arr.length;
            size++;
            notifyAll();
        }
    }

    public int take() throws InterruptedException {
    
    
        int ret = 0;
        synchronized (this) {
    
    
            while (size == 0) {
    
    
                wait();
            }
            ret = arr[head];
            head = (head + 1) % arr.length;
            size--;
            notifyAll();
        }
        return ret;
    }

    public static void main(String[] args) throws InterruptedException {
    
    
        BlockingQueue bq = new BlockingQueue();
        Thread t1 = new Thread(() -> {
    
    

            try {
    
    
                for (int i = 0; i < 10; i++) {
    
    
                    bq.put(i);
                    System.out.println("生产者放入:" + i);
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        });
        t1.start();
        Thread t2 = new Thread(() -> {
    
    

            try {
    
    
                for (int i = 0; i < 10; i++) {
    
    
                    int num = bq.take();
                    System.out.println("消费者取出:" + num);
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        });
        t2.start();
        t1.join();
        t2.join();
    }
}

Insert image description here

Guess you like

Origin blog.csdn.net/st200112266/article/details/132995333