Collaborative mechanisms between threads

Source: Internet
Author: User

In the previous article, we introduced the Synchronized keyword, which can basically realize the correct access and modification of critical resources between threads in critical section. However, it relies on a Java object built-in lock that can only be occupied by one thread at a time, and other threads that are trying to occupy the block on the object's blocking queue.

But in fact there is a situation also exists, if a thread obtains a lock but in the execution process due to certain conditions, such as the database query resources have not arrived, the disk read instruction data is not returned, in this case, let the thread still occupy CPU waiting is a waste of resources.

Therefore, there is also a wait queue on each object that blocks all threads that have acquired a lock and are missing certain conditions during the run, so the lock and queue status of the entire object is the same.

The Entry set blocks all threads that failed to attempt to acquire the current object lock, and Wait set blocks all the thread collections that have surrendered the CPU due to missing certain conditions during the lock run.

When a site says the waiting condition is met, it is removed waiting queue to enter the blocking queue to re-compete the lock resource.

Wait/notify method

There are several methods in the Object class that we do not use very often, but we do have a core approach to thread collaboration, and we control the collaboration between threads through these methods.

public final native void wait(long timeout)public final void wait()public final native void notify();public final native void notify();

The Wait class method is used to block the current thread, mount the current thread into the wait Set queue, and the Notify class method is used to free one or more threads in the waiting queue.

Therefore, these two methods are primarily the wait queue for the operands, which is the action of blocking and releasing those threads that acquire the lock but lack the condition to continue executing during the run.

But there is a premise to note that wait and notify operate the wait queue for an object's built-in lock, which means that the thread on the wait queue must be blocked and freed if the object's built-in lock is acquired. Simply put, these two methods can only be called inside a block of code that is synchronized decorated .

Let's look at a section of code:

public class Test {    private static Object lock = new Object();    public static void main(String[] args) throws InterruptedException {        Thread thread1 = new Thread(){            @Override            public void run(){                synchronized (lock){                    try {                        lock.wait();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        };        Thread thread2 = new Thread(){            @Override            public void run(){                synchronized (lock){                    System.out.println("hello");                }            }        };        thread1.start();        thread2.start();        Thread.sleep(2000);        System.out.println(thread1.getState());        System.out.println(thread2.getState());    }}

Operation Result:

As you can see, the program does not end normally, that is, the thread has not exited normally. The thread first starts at thread two, so it gets the lock lock, then calls the wait method to block itself on the wait queue of the lock object and releases the lock to hand over the CPU.

When thread two starts, it may block because the thread is still holding the lock, but when the thread releases the lock, the line Cheng obtains the lock and executes the print statement, and then the synchronization method ends and releases the lock.

At this point, thread one still blocks on the wait queue of the lock object, so the whole program does not exit normally.

What is the meaning of demonstrating such a program? Just to tell you that although the blocking queue and the threads on the wait queue do not get the CPU's normal execution instructions, they are in two different states, and the threads on the blocking queue will compete to lock the resource fairly when they know the lock has been released, while the thread waiting on the queue must have other threads call notify Method notifies and moves out of the waiting queue into the blocking queue and re-competes the lock resource.

Implementation of related methods

1. Sleep method

The Sleep method is used to block the current thread's specified length, and the thread state becomes timed_waiting, but differs from the wait method. Both are out of the CPU, but the sleep method does not release the currently held lock.

That is, the sleep method is not used for inter-thread synchronization of the method, it just let the thread temporarily hand over the CPU, suspended for a period of time, the time will be dispatched by the system to allocate the CPU to continue execution.

2. Join method

The Join method is used to implement an operation that waits between two threads to see the segment code:

public void testJoin() throws InterruptedException {    Thread thread = new Thread(){        @Override        public void run(){            for (int i=0; i<1000; i++)                System.out.println(i);        }    };    thread.start();    thread.join();    System.out.println("main thread finished.....");}

Aside from the Join method, the printing method in the main thread must be executed first, and in fact the program will execute the thread thread after it is finished.

The implementation mechanism differs from the sleep method, and we look together:

The core of the method is to call wait (delay) to block the current thread, and how long it has elapsed since the thread was woken up from the entry method to the current time.

Then compare Millis and this now, if the Millis is less than the now description, stating that the wait time has arrived, you can exit the method returned. Otherwise, the thread wakes up early and needs to wait.

It is important to note that since the wait method is called, the waiting thread must be freed from the current object's built-in lock, which differs from the sleep method.

A typical thread synchronization problem

Let's write a very interesting code to implement the producer consumer model in the operating system, with our wait and notify methods.

Producers keep producing products in warehouses until the warehouses are full, and consumers are constantly removing products from the warehouses until the warehouses are empty. If the producer finds that the warehouse is full, it cannot continue to produce the product, and the consumer cannot remove the product from the warehouse if it finds that the warehouse is empty.

public class Repository {    private List<Integer> list = new ArrayList<>();    private int limit = 10;  //设置仓库容量上限    public synchronized void addGoods(int count) throws InterruptedException {        while(list.size() == limit){            //达到仓库上限,不能继续生产            wait();        }        list.add(count);        System.out.println("生产者生产产品:" + count);        //通知所有的消费者        notifyAll();    }    public synchronized void removeGoods() throws InterruptedException {        while(list.size() <= 0){            //仓库中没有产品            wait();        }        int res = list.get(0);        list.remove(0);        System.out.println("消费者消费产品:" + res);        //通知所有的生产者        notifyAll();    }}

Write a warehouse class, which provides two methods for external invocation, one for the warehouse, and if the warehouse is full, block the wait queue for the warehouse object, one is to remove the product from the warehouse, and if the warehouse is empty, it is blocked in the warehouse waiting queue.

public class Producer extends Thread{    Repository repository = null;    public Producer(Repository p){        this.repository = p;    }    @Override    public void run(){        int count = 1;        while(true){            try {                Thread.sleep((long) (Math.random() * 500));                repository.addGoods(count++);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

Define a producer class, and the producer randomly adds products to the warehouse. If there is no successful addition, it will be blocked in the loop.

public class Customer extends Thread{    Repository repository = null;    public Customer(Repository p){        this.repository = p;    }    @Override    public void run(){        while(true){            try {                Thread.sleep((long) (Math.random() * 500));                repository.removeGoods();            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

Define a consumer class, and the consumer class randomly takes a product from the warehouse. If a product is not successfully removed, it is also blocked in the loop.

public void testProducerAndCustomer() {    Repository repository = new Repository();    Thread producer = new Producer(repository);    Thread consumer = new Customer(repository);    producer.start();    consumer.start();    producer.join();    consumer.join();    System.out.println("main thread finished..");}

The main thread initiates these two threads, which is roughly the case with the program running:

生产者生产产品:1消费者消费产品:1生产者生产产品:2消费者消费产品:2生产者生产产品:3消费者消费产品:3。。。。。。。。。。消费者消费产品:17生产者生产产品:21消费者消费产品:18生产者生产产品:22消费者消费产品:19生产者生产产品:23消费者消费产品:20生产者生产产品:24生产者生产产品:25生产者生产产品:26消费者消费产品:21生产者生产产品:27生产者生产产品:28消费者消费产品:22消费者消费产品:23生产者生产产品:29生产者生产产品:30。。。。。。。。。。。。

Careful observation, you will find that consumers will never consume a non-existent product, consumption must be produced by producers of products. At first it may be that the producer produces a product, and the consumer consumes a product, and once the consumer thread executes faster than the producer, it is bound to be blocked because the warehouse capacity is empty.

Producer threads can execute more quickly than the consumer thread, and the consumer thread executes faster than the producer will cause the warehouse to be empty and block itself.

To summarize, the synchronized-modified block of code is a blocking queue of directly used object built-in locks, the thread acquires no locks that are naturally blocked on the queue, and wait/notify is our manual control of the queued and outbound operations of the waiting queue. But essentially the two queues that utilize the object's built-in locks.

These two articles introduce the use of the built-in locks provided by Java to our objects to complete the basic inter-thread synchronization operations, this part of the knowledge is the subsequent introduction of the various synchronization tools, such as the framework of the implementation of the set of the underlying principles.

All the code, pictures, and files in the article are stored on my GitHub:

(Github.com/singleyam/overview_java)

Welcome to the public number: Onejavacoder, all articles will be synchronized to the public number.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.