Java concurrent programming JUC (3) Callable interface JUC auxiliary class read-write lock

A Callable interface

How to create threads

insert image description here

Callable interface Runnable interface comparison

  1. Is there a return value
    • Callable interface has a return value
    • Runnable interface has no return value
  2. whether to throw an exception
    • Callable interface, if the return value cannot be calculated, an exception will be thrown
    • Runnable interface, no exception will be thrown
  3. Implementation method name is different
    • Callable ----> call()
    • Runnable ----> run()

Callable uses the introduction

package new_course.chp4.callable;

import java.util.concurrent.Callable;

/**
 * @author Created by Lin Weihong
 * @date on 2022/6/2 13:25
 */
public class Demo01 {
    
    
    public static void main(String[] args) {
    
    
        //Runnable方式接口创建线程
        new Thread(new MyThread01(),"AA").start();

        //Callable接口,报错,不能直接用Callable替换Runnable
//        new Thread(new MyThread02(),"BB").start();
    }
}

//比较两个接口
class MyThread01 implements Runnable{
    
    

    @Override
    public void run() {
    
    

    }
}

class MyThread02 implements Callable {
    
    

    @Override
    public Integer call() throws Exception {
    
    
        return 200;
    }
}

insert image description here

FutureTask

package new_course.chp4.callable;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

/**
 * @author Created by Lin Weihong
 * @date on 2022/6/2 14:13
 * FutureTask
 */
public class Callable_FutureTask {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    

        FutureTask<Integer> futureTask = new FutureTask<>(() -> {
    
    
            System.out.println(Thread.currentThread().getName() + " come in callable");
            return 1024;
        });

        FutureTask<Integer> futureTask2 = new FutureTask<>(() -> {
    
    
            System.out.println(Thread.currentThread().getName() + " come in callable");
            return 2048;
        });
        //创建线程
        new Thread(futureTask, "lucy").start();
        new Thread(futureTask2, "kitty").start();

        while (!futureTask.isDone()) {
    
    //判断是否做
            System.out.println("wait...................");
        }

        //调用futureTask的get方法
        System.out.println(futureTask.get());//需要等待
        System.out.println(futureTask2.get());
        try {
    
    
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println(futureTask.get());//直接返回结果
        System.out.println(Thread.currentThread().getName() + " come over");

        //FutureTask原理 未来任务
        /**
         * 1、老师上课,口渴,想喝水,去买水不合适,讲课线程继续
         *    单开启线程找班长帮我买水
         *    把水买回来,我需要的时候直接get
         *
         * 2、4个同学,1同学(1+2+3+4+5)  2同学(10+11+12...+50)  3同学(60+61+62)  4同学(100+200)
         *    2同学计算量比较大,FutureTask单给2同学开线程计算
         *    先去汇总别的同学,最后等2计算完,最终在做汇总
         *
         * 注意:结果只会汇总一次
         *
         */
    }
}


Two powerful auxiliary classes of JUC

decrement count CountDownLatch

The constructor of this class is
**CountDownLatch(int count)** Construct a CountDownLatch initialized with a given count Insert the code snippet here

The two commonly used main methods
await() makes the current thread wait until the latch counts down to zero, unless the thread is interrupted
**countDown()** decrements the count of the latch, if the count reaches zero, it will be released all waiting threads

The CountDownLatch class can set a counter, and then use the countDown method to decrement 1, use the await method to wait for the counter to be not greater than 0, and then continue to execute the statement after the await method. The specific steps can be evolved into defining a class, decrementing 1 operation, and
waiting to 0, the execution result is 0

package new_course.chp4.juc;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author Created by Lin Weihong
 * @date on 2022/6/2 14:49
 * 演示 CountDownLatch
 *
 * 题目:设计一到多线程解决方案,教师有六名同学,只有当六名同学全部走完以后,班长才能锁门
 */

public class CountDownLatchDemo {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        //第一步.创建CountDownLatch,并设置初始值
        CountDownLatch countDownLatch = new CountDownLatch(6);

        //6名同学走出教师
        for (int i = 1; i <= 6 ; i++) {
    
    
            new Thread(() -> {
    
    
                System.out.println(Thread.currentThread().getName() + " 号同学离开了教室");
                //第二步 计数 -1
                countDownLatch.countDown();
            },String.valueOf(i)).start();
        }
        //第三步 等待,直到减到0才输出
        countDownLatch.await();
        System.out.println("班长关门");
    }
}

CyclicBarrier

This class allows a group of threads to wait for each other until reaching a common barrier point. In a program designed to design a group of fixed-size threads, these threads must wait for each other, because the barrier can be reused after releasing the waiting thread, so it is called a cycle barrier

Commonly used construction methods are:
**CyclicBarrier(int parties, Runnable barrierAction)** Create a new CyclicBarrier, which will start when a given number of participants (threads) are waiting, and execute the given A barrier operation that is performed by the last thread that enters the barrier

Commonly used methods are:
**await()** waits until all participants have called the await method on this barrier

package new_course.chp4.juc;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * @author Created by Lin Weihong
 * @date on 2022/6/2 15:07
 * 演示CyclicBarrier
 * <p>
 * 题目,当七把钥匙都得到的时候,才能开启这扇大门
 */
public class CyclicBarrierDemo {
    
    
    //创建固定值
    private static final int NUMBER = 7;

    public static void main(String[] args)  {
    
    
        //创建CyclicBarrier,达到值输出的信息
        CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () -> System.out.println("集齐7把钥匙,可以打开门"));
        //集齐七把钥匙过程
        for (int i = 1; i <= 7; i++) {
    
    
            new Thread(() -> {
    
    
                System.out.println(Thread.currentThread().getName() +" 把钥匙被收集到了");
                try {
    
    
                    //每次执行cyclicBarrier,障碍数+1,直到达到NUMBER,才会执行await()之后的方法
                    cyclicBarrier.await();
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }

    }
}

Semaphore

A counting semaphore. Conceptually, a semaphore maintains a set of permits, blocking each acquire() until a permit is available , and then acquiring the permit if necessary. Each **release()** adds a permit, potentially releasing a blocking acquirer. However, instead of using the actual permit object, the Semaphore just counts the number of available permits and acts accordingly

The specific and commonly used construction methods are:
**Semaphore(int permits)** Create a Semapore with a given number of permits and unfair fair settings

The specific commonly used methods are:
**acquire()** obtains a license from this semaphore, and blocks the thread until a license is provided, otherwise the thread is interrupted
**release()** releases a license and returns it to the signal quantity

Set the number of licenses Semaphore semaphore = new Semaphore(3);
Generally, acquire() will throw an exception, and release is executed in finally

package new_course.chp4.juc;

import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * @author Created by Lin Weihong
 * @date on 2022/6/2 16:26
 * 6辆汽车,停3个停车位
 */
public class SemaphoreDemo {
    
    
    public static void main(String[] args) {
    
    
        //创建Semaphore,设置3个许可数量
        Semaphore semaphore = new Semaphore(3);
        //模拟6辆汽车
        for (int i = 1; i <= 6 ; i++) {
    
    
            new Thread(() -> {
    
    
                //抢占
                try {
    
    
                    //抢占
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + " 抢到了车位");
                    //设置随机停车时间
                    try {
    
    
                        TimeUnit.SECONDS.sleep(new Random().nextInt(5));//随机停留5s以内
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " -----离开了车位");
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }finally {
    
    
                    //释放
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }
}

//结果:
1 抢到了车位
3 抢到了车位
2 抢到了车位
1 -----离开了车位
3 -----离开了车位
5 抢到了车位
4 抢到了车位
4 -----离开了车位
6 抢到了车位
5 -----离开了车位
6 -----离开了车位
2 -----离开了车位

Three read-write locks

pessimistic lock

Only one thread can operate a resource at a time
insert image description here

optimistic lock

Multiple threads can operate resources at the same time, and each operation will carry the version number version

insert image description here

table lock

Only one record is operated, but the entire table is locked, other threads cannot operate any resources, and deadlock will not occur

insert image description here

row lock

Only one record is operated, and the one record is locked. Other threads can operate other records, and a deadlock will occur.

Read lock (shared lock)

deadlock
insert image description here

Write lock (exclusive lock)

deadlock

insert image description here

Introduction of read-write lock problem

package new_course.chp4.readwrite;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author Created by Lin Weihong
 * @date on 2022/6/3 15:42
 */

class MyCache {
    
    
    //创建Map集合,因为数据不断发生变化,所以要加入volatile
    private volatile Map<String, Object> map = new HashMap<>();


    //向集合放入数据
    public void put(String key, Object value) {
    
    

        System.out.println(Thread.currentThread().getName() + " 正在进行写操作:" + key);
        try {
    
    
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        //放入数据
        map.put(key, value);
        System.out.println(Thread.currentThread().getName() + " 数据已经写完: " + key);
    }

    //从集合取出数据
    public Object get(String key) {
    
    
        Object result = null;
        System.out.println(Thread.currentThread().getName() + " 正在进行读取操作:" + key);
        try {
    
    
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        result = map.get(key);
        System.out.println(Thread.currentThread().getName() + " 已经读取完毕:" + key);
        return result;
    }

}

public class ReadWriteLockDemo {
    
    
    public static void main(String[] args) {
    
    
        MyCache myCache = new MyCache();
        //创建线程,往里面放数据
        for (int i = 1; i <= 5; i++) {
    
    
            final int num = i;
            new Thread(() -> {
    
    
                myCache.put(num + "", num + "");
            }, String.valueOf(i)).start();
        }

        //创建线程,往里面放数据
        for (int i = 1; i <= 5; i++) {
    
    
            final int num = i;
            new Thread(() -> {
    
    
                myCache.get(num + "");
            }, String.valueOf(i)).start();
        }
    }
}

Result: start reading before finishing writing, not the correct result

insert image description here

Retrofitting with read-write locks

package new_course.chp4.readwrite;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author Created by Lin Weihong
 * @date on 2022/6/3 15:42
 */

class MyCache {
    
    
    //创建Map集合,因为数据不断发生变化,所以要加入volatile
    private volatile Map<String, Object> map = new HashMap<>();

    //创建读写锁对象
    private ReadWriteLock rwLock = new ReentrantReadWriteLock();

    //向集合放入数据
    public void put(String key, Object value) {
    
    
        //添加写锁
        Lock lock = rwLock.writeLock();
        //加锁
        lock.lock();
        try {
    
    
            System.out.println(Thread.currentThread().getName() + " 正在进行写操作:" + key);
            TimeUnit.MILLISECONDS.sleep(300);
            //放入数据
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + " 数据已经写完: " + key);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //解锁
            lock.unlock();
        }

    }

    //从集合取出数据
    public Object get(String key) {
    
    
        //加上读锁
        Lock lock = rwLock.readLock();
        lock.lock();
        Object result = null;
        try {
    
    
            System.out.println(Thread.currentThread().getName() + " 正在进行读取操作:" + key);
            TimeUnit.MILLISECONDS.sleep(300);
            result = map.get(key);
            System.out.println(Thread.currentThread().getName() + " 已经读取完毕:" + key);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
        return result;
    }

}

public class ReadWriteLockDemo {
    
    
    public static void main(String[] args) {
    
    
        MyCache myCache = new MyCache();
        //创建线程,往里面放数据
        for (int i = 1; i <= 5; i++) {
    
    
            final int num = i;
            new Thread(() -> {
    
    
                myCache.put(num + "", num + "");
            }, String.valueOf(i)).start();
        }

        //创建线程,往里面放数据
        for (int i = 1; i <= 5; i++) {
    
    
            final int num = i;
            new Thread(() -> {
    
    
                myCache.get(num + "");
            }, String.valueOf(i)).start();
        }
    }
}

Running results: From the results, it can be found that the read lock is a shared lock, all threads can read together, and the write lock is an exclusive lock
insert image description here

Read-write lock summary

一个资源可以被多个读线程访问,或者可以被一个写线程访问,但是不能同时存在读写线程,读写互斥,读读共享

Evolution of Read-Write Locks

insert image description here

insert image description here

Downgrade of read-write lock

downgrade write lock to read lock

insert image description here

package new_course.chp4.readwrite;

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author Created by Lin Weihong
 * @date on 2022/6/3 16:43
 * 展示读写锁降级
 */
public class Demo01 {
    
    
    public static void main(String[] args) {
    
    
        //可重入读写锁对象
        ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
        //读锁
        ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
        //写锁
        ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();

        //锁降级
        //1 获取写锁
        writeLock.lock();
        System.out.println("小新--write");

        //2 获取读锁
        readLock.lock();
        System.out.println("小新--read");
        System.out.println("***************程序正常写和读,证明了写操作时,是可以读的******************");

        //3.释放写锁
        writeLock.unlock();

        //4.释放读锁
        readLock.unlock();

    }
}

Guess you like

Origin blog.csdn.net/weixin_48244108/article/details/125342603