Learn about Semaphore and thread pool

Posted by dreamlove on Tue, 02 Nov 2021 06:13:28 +0100

1, First, understand what Semaphore and thread pool do respectively?

Semaphore is a concurrency tool class, which is used to control the number of threads that can be concurrent at the same time. It internally maintains a set of virtual licenses, and specifies the number of licenses through the constructor. Each time a thread performs an operation, it first obtains the license through the acquire method, and then releases the license through the release method. If no license is available, the acquire method will block until another thread releases the license.
Thread pool is used to control the number of threads actually working and reduce memory overhead by thread reuse. The number of threads that can work at the same time in the thread pool is certain. Threads exceeding this number need to enter the thread queue and wait until there are available working threads to execute tasks.
Using seampore, you can create as many threads as you want, but the number of threads that can be executed at the same time is limited. However, using the thread pool, the threads you create are only submitted to the thread pool for execution as tasks. The actual working threads are created by the thread pool, and the number of actual working threads is managed by the thread pool itself.
In short, the actual working threads of the thread pool are work threads, which are not created by yourself. They are created by the thread pool, and the thread pool automatically controls the actual number of concurrent work threads. Seampore is equivalent to a signal lamp, which is used to limit the flow of threads. Seampore can limit the flow of threads you create (or the flow of work threads in the thread pool). The flow limit of seampore must be realized through manual acquire and release.
The difference is two points:
1. Who created the actual working thread?
Using thread pool, the actual working thread is created by thread pool; With seampore, the actual working thread is created by yourself.
2. Is current limiting automatically implemented?
Thread pool automatic, seampore manual.

The following is a specific case to illustrate the specific difference between the two.

1) Using Semaphore:

  public static void testSeamphore() {
    Semaphore semaphore = new Semaphore(2);
    for (int i = 0; i < 5; i++) {
      Thread thread = new Thread() {
        public void run() {
          try {
            semaphore.acquire();
            System.out.println(Thread.currentThread().getName() + " start running **********************");
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + " stop running  ----------------------");
            semaphore.release();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      };
      thread.setName("my thread " + i);
      thread.start();
    }
  }

We created 5 threads with semaphore 2, and printed as follows:

my thread 0 start running **********************
my thread 1 start running **********************
my thread 0 stop running ----------------------
my thread 1 stop running ----------------------
my thread 2 start running **********************
my thread 3 start running **********************
my thread 3 stop running ----------------------
my thread 2 stop running ----------------------
my thread 4 start running **********************
my thread 4 stop running ----------------------

It is easy to observe from the console:

1. At most 2 * * * records will be printed each time because the seampore semaphore is 2, so the actual number of concurrent threads is controlled to 2.

2. The names of the five threads start with my thread, indicating that the actual working thread is created by ourselves.

2) Use thread pool:

  public static void testPool() {
    ExecutorService executorService = new ThreadPoolExecutor(2, 5,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>());
 
    for (int i = 0; i < 5; i++) {
      Thread thread = new Thread() {
        public void run() {
          try {
            System.out.println(Thread.currentThread().getName() + " start running **********************");
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + " stop running  ----------------------");
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      };
      thread.setName("my thread " + i);
      executorService.submit(thread);
    }
    executorService.shutdown();
  }

We created a thread pool with a core capacity of 2 and a total capacity of 5 to execute 5 thread tasks in parallel. Print as follows:

pool-1-thread-1 start running **********************
pool-1-thread-2 start running **********************
pool-1-thread-2 stop running ----------------------
pool-1-thread-1 stop running ----------------------
pool-1-thread-1 start running **********************
pool-1-thread-2 start running **********************
pool-1-thread-1 stop running ----------------------
pool-1-thread-2 stop running ----------------------
pool-1-thread-1 start running **********************
pool-1-thread-1 stop running ----------------------

It is easy to observe and know through the console:

1. At most two threads execute each time, which is determined by the core thread capacity. Redundant thread tasks are placed in the blocking queue for waiting.

2. The actual working thread is not created by ourselves, but provided by the thread pool. They are pool-1-thread-1 and pool-1-thread-2.

2, Semaphore is the embodiment of mutex
Semaphore implements the mutex by using the semaphore object with the initial value of 1. In this way, each thread must release the license after obtaining the license, and other threads can obtain the license. The thread that currently has the license has the mutex.

The following are specific cases:

    public static void testMutex() {
        Semaphore semaphore = new Semaphore(1);
        for (int i = 0; i < 5; i++) {
            new Thread() {
                public void run() {
                    try {
                        semaphore.acquire();
                        System.out.println(Thread.currentThread().getName() + "Licensed");
                        Thread.sleep(2000);
                        System.out.println(Thread.currentThread().getName() + "License released");
                        semaphore.release();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    }

The console prints as follows:

Thread-0 is licensed
Thread-0 released license
Thread-1 is licensed
Thread-1 released license
Thread-2 is licensed
Thread-2 released license
Thread-3 is licensed
Thread-3 released license
Thread-4 licensed
Thread-4 released license

It can be seen that before any thread releases the license, other threads cannot get the license. In this way, the current thread must be executed before other threads can execute. In this way, mutual exclusion is realized.

Title III. Semaphore release before acquire

Seampore has a special usage scenario, that is, release the license first and then apply for the license. At this time, an additional license will be added.

Be extra careful in actual programming. In the following example, for semaphores created through new Semaphore(0), the default number of licenses is 0. If release is called first, a license will be added, and the new license can be obtained by acquiring again.

    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphore = new Semaphore(0);
        System.out.println(semaphore.availablePermits());
        semaphore.release();
        System.out.println(semaphore.availablePermits());
        semaphore.acquire(); //block
        System.out.println(semaphore.availablePermits());
    }

Therefore, the above code does not actually block, but directly outputs 0 1 0. In this example, if release and acquire are switched, blocking will occur.

0
1
0

Topics: Java Spring Cloud