[8] Some classes under the java.util.concurrent package

1. Synchronization lock lock, and some commonly used implementation classes

Synchronization lock lock, note (different from synchronized):

1) Use lock to manually release the lock anyway (otherwise a deadlock will occur), so put it in the finally block, and the lock will be automatically released after synchronized leaves the block, and generally no deadlock will occur

2) Lock allows the thread waiting for the lock to respond to the interruption . When the thread holding the lock does not release the lock for a long time, the thread waiting for the lock can be interrupted to do other things (through the lock.lockInterruptibly() method) to avoid deadlock . But synchronized does not work. When using synchronized, the waiting thread will wait forever and cannot respond to interrupts;

lock.lockInterruptibly() method: When two threads want to acquire a certain lock through lock.lockInterruptibly() at the same time, if thread A acquires the lock at this time, and thread B has to wait again, then the waiting of thread B can be interrupted at this time process. (When a thread acquires the lock, it will not be interrupted, only the thread in the blocking process can be interrupted.)

3) Through Lock, you can know whether the lock has been successfully acquired , but synchronized can't do it. lock.tryLock(); (returns true if the lock is acquired, otherwise it returns false, you can also set the waiting time, if it is acquired within the waiting time, it returns true, otherwise it returns false)

4) Lock can improve the efficiency of read operations by multiple threads (ReadWriteLock).

1. Reentrant lock

ReentrantLock (ReentrantLock): ReentrantLock is based on AQS. Reentrancy means that after a thread has acquired the lock (during the lock execution period), the thread can acquire the lock again and enter the synchronization code block. It needs to be emphasized here that the thread referred to in the concept of reentrancy is the thread that has acquired the lock. This does not conflict with thread safety, because only one thread can acquire the lock, which means that it is always the same thread (the thread that acquires the lock). ) When running synchronous code, it is equivalent to single-threaded operation, and of course it is thread-safe. The synchronized keyword also supports reentrancy. For example, synchronized can be used in recursive calls.

ReentrantLock provides fair lock and unfair lock (the default is unfair lock) .

Fair lock: Thread acquires locks in the order of waiting queue for applying locks. (Specified as a fair lock: Lock lock = new ReentrantLock(true);)

Unfair locks: Threads acquire locks randomly and do not follow the waiting queue for applying locks.

The synchronized implementation is an unfair lock: the thread randomly acquires the lock.

ReentrantLock provides explicit addition and unlocking operations. Provides lock(), unlock() methods for adding and unlocking operations, while synchronized is to implicitly perform lock and unlock operations (depending on the compiler to compile it into moniterenter and moniterexit). ReentrantLock's waiting for the lock can be interrupted. When the thread holding the lock does not release the lock for a long time, the thread waiting for the lock can choose to give up waiting, thus avoiding the deadlock problem that may be caused by synchronization. ReentrantLock.tryLock() can set the waiting time.

eg:

        while(true){

            lock.lock();

            try {

                if(tickets>0){

                    System.out.println(Thread.currentThread().getName()+"成功售票:"+tickets);

                    Thread.sleep(20);

                    tickets--;

                }else{

                    break;

                }

            } finally {

                lock.unlock();

            }

        }

When using lock to communicate with threads, you need to use Condition's await, signal, and signalAll methods instead of wait, notify, and notifyAll in the Object class.

Condition condition1 = lock.newCondition();

note:

Object's notify() method can wake up a thread that is waiting for the lock of the object. When multiple threads are waiting for the lock of the object, only one of them can be awakened. It is not known which thread is woken up. Use wait, notify, notifyAll methods must be in the synchronized code block or synchronized method

Calling Condition's await(), signal(), signalAll() methods must be protected by lock, that is, must be between lock.lock() and lock.unlock before you can use the await() corresponding to Object in Conditon Wait(); signal() in Condition corresponds to notify() of Object, but the thread to be awakened can be specified using signal ; signalAll() in Condition corresponds to notifyAll() of Object

2. Read-write lock

When a thread is currently occupying the read lock, it needs to perform a write operation, and it needs to wait for the read lock to be released before acquiring the write lock

When a thread is currently occupying a write lock, it needs to perform a read operation, and it needs to wait for the write lock to be released before acquiring the read lock

For example: when there are multiple threads reading and writing files, read operations and write operations will conflict, and write operations and write operations will conflict, but read operations and read operations will not conflict.

However, if the synchronized keyword is used to achieve synchronization, it will cause a problem:

If multiple threads are only performing read operations, when one thread is performing read operations, other threads can only wait and cannot perform read operations.

Therefore, a mechanism is needed to ensure that when multiple threads are only performing read operations, there will be no conflicts between threads, which can be done through Lock.

In addition, through Lock, you can know whether the thread has successfully acquired the lock. This is something that synchronized can't do

/**
* 读写锁:
* 写写、读写互斥,需要等待释放锁
* 读读不互斥,不需要释放锁
* @author JACK
*
*/
public class TestReadWriteLock {
    public static void main(String[] args) {
        ReadAndLockDemo demo = new  ReadAndLockDemo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                demo.set((int)(Math.random()*100));
            }
        },"write").start();
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    demo.get();
                }
            }).start();
        }
    }
}
class ReadAndLockDemo{
    private int number=0;
    ReadWriteLock readWriteLock = new  ReentrantReadWriteLock();
    //读
    public void get(){
        readWriteLock.readLock().lock();//上读锁
        try {
            System.out.println(Thread.currentThread().getName()+":"+number);
        } finally {
            readWriteLock.readLock().unlock();//释放读锁
        }
        
    }
    //写
    public void set(int number){
        readWriteLock.writeLock().lock();//上写锁
        try {
            System.out.println(Thread.currentThread().getName()+number);
            this.number=number;
        } finally {
            readWriteLock.writeLock().unlock();//释放写锁
        }
    }
}

3. Atresia

/**
* CountDownLatch:闭锁,在进行计算时,只有在其他线程计算全部完成,当前运算才执行
* @author JACK
*
*/
public class TestCountDownLatch {
    public static void main(String[] args) {
        CountDownLatch latch = new  CountDownLatch(5);//参数表示?个线程完成后,主线程才运算
        LatchDemo latchDemo = new  LatchDemo(latch);
        long currentTimeMillis =  System.currentTimeMillis();
        for(int i=0;i<5;i++){
            new Thread(latchDemo).start();
        }
        try {
            latch.await();//等待其他线程上的运算完成,当前线程才可以进行运算
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long currentTimeMillis2 =  System.currentTimeMillis();
        System.out.println("耗费时间:"+(currentTimeMillis2-currentTimeMillis));
    }
}
class LatchDemo implements Runnable{
    private CountDownLatch latch;
    
    public LatchDemo(CountDownLatch latch) {
        this.latch=latch;
    }
    @Override
    public void run() {
        System.out.println("线程");
        try{
            for(int i=1;i<10000;i++){
                System.out.println(i);
            }
        }finally{
            latch.countDown();//线程数减1
        }
    }
    
}

 

Guess you like

Origin blog.csdn.net/Jack_PJ/article/details/88015515