在现有线程安全类中添加功能

Java类库中包含许多有用的“基础模块”类。通常应该优先选择重用这些类而不是开发新类:重用能降低开发工作量、开发风险以及维护成本。很多情况这些现有的类只能提供大部分工作,我们需要在不破坏线程安全的情况下添加一些新的操作。

要添加一个新的原子操作,有以下几种方法:

第一种:修改原始的类

这种方法最简单最安全。但通常情况下无法访问或修改类的源代码。

第二种:扩展类机制(通过继承)

下面的代码中BetterVector扩展了Vector,并添加了新方法putIfAbsent。但并非所有的类都像Vector那样将状态向子类公开,因此也就不适合采用这种方法。

public class BetterVector<E> extends Vector<E>{
    public synchronized boolean putIfAbsent(E e){
        boolean absent = !contains(x);
        if(absent)
            add(x);
        return absent;
    }
}

第三种:客户端加锁机制

扩展类的功能,而不是类本身(将扩展代码放在一个“辅助类”中)。

public class ListHelper<E>{
    public List<E> list = Collection.synchronizedList(new ArrayList<E>());
    ...
    public boolean putIfAbsent(E e){
        synchronized(list){
            boolean absent = !contains(x);
            if(absent)
                add(x);
            return absent;
        }
    }
}

通过添加一个原子操作来扩展类是脆弱的,因为它将类的加锁代码分布到多个类中。客户端加锁机制更加脆弱,因为它将类C的加锁代码放到了与C完全无关的其他类中。

第四种:组合

下列代码中ImprovedList通过将List对象的操作委托给低层的List实例来实现List操作,同时还添加了一个原子的putIfAbsent方法。

public class ImprovedList<T> implements List<T>{
    private final List<T> list;
    public ImprovedList(List<T> list){ this.list = list; }
   
    public synchronized boolean putIfAbsent(T x){
        boolean contains = list.contains(x);
        if(contains)
            list.add(x);
        return !contains;
    }

    public synchronized void clear(){ list.clear(); }
    //按照类似的方式委托List的其他方法
    //...
}

ImprovedList通过自身的内置锁增加了一层额外的锁。他并不关心底层的List是否是线程安全的,即使List不是线程安全的或者修改了它的加锁实现,ImprovedList也会提供一致的加锁机制来实现线程安全性。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏宋凯伦的技术小栈

分享调用Java private方法

上周在修复bug时,发现Java类中某方法是private,且类中没有用到,第一感觉是方法多余。其实通过分析,发现原来Native Code会通过JNI调到此方...

24350
来自专栏开源优测

[快学Python3]基础知识

设置源文件编码 在默认情况下,Python3源码文件是以UTF-8编码进行保存的,所有的字符串都是unicode编码格式。 一般情况下,我们在源码文件第一行使用...

288130
来自专栏小灰灰

Nginx 路由转发配置笔记

Nginx 路由转发配置笔记 由于预算有限,只有一台服务器,想要玩的东西不少,所以这个台服务器上会提供多重服务,因此涉及到的nginx转发就必有重要了 由ng...

82460
来自专栏前端真相

为什么数组下标是从0开始?

数组寻址——arr[i] = base_address + i * type_size(1)

31960
来自专栏偏前端工程师的驿站

JS魔法堂: Native Promise Only源码剖析

一, 前言                                深入学习Promise的朋友应该都看过<深入理解Promise五部曲>这一系列的文章,...

22390
来自专栏java思维导图

Java中高级面试题部分答案解析(2)

这里是一些题型解析,还是这些话:不一定全部正确,有一些是没有固定答案的,如果发现有错的或者更适合的答案欢迎留言矫正。

9910
来自专栏互联网杂技

堆,栈,内存泄露,内存溢出介绍

简单的可以理解为: heap(堆):是由malloc之类函数分配的空间所在地。地址是由低向高增长的。 stack(栈):是自动分配变量,以及函数调用的时候所使用...

45640
来自专栏游戏开发那些事

【游戏开发】小白学Lua(上)

  在很多游戏中,脚本语言是不可或缺的一部分,很多游戏都使用到了Lua,js,python一类的脚本,脚本语言可以在很多方面给开发进程带来帮助。脚本语言可以作为...

16220
来自专栏软件开发

C语言 第四章 关系、逻辑运算与分支流程控制

一、关系运算 若干表达式用关系运算符连接而成,就构成关系表达式。 关系表达式中,表达式成立,其值为1(真);表达式不成立,其值为0(假)。 其他表达式中,其结...

216100
来自专栏Java架构沉思录

Redis进阶不得不了解的内存优化细节

Redis所有的数据都在内存中,而内存又是非常宝贵的资源。对于如何优化内存使用一直是Redis用户非常关注的问题。本文让我们深入到Redis细节中,学习内存优化...

67520

扫码关注云+社区

领取腾讯云代金券