Thread / process communication

Communication between processes

  1. Pipe (pipe): Pipe is a half-duplex communication method, data can only flow in one direction, and can only be used between processes with blood relationship. The blood relationship of the process usually refers to the father-son process relationship
  2. Named pipe: Named pipe is also a half-duplex communication method, but it allows unrelated process communication
  3. Semaphore (semophore): A semaphore is a counter that can be used to control multiple processes' access to shared resources. It is usually used as a lock mechanism to prevent a process from accessing a shared resource while other processes are also accessing the resource. Therefore, it is mainly used as a synchronization method between processes and between different threads in the same process
  4. Message queue (message queue): A message queue is a linked list of messages, stored in the kernel and identified by a message queue identifier. The message queue overcomes the shortcomings of less signal transmission information, the pipeline can only carry unformatted byte streams, and the buffer size is limited.
  5. Signal (signal): signal is a more complex communication method, used to notify the receiving process that an event has occurred
  6. Shared memory (shared memory): Shared memory is a piece of memory that can be accessed by other processes. This shared memory is created by one process, but multiple processes can access it. Shared memory is the fastest IPC method. It is aimed at The communication method between other processes is specially designed to run inefficiently. It is often used in conjunction with other communication mechanisms, such as semaphores, to achieve synchronization and communication between processes
  7. Socket (socket): The socket is also a communication mechanism between processes. Unlike other communication mechanisms, it can be used for different and inter-process communication.
  8. Memory mapping: Memory mapping allows communication between any multiple processes. Each process using this mechanism implements it by mapping a shared file to its own process address space

Communication mechanism between threads

  There are two models of communication between threads: shared memory and message passing .

题目:有两个线程ABA线程向一个集合里面依次添加元素
"abc"字符串,一共添加十次,当添加到第五次的时候,
希望B线程能够收到A线程的通知,然后B线程执行相关的业务操作。

Method 1: Use the volatile keyword

  It is the idea of ​​using shared memory to realize mutual communication between threads based on the volatile keyword, that is, multiple threads listen to a variable at the same time. When this variable changes, the thread can perceive and execute the corresponding business. This is also the simplest way to achieve

public class TestSync {
    // 定义一个共享变量来实现通信,它需要是volatile修饰,否则线程不能及时感知
    static volatile boolean notice = false;
    public static void main(String[] args) {
        List<String>  list = new ArrayList<>();
        // 实现线程A
        Thread threadA = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                list.add("abc");
                System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (list.size() == 5)
                    notice = true;
            }
        });
        // 实现线程B
        Thread threadB = new Thread(() -> {
            while (true) {
                if (notice) {
                    System.out.println("线程B收到通知,开始执行自己的业务...");
                    break;
                }
            }
        });
        // 需要先启动线程B
        threadB.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 再启动线程A
        threadA.start();
    }
}

Method 2: Use the wait () and notify () methods of the Object class

  The Object class provides methods for communication between threads: wait (), notify (), notifyaAll (), they are the basis of multi-thread communication, and the idea of ​​this implementation is naturally inter-thread communication. Note: wait and notify must be used with synchronized, the wait method releases the lock, the notify method does not release the lock

public class TestSync {
    public static void main(String[] args) {
        // 定义一个锁对象
        Object lock = new Object();
        List<String>  list = new ArrayList<>();
        // 实现线程A
        Thread threadA = new Thread(() -> {
            synchronized (lock) {
                for (int i = 1; i <= 10; i++) {
                    list.add("abc");
                    System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (list.size() == 5)
                        lock.notify();// 唤醒B线程
                }
            }
        });
        // 实现线程B
        Thread threadB = new Thread(() -> {
            while (true) {
                synchronized (lock) {
                    if (list.size() != 5) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("线程B收到通知,开始执行自己的业务...");
                }
            }
        });
        // 需要先启动线程B
        threadB.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 再启动线程A
        threadA.start();
    }
}

After thread A issues the notify () wakeup notification, thread B only starts executing after running its own thread's business. This also shows that the notify () method does not release the lock, while the wait () method releases the lock.

Method 3: Use JUC tool class CountDownLatch

  After jdk1.5, there are many concurrent programming related tool classes under the java.util.concurrent package. CountDownLatch is based on the AQS framework, which is equivalent to maintaining a shared variable state between threads

public class TestSync {
    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        List<String>  list = new ArrayList<>();
        // 实现线程A
        Thread threadA = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                list.add("abc");
                System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (list.size() == 5)
                    countDownLatch.countDown();
            }
        });
        // 实现线程B
        Thread threadB = new Thread(() -> {
            while (true) {
                if (list.size() != 5) {
                    try {
                        countDownLatch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("线程B收到通知,开始执行自己的业务...");
                break;
            }
        });
        // 需要先启动线程B
        threadB.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 再启动线程A
        threadA.start();
    }
}

Method 4: Use ReentrantLock with Condition

public class TestSync {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();

        List<String> list = new ArrayList<>();
        // 实现线程A
        Thread threadA = new Thread(() -> {
            lock.lock();
            for (int i = 1; i <= 10; i++) {
                list.add("abc");
                System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (list.size() == 5)
                    condition.signal();

            }
            lock.unlock();
        });
        // 实现线程B
        Thread threadB = new Thread(() -> {
            lock.lock();
            if (list.size() != 5) {
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程B收到通知,开始执行自己的业务...");
            lock.unlock();
        });
        threadB.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        threadA.start();
    }
}

Not only is the code writing complex in this way, but thread B cannot be executed immediately after it is awakened by A because it does not acquire the lock, that is, A does not release the lock after the wake-up operation. This method is the same as wait () and notify () of Object.

Method 5: Basic LockSupport to achieve blocking and wake-up between threads

  LockSupport is a very flexible tool for blocking and awakening between threads. Use it without paying attention to whether to wait for the thread to run first or to wake the thread to run first, but you need to know the name of the thread.

public class TestSync {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        // 实现线程B
        final Thread threadB = new Thread(() -> {
            if (list.size() != 5) {
                LockSupport.park();
            }
            System.out.println("线程B收到通知,开始执行自己的业务...");
        });
        // 实现线程A
        Thread threadA = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                list.add("abc");
                System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (list.size() == 5)
                    LockSupport.unpark(threadB);
            }
        });
        threadA.start();
        threadB.start();
    }
}

———————————————————————————————————————

The difference between process and thread

  A process is an independent unit for resource allocation and scheduling by the system.
  A thread is an entity of a process, and is the basic unit of CPU scheduling and dispatching. It is a basic unit that is smaller than a process and can run independently. A thread has its own stack and local variables. A thread has its own stack space (such as a program counter, a set of registers, and a stack), but it can share all the resources owned by the process with other threads that belong to the same process.

the difference
  The main difference between processes and threads is that they are different ways of operating system resource management. Processes have independent address spaces. After a process crashes, it will not affect other processes in protected mode, and threads are just different execution paths in a process. Threads have their own stack and local variables, and have independent execution sequences, but there is no separate address space between threads. The death of one thread is equal to the death of the entire process, so multi-process programs are more robust than multi-threaded programs, but When the process is switched, it consumes more resources and the efficiency is worse. However, for concurrent operations that require simultaneous execution and sharing of certain variables, only threads can be used, not processes.

Published 162 original articles · praised 58 · 90,000 views

Guess you like

Origin blog.csdn.net/ThreeAspects/article/details/105539033