Java--Thread synchronization & thread communication

Previous--Five-state model & control thread

Thread synchronization:

Synchronized monitor (synchronized): 

Java multithreading introduces the critical section problem. An exception may occur when two processes modify the same resource concurrently. Java introduced synchronization monitors to solve this problem. There are two ways to use synchronization monitors: synchronized code blocks and synchronized methods.

Synchronized code block:

synchronized(obj){
    //此处代码是同步代码块
}

The obj of the above code is the synchronization monitor. Only one thread can obtain the lock of the synchronization monitor at the same time. When the synchronization code block is executed, the thread will automatically release the lock of the synchronization monitor.

Usually, a shared resource that may be accessed concurrently is used as a synchronization monitor obj. For example, a bank account is used as a synchronization monitor for bank withdrawal problems.

Synchronization method:

public synchronized void drow(double drawAmount){
    //同步方法体
}

Use synchronized to decorate a method, the method is a synchronized method. A synchronization method does not need to explicitly specify a synchronization monitor. Its synchronization monitor is this, which is the object that calls the method.

An immutable class is always thread-safe because its object state cannot be changed; methods of mutable class objects require the use of additional methods (such as those above) to ensure thread-safety.

The synchronized keyword can modify methods and code blocks, but cannot modify constructors, member variables, etc.

The thread safety of mutable classes is at the expense of operating efficiency, so do not synchronize all methods of thread-safe classes. If a mutable class has two operating environments - single-threaded and multi-threaded, then there should be two versions of the mutable class, a thread-unsafe version and a thread-safe version.

Release of Sync Monitor

The following conditions will release the sync monitor

  • The synchronization method and synchronization code block execution ends;
  • The thread encounters break and return in a synchronized code block or synchronized method to terminate execution;
  • The thread has an unhandled Error or Exception in a synchronized code block or synchronized method;
  • The thread executes the wait() method of the synchronization monitor object in a synchronized block or method.

The following conditions will not release the sync monitor

  • When a thread executes a synchronized code block or synchronized method, the program calls Thread.sleep() and Thread.yield() to suspend the execution of the thread;
  • When a thread executes a synchronized code block, another thread calls the thread's suspend() to suspend the thread.

Synchronization lock (lock): 

Synchronization locks are a more powerful thread synchronization mechanism than synchronization monitors—synchronization is achieved by explicitly defining synchronization lock objects, which are acted by Lock objects.

Lock and ReadWriteLock are two root interfaces provided by Java 5. Lock has a ReentrantLock (reentrant lock) implementation class, and ReadWriteLock has a ReentrantReadWriteLock implementation class.

public class Account{
    ...
    //定义锁对象,以ReentrantLock 为例
    private final ReentrantLock lock = new ReentrantLock();
    ...
    public void draw(double drawAmount){
        //加锁
        lock.lock();
        try{
            ...
        }catch(InterruptedException e){
            ...
        }finally{
            //释放锁
            lock.unlock();
        }
    }
}

ReentrantLock locks are reentrant, that is, a thread can re-lock a locked ReentrantLock lock. The ReentrantLock object maintains a counter to track the nested calls of the lock() method. The thread must call unlock after calling lock(). () to release the lock. So a piece of code protected by a lock can call another code protected by the same lock.

Thread communication:

Consider a "producer-consumer problem": a bank account where the system requires depositors and withdrawers to alternately operate.

Traditional thread communication:

In order to achieve this function, you can use the wait(), notify(), notifyAll() methods of the Object class. Note that these three methods do not belong to the Thread class, but must be called by the synchronization monitor object.

For the synchronization method decorated with synchronized, because the synchronization monitor is this, these three methods can be called directly; for the code block decorated with synchronized, the synchronization monitor is the object in the synchronized parentheses, and these methods must be called by this object.

  • wait(): Causes the thread to wait until other threads call the synchronization monitor's notify() or notifyAll() to wake up the thread. The current thread calling the wait() method releases the lock on the synchronization monitor.
  • notify(): Wake up a single thread waiting on this sync monitor, or wake up one randomly if multiple threads are waiting on this sync monitor. The awakened thread cannot execute until the current thread relinquishes its lock on the synchronization monitor.
  • notifyAll(): Wakes up all threads waiting on this sync monitor. The awakened thread cannot execute until the current thread relinquishes its lock on the synchronization monitor.
//取钱方法,该方法是同步方法
public synchronized void draw(double drawAmount){
    try{
        //如果flag为假,表示还未存钱,取钱方法阻塞
        if(!flag)
            wait();
        else{
            System.out.println("取钱");
            flag = false;    //转换标志
            notifyAll();    //唤醒其他线程
        }
    }catch(InterruptedException e){
        ex.printStackTrace();
    }
}
//存钱方法,该方法是同步方法
public synchronized void deposit(double depositAmount){
    try{
        if(flag)
            wait();
        else{
            System.out.println("存钱");
            flag = true;
            notifyAll();
        }
    }catch(InterruptedException e){
        ex.printStackTrace();
    }
}

Use Condition to control thread communication:

If the program does not use a synchronization monitor but a Lock object, then the above three methods cannot be called. Java provides the Condition class to maintain coordination. Condition can let those threads that have obtained the Lock object release the Lock object, and can also wake up other waiting threads. In this case, Lock replaces the synchronized method or synchronized code block, and Condition replaces the function of the synchronization monitor.

A Condition instance is bound to a Lock object. To get the Condition instance for a specific Lock object instance, just call the Lock instance's newCondition() method. The Condition class provides the following three methods:

  • await(): similar to wait()
  • signal(): similar to notify()
  • signalAll(): similar to notifyAll()
public class Account{
    ...
    //显式定义Lock实例
    private final Lock lock = new ReentrantLock();
    //获得指定Lock对象对应的Condition
    private final Condition cond = lock.newCondition();
    ...
    public void draw(double drawAmount){    //取钱方法
        lock.lock();    //加锁
        try{
            if(!falg)    //还未存钱时,取钱等待
                cond.await();
            else{
                System.out.println("取钱");
                flag = false;    //转换标志
                cond.signalAll();   //唤醒其他线程
            }
        }catch(InterruptedException e){
            ex.printStackTrace();
        }finally{
            lock.unlock();    //释放锁
        }
    }
    public void deposit(double drawAmount){   //存钱方法
        lock.lock();
        try{
            if(falg)
                cond.await();
            else{
                System.out.println("存钱");
                flag = true;
                cond.signalAll();
            }
        }catch(InterruptedException e){
            ex.printStackTrace();
        }finally{
            lock.unlock();
        }
    }

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325304535&siteId=291194637