首页 > 编程语言 >阅读《java并发编程实战》第五章

阅读《java并发编程实战》第五章

时间:2023-05-28 09:35:14浏览次数:50  
标签:task java 编程 并发 ans cache arg Computable public

阅读《java并发编程实战》第五章

Semaphore的应用举例

  • Semaphore的应用举例:实现一个固定大小的Set。当容器满了之后,无法add,线程阻塞。
public class BoundedHashSet {
    // invariant: size of Set always less than or equal to given size
    private final Set<Integer> set;
    private final Semaphore semaphore;

    public BoundedHashSet(final int size) {
        if (size <= 0) {
            throw new IllegalArgumentException("size should be above 0.");
        }
        this.set = Collections.synchronizedSet(new HashSet<>());
        this.semaphore = new Semaphore(size);
    }

    public boolean add(int x) throws InterruptedException {
        semaphore.acquire();
        boolean wasAdded = false;
        try {
            wasAdded = set.add(x);
            return wasAdded;
        } finally {
            if (!wasAdded) {
                semaphore.release();
            }
        }
    }

    public boolean remove(int x) {
        boolean res = set.remove(x);
        if (res) {
            semaphore.release(); // release不会抛 InterruptedException
        }
        return res;
    }
}

构建可伸缩的结果缓存(实践同步的思路)

version 1: 线程安全,但并发度很差

public interface Computable <A, V> {
    V compute(A arg);
}

/**
 * ThreadSafe, but performance/flexibility is very bad.
 * Not Good
 */
@ThreadSafe
public class Memorizer1<A, V> implements Computable<A, V> {
    // 有点像静态代理模式,类似Collections.synchronizedMap()
    private final Computable<A, V> task; 

    // GuardedBy(this)
    private final Map<A, V> cache = new HashMap<>();

    public Memorizer1(Computable<A, V> task) {
        this.task = task;
    }

    @Override
    public synchronized V compute(A arg) {
        V ans = cache.get(arg);
        if (ans == null) {
            ans = task.compute(arg);
            cache.put(arg, ans);
        }
        return ans;
    }
}

version 2: 线程安全,但是task.compute()方法有可能被调用两次,因为compute

public class Memorizer2<A, V> implements Computable<A, V> {

    private final Computable<A, V> task;
    private final Map<A, V> cache = new ConcurrentHashMap<>();

    public Memorizer2(Computable<A, V> task) {
        this.task = task;
    }

    @Override
    public synchronized V compute(A arg) {
        V ans = cache.get(arg);
        if (ans == null) {
            ans = task.compute(arg);
            cache.put(arg, ans);
        }
        return ans;
    }
}

version 3: 线程安全,但是同样有version 2的问题,会存在多个线程进入到 if (ans == null) 里面并发执行,重复计算。

public class Memorizer3<A, V> implements Computable<A, V> {

    // have an idea: if the param needs to be passed from outter, then initialize it in constructor
    // otherwise initialize in declaration.
    private final Map<A, Future<V>> cache = new ConcurrentHashMap<>();
    private final Computable<A, V> task;

    public Memorizer3(Computable<A, V> task) {
        this.task = task;
    }

    @Override
    public V compute(A arg) {
        Future<V> ans = cache.get(arg);
        if (ans == null) {
            Callable<V> eval = () -> task.compute(arg);
            FutureTask<V> ft = new FutureTask<>(eval);
            cache.put(arg, ft);
            ans = ft;
            ft.run();
        }
        try {
            return ans.get();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
    }
}

version 4: 线程安全,并发度也高,也解决了同时计算的问题,主要是利用了并发容器ConcurrentHashMap的putIfAbsent原子操作,只有成功添加的线程才有机会执行计算。

public class Memorizer4<A, V> implements Computable<A, V> {
    private final Computable<A, V> task;
    private final Map<A, Future<V>> cache = new ConcurrentHashMap<>();

    public Memorizer4(Computable<A, V> task) {
        this.task = task;
    }

    @Override
    public V compute(A arg) {        
        while (true) {
            Future<V> ans = cache.get(arg);
            if (ans == null) {
                FutureTask<V> ft = new FutureTask<>(() -> task.compute(arg));
                // if already exists, it will return the current value, so that ans will not be null, thus not run task
                ans = cache.putIfAbsent(arg, ft);
                if (ans == null) { // it means put success
                    ans = ft;
                    ft.run();
                }
            }
            try {
                return ans.get();
            } catch (CancellationException e) {
                cache.remove(arg, ans);
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

总结:通过这个例子,可以看到,充分利用并发容器类,以及并发工具类(FutureTask类,Task接口)可以简化并发编程的难度,以及提高并发的可伸缩性。

标签:task,java,编程,并发,ans,cache,arg,Computable,public
From: https://www.cnblogs.com/xianzhon/p/17437793.html

相关文章

  • java面试 (12)- Valiolate原理?是线程安全的吗?
    1:导致线程问题的原因:抢占式执行多个线程同时修改了同一个变量非原子性操作内存可见性问题指令重排问题2:并发编程三大特性可见性原子性有序性3:volatile关键字3.1volatile解决了内存可见性和指令重排序的问题写volatile变......
  • 实验6 结构体应用编程
    task1//打印不及格学生信息和所有学生信息程分别调用#include<stdio.h>#include<string.h>#defineN3typedefstructstudent{intid;charname[20];charsubject[20];doubleperf;//平时成绩doublemid;//期中成绩doublefinal;......
  • C语言编程—数组
    C语言支持数组数据结构,它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。数组的声明并不是声明一个个单独的变量,比如runoob0、runoob1、...、runoob99,而是声明一个数组变量,比如runoob,然后使用runoob[0]、runoob......
  • 实验6 结构体应用编程
    实验四#include<stdio.h>#include<string.h>#defineN100typedefstruct{charnum[100];ints1;ints2;doublesum;charlevel[10];}STU;intfun(STUa[],intn,STUh[]);intmain(){STUs[N]={{"GA......
  • 1. java + react 实现 HRM
    1.云服务的三种方式1.1IAAS基础设施即服务,只会提供基础的设施,eg:服务器,网络等;1.2PAAS平台即服务,提供平台,可以把自己写好的代码部署到平台上;1.3SAAS软甲即服务eg:hrm,cms,crm等;提供所有的服务;【部署到互联网】;......
  • 实验6 结构体应用编程
    1.task41//finish!2#include<stdio.h>3#include<string.h>4#include<stdlib.h>5#defineN10067typedefstruct{8charnum[10];//学号9ints1;//期末成绩10ints2;//平时成绩1......
  • 用JavaScript求1000以内的质数
    varprimes=[2];//2是质数,先将其加入质数数组中for(vari=3;i<=1000;i++){varisPrime=true;//假设i是质数for(varj=0;j<primes.length&&primes[j]<=Math.sqrt(i);j++){if(i%primes[j]===0){isPrime=false;//如果i可......
  • java后端开发流程总结
    流程简介:1、数据库见表(工具建表和cmd命令行(sql语言)两种方式)2、前端页面准备(html+css+js)3、controler层编写(针对具体功能编写,比如登录功能,在这一层获取前台输入的账号密码。这是就可以等待来自数据库里的数据了)4、接着编写serverdao层依据controler层的功能编写相应的get......
  • JAVA的springboot私人健身与教练预约管理系统、健身房管理系统,附源码+数据库+lw文档+P
    1、项目介绍任何系统都要遵循系统设计的基本流程,本系统也不例外,同样需要经过市场调研,需求分析,概要设计,详细设计,编码,测试这些步骤,基于java技术、springboot框架、B/S机构、Mysql数据库设计并实现了私人健身与教练预约管理系统。系统主要包括首页,个人中心,用户管理,教练管理,健身项目......
  • 使用Arduino IDE对ESP32双核进行编程
    使用ArduinoIDE对ESP32双核进行编程 [复制链接]qrcodeESP32 FreeRTOS查看: 22988 | 回复: 0风筝 发表于:2020-1-1722:23:45|只看该作者|只看大图|倒序浏览楼主 ESP模块因其Wi-Fi功能(如ESP8266、ESP-12E等)而广受欢迎。这些都是......