First praise after look, form a habit 🌹

Welcome wechat to follow the way of Java programming progress a little bit every day, precipitation technology to share knowledge.

Three badly asked multi-threaded interview questions

Interview question 1: Implement producer-consumer model with multi-threading technology

What is the producer-consumer model

In popular terms, producers are constantly producing and consumers are constantly consuming. The producer produces something to tell the consumer to consume, and when there is nothing left the consumer waits until the producer produces it. Thus, the strong coupling between producer and consumer can be solved through an intermediate container or communication means.

This leads to a simple producer-consumer implementation

Using blocking queues

producers

/ * * *@Auther: a lei who loves talking *@Company: The Tao of Java Programming@Date: 2020/7/11 * to them@Version1.0 * /
public class Product extends Thread {
    private LinkedBlockingDeque<String> stringLinkedBlockingDeque;

    Product(LinkedBlockingDeque<String> stringLinkedBlockingDeque) {
        this.stringLinkedBlockingDeque = stringLinkedBlockingDeque;
    }

    @Override
    public void run(a) {
        for (int i = 0; i < 5; i++) {
            int nextInt = new Random().nextInt(5000);
            try {
                stringLinkedBlockingDeque.put("Goods" + i);
                System.out.println("The producer produces :" + i);
                sleep(nextInt);
            } catch(InterruptedException e) { e.printStackTrace(); }}}}Copy the code

consumers

/ * * *@Auther: a lei who loves talking *@Company: The Tao of Java Programming@Date: 2020/7/11 in *@Version1.0 * /
public class Consumer extends Thread {
    private LinkedBlockingDeque<String> stringLinkedBlockingDeque;

    Consumer(LinkedBlockingDeque<String> stringLinkedBlockingDeque) {
        this.stringLinkedBlockingDeque = stringLinkedBlockingDeque;
    }

    @Override
    public void run(a) {
        for (int i = 0; i < 5; i++) {
            int nextInt = new Random().nextInt(10000);
            try {
                String take = stringLinkedBlockingDeque.take();
                System.out.println(Thread.currentThread()+Consumer consumption:+take);
                sleep(nextInt);
            } catch(InterruptedException e) { e.printStackTrace(); }}}}Copy the code

The test class

/ * * *@Auther: a lei who loves talking *@Company: The Tao of Java Programming@Date: 2020/7/11 comes to *@Version1.0 * /
public class MainTest {
    public static void main(String[] args) {
        LinkedBlockingDeque<String> strings = new LinkedBlockingDeque<String>(4);
        new Product(strings).start();
        new Consumer(strings).start();
        new Consumer(strings).start();
        newConsumer(strings).start(); }}Copy the code

The test results

Here we can see that a multithreaded producer-consumer model is implemented by blocking queues. Whenever there is an item in the queue, the consumer calls the blocking take method to consume, and if there is no item, the consumer waits. The three consumers do not influence each other and there is no repeated consumption.

Working with threads

To implement the producer-consumer model with multithreaded collaboration, you must thoroughly understand what wait(), notify(), and notifyAll() are used for.

  • You have to knowAll three are methods of the Object class, not thread-owned methods.
  • You have to knowThe effect of wait() is to put the current thread on a pre-execution queue and stop at the code where wait() is located, waiting for a wake up notification.
  • You have to knowwait(),notify()onlyExecute in synchronized code blocks or synchronized methods, otherwise an exception is thrown.
  • You have to knowThe wait() method call regrets releasing the lock, and the thread competes with other threads to regain the lock.
  • You have to knowNotify (), which notifies those waitingCurrent thread object lockIf there are too many threads waiting, the thread planner randomly selects a thread in the wait state.
  • You have to knowNotifyAll wakes up all waitsCurrent thread object lockWhich thread will be processed first depends on the implementation of the operating system.

Code implementation:

/ * * *@Auther: a lei who loves talking *@Company: The Tao of Java Programming@Date: * 2020/7/11 struck@Version1.0 * /
public class Goods {
    private int id;
    private String name;
    public Goods(int id, String name) {
        this.id = id;
        this.name = name; }}Copy the code

producers

/ * * *@Auther: a lei who loves talking *@Company: The Tao of Java Programming@Date: better 2020/7/11 *@Version1.0 * /
public class Producer implements Runnable {
    private Goods goods;
    private Object lock;
    private Vector<Goods> container;

    Producer(Object lock, Vector<Goods> container) {
        this.lock = lock;
        this.container = container;
    }

    @Override
    public void run(a) {
        while (true) {
            synchronized (lock) {
                goods = new Goods(1."Goods");
                if (container.size() < 5) {
                    container.add(goods);
                    System.out.println(Thread.currentThread().getName() + "Produce goods");
                } else {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        }
    }
}
Copy the code

consumers

/ * * *@Auther: a lei who loves talking *@Company: The Tao of Java Programming@Date: better 2020/7/11 *@Version1.0 * /
public class Consumer implements Runnable {
    private Goods goods;
    private Object lock;
    private Vector<Goods> container;

    Consumer(Object lock, Vector<Goods> container) {
        this.lock = lock;
        this.container = container;
    }

    @Override
    public void run(a) {
        while (true) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lock) {
                if(! container.isEmpty()) { container.remove(0);
                    System.out.println(Thread.currentThread().getName() + "Consumer goods");
                } else {
                    lock.notifyAll();
                }
            }
        }
    }
}


Copy the code

The test class

/ * * *@Auther: a lei who loves talking *@Company: The Tao of Java Programming@Date: 2020/7/11 and *@Version1.0 * /
public class MainTest {
    public static void main(String[] args) {
        Vector<Goods> goods = new Vector<Goods>(5);
        Object lock = new Object();
        Producer producer = new Producer(lock, goods);
        Consumer consumer = new Consumer(lock, goods);
        for (int i = 0; i < 5; i++) {
            Thread threadA = new Thread(producer, "Producer thread" + i);
            threadA.start();
        }
        for (int j = 0; j < 3; j++) {
            Thread threadB = new Thread(consumer, "Consumer thread"+ j); threadB.start(); }}}Copy the code

Execution Result:Code explanation (If you don't know, you have to see it) :

  • Object lock = new Object(); The purpose of this is to let you know that synchronized is by acquiring aobjectAs locks, rather than threads as locks.
  • The producer produces, produces five, suspends the current thread and releases the lock (lock object), allowing other threads (including the consumer thread) to compete.
  • The consumer consumes when the container is empty (by waking upWait for lock to lock the objectInforms other production threads to execute the run method.

Interview question 2: Use multiple threads to print 0-100 alternately

This topic is not rare, the general Java basic books are introduced, but for many beginners, learning Java has been very headache, at that stage to understand the knowledge of multithreading, must be a terrible thing.

Today I’m going to give you a good rundown…

Implementation 1: Use wait/notify

Go straight to code

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/ * * *@Auther: a lei who loves talking *@Company: The Tao of Java Programming@Date: 2020/2/27 15:06
 * PACKAGE_NAME
 * @Description: * /
public class PrintThread {
    public static int num = 1;
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        Thread1 thread1 = new Thread1();
        thread1.setName("Thread one");
        Thread2 thread2 = new Thread2();
        thread2.setName("Thread two");
        executorService.submit(thread1);
        executorService.submit(thread2);
        executorService.shutdown();
    }

    static class Thread1 extends Thread {
        @Override
        public void run(a) { print(); }}static class Thread2 extends Thread {
        @Override
        public void run(a) { print(); }}static void print(a) {
        synchronized (PrintThread.class) {
            while (true) {
                try {
                    PrintThread.class.notify();
                    if(num<=100) {if (Thread.currentThread().getName().equals("Thread one")) {
                            if(num%2= =0){
                                System.out.println(Thread.currentThread().getName() + "- >"+ num++); }}else{
                            System.out.println(Thread.currentThread().getName() + "- >" + num++);
                        }
                    }
                    PrintThread.class.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
Copy the code

Question:

  1. What does synchronized (printthread.class) mean?

Printthread. class The current class acts as a lock object that each thread must acquire to access a synchronized method. Second, wait and notify operate on lock objects.

  1. Why notify() first?

A: If a thread preempts a resource and then immediately waits () and then suspends, it is in an infinite loop. Use notify() first to ensure that two threads can wake each other up.

  1. Why not notifyAll()?

Answer: Notify () is sufficient for the current scenario, and you can use notifyAll() if you like.

Implementation 2: Use Lock Lock

In the code

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/ * * *@Auther: a lei who loves talking *@Company: The Tao of Java Programming@Date: 2020/7/11 15:06
 * PACKAGE_NAME
 * @Description: * /
public class PrintThread {
    public static int num = 1;
    public static ReentrantLock reentrantLock = new ReentrantLock();
    public static Condition condition = reentrantLock.newCondition();

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        Thread1 thread1 = new Thread1();
        thread1.setName("Thread one");
        Thread2 thread2 = new Thread2();
        thread2.setName("Thread two");
        executorService.submit(thread1);
        executorService.submit(thread2);
        executorService.shutdown();
    }

    static class Thread1 extends Thread {
        @Override
        public void run(a) { print(); }}static class Thread2 extends Thread {
        @Override
        public void run(a) { print(); }}static void print(a) {
        while (true) {
            reentrantLock.lock();
            try {
                condition.signal();
                if (num <= 100) {
                    if (Thread.currentThread().getName().equals("Thread one")) {
                        if (num % 2= =0) {
                            System.out.println(Thread.currentThread().getName() + "- >"+ num++); }}else {
                        System.out.println(Thread.currentThread().getName() + "- >" + num++);
                    }
                }
                condition.await();
            } catch(InterruptedException e) { e.printStackTrace(); } reentrantLock.unlock(); }}}Copy the code

This implementation is much the same and much better understood than the one above. ReentrantLock and unlock code blocks. Condition and operation conditions provide object-like monitor methods that, in conjunction with Lock, implement a wait/notify pattern.

Interview question three: the use of multithreading a do addition a do division, the final sum calculation

The most common thing you might be thinking about when you look at this question is how do I get the value of the thread computed?

Normal operation

Look at the code

/ * * *@Auther: a lei who loves talking *@Company: The Tao of Java Programming@Date: 2020/7/11 16:07
 * @Description: * /
public class ThreadsCount {
    public static void main(String[] args) {
        int a = 1;
        int b = 2;
        int c = 98;
        int d = 1;
        int sum = 0;
        ThreadA threadA = new ThreadA(a, b);
        ThreadB threadB = new ThreadB(c, d);
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        Future<Integer> first = executorService.submit(threadA);
        Future<Integer> two = executorService.submit(threadB);
        try {
            Integer aa = first.get();
            Integer bb = two.get();
            sum = aa + bb;
            System.out.println(sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } finally{ executorService.shutdown(); }}}class ThreadA implements Callable<Integer> {
    int a = 0;
    int b = 0;

    public ThreadA(int a, int b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public Integer call(a) throws Exception {
        returna * b; }}class ThreadB implements Callable<Integer> {
    // handle d=0
    int c = 0;
    int d = 0;

    public ThreadB(int c, int d) {
        this.c = c;
        this.d = d;
    }

    @Override
    public Integer call(a) throws Exception {
        returnc / d; }}Copy the code

This is done using a thread pool, calling the thread pool submit method to get a Future, and calling the Future get method to block to get the thread return value. In addition to the Callable interface can be implemented to get the return value, interested students can have a try, relatively simple do not post code.

But!! You have no idea, I actually want to say another way of implementation, more 🐂 skin Plus! You’ve never used it!

SAO operation

Implement it using CompletionService.

It combines the functionality of Executor and BlockingQueue. You can submit Callable tasks to it for completion, and then use queue-like take and poll methods to retrieve the results of completed tasks. The result is encapsulated into a Future class.

In the code

import java.util.concurrent.*;

/ * * *@Auther: Xianglei
 * @Company: xxx
 * @Date: 2020/3/15 18:07
 * PACKAGE_NAME
 * @Description: * /
public class ThreadsCount {
    public static void main(String[] args) {
        int a = 1;
        int b = 2;
        int c = 98;
        int d = 1;
        int sum = 0;
        ThreadA threadA = new ThreadA(a, b);
        ThreadB threadB = new ThreadB(c, d);
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        ExecutorCompletionService<Integer> integerExecutorCompletionService = new ExecutorCompletionService<Integer>(executorService);
        integerExecutorCompletionService.submit(threadA);
        integerExecutorCompletionService.submit(threadB);
        try {
            for (int i = 1; i <=2; i++) {
                Future<Integer> take = integerExecutorCompletionService.take();
                sum = sum + take.get();
                System.out.println("The first"+i+"Second calculation:"+sum); }}catch (InterruptedException e) {
            e.printStackTrace();
        } catch(ExecutionException e) { e.printStackTrace(); }}}class ThreadA implements Callable<Integer> {
    int a = 0;
    int b = 0;

    public ThreadA(int a, int b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public Integer call(a) throws Exception {
        returna * b; }}class ThreadB implements Callable<Integer> {
    // handle d=0
    int c = 0;
    int d = 0;

    public ThreadB(int c, int d) {
        this.c = c;
        this.d = d;
    }

    @Override
    public Integer call(a) throws Exception {
        returnc / d; }}Copy the code

The test resultsAs an aside: this approach is more suited to front-end needsPage renderingWhile the rendered data comes back later, this can be done in thread poolsParallel processing, and the final result will be obtainedReturn immediately (display)Out of this, the front-end user can get a more dynamic and responsive interface.


There are also more operations on thread pools, which I will talk about in more detail when I analyze thread pools. Today, so much for the moment, this is also my interview in the United States, god policy encountered questions. I hope you can have a good understanding of it. If you have any questions, you are welcome to feedback and exchange!


More wonderful articles on: The Way of Java programming 🎁

Welcome friends to pay attention to! 🌹