effective java 读书笔记---第10章 并发

本文深入探讨Java并发编程的关键概念和技术,包括同步访问共享数据的方法、避免过度同步的重要性、使用Executor和Task的优势、并发工具的优先级高于wait和notify、线程安全的文档性等,并提供了一系列实用的编程技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

66.同步访问共享的可变数据
基本数据类型除了 long 与 double 都是原子性的
java虽然保证了在读取原子数据时不会看到任意的数值,但它并不能保证一个线程的写入,对例外一个线程是可见的,考察如下代码:

    private static boolean canAdd = false;
    public static void main(String[] args)
        throws InterruptedException
    {
        new Thread(() -> {
            int i = 0;
            while (!canAdd)
            {
                i++;
            }
        }).start();
        TimeUnit.SECONDS.sleep(1);
        canAdd = true;
    }

其实这块代码将永远不会停止,而不是预想的那样在1s 左右停止,这是由于虚拟机在运行时将代码优化成了 if(!canAdd) while(true){…}
修正这个问题应该同步访问canAdd

    private static boolean canAdd = false;
    public synchronized static boolean getCanAdd(){
        return canAdd;
    }
    public synchronized static void stop(){
        canAdd = true;
    } 
    public static void main(String[] args)
        throws InterruptedException
    {
        new Thread(() -> {
            int i = 0;
            while (!getCanAdd())
            {
                i++;
            }
        }).start();
        TimeUnit.SECONDS.sleep(1);
        stop();
    }

注意读写方法都需要同步,如果只同步了一个方法,同步是不起作用的
如果同步方法不为了互斥访问,以上例子可以使用volatile 来修饰 canAdd,保证每个线程在读取该域时都将看到最近刚刚被写入的值
注意++操作符不会原子的,可能存在第二个线程在第一个线程读取旧值与返回新值之间读取这个域,可以使用 synchronized 来保证++操作符的互斥,其实可以使用 AtomicLong 类来实现自增
不共享可变的数据,或者共享不可变的数据就不存在需要考虑线程同步的问题
当多个线程共享可变数据时每个读或者写的线程都必须执行同步

67.避免过度同步
在一个被同步的区域内部,不要调用设计成要被覆盖的方法,或者是由客户端以函数对象的形式提供的方法
为了避免死锁和数据破坏,千万不要从同步区域内部调用外来方法

68.executor和 task 优先于线程
尽量不要编写自己的工作队列,尽量不要直接使用线程
工作队列Executors 类中提供了各种线程池(单例,多例,固定数量,可配置数量等的) ThreadPoolExecutor
任务有两种 Runnable与 Callable
任务的通用机制: executor service
ScheduledThreadPoolExecutor 来代替 timer执行定时任务

69.并发工具优先于wait 和notify
并发集合,不可能排除并发活动,将它锁定没有什么作业,只会使程序变慢
ConcurrentHashMap BlockingQueue
同步器(Synchronizer),最常用的同步器CountDownLatch和Semaphore,较少使用CyclicBarrier Exchanger
倒计数锁存器(CountDownLatch),构造方法 int 值只所有在等待的线程在被处理之前必须在锁存器上调用countDown方法的次数
定时操作应该使用 System.nanoTime而不是System.currentTimeMills,前者更加精准,不受系统的实时时钟影响
永远应该在 while 循环中使用 wait 方法,循环可以在等待之前和之后测试条件

synchronized(obj){
        while(...obj){
       obj.wait();
    }
    ....
}

在 while 中检查条件的原因:其他线程获取锁后调用 notify 但已经改变了 obj 的状态,此时需要重新检查条件,条件并不成立,但某个方法恶意调用了 notify,通知线程过度大方 notifyAll,在没有通知的情况下线程也有可能会苏醒(伪唤醒)
正常来说不应该使用 wait 与 notify
优先使用 notifyAll,如果使用 notify,请小心保持程序的活性

70.线程安全的文档性
线程安全级别:不可变的,无条件线程安全的,有条件线程安全,非线程安全的,线程对立的(几乎不存在)
私有锁对象模式(类内部持有锁,保证线程安全)只能用于无条件线程安全的类,可以避免子类和客户端程序的干扰

71.慎用延迟初始化
就像绝大数优化一样,延迟初始化除非绝对必要,否则不要这样做
大多数情况正常初始化都要优于延迟初始化
如果使用延迟初始化来破坏初始化循环就需要使用同步方法
如果出于性能考虑需要对静态域使用延迟初始化,就使用如下方式(lazy initialization holder class):

    private TestLazyInit()
    {
    }

    private static class TestLazyInitHolder
    {
        static final TestLazyInit field = new TestLazyInit();
    }

    public static TestLazyInit getInstance()
    {
        return TestLazyInitHolder.field;
    }

如果出于性能考虑对实例域做延迟加载,就使用双重检查模式

//注意 field 需要 volatile 修饰
if(null==field){
    synchronized(this){
        if(null==field){
            ....    
        }
    }
}

可以使用局部变量检查的方式(性能更好)
虽然也可以对静态域使用双重检查模式,但没有必要这样做,前一种方法是更好的选择

72.不要依赖于线程调度器
任何依赖线程调度器来达到正确性或者性能要求的程序,很有可能都是不可移植的
线程优先级是 java 平台上最不可移植的特征
大多数情况Thread.yield的唯一用途是在测试期间人为的增加程序的并发性,应该使用 Thread.sleep(1)代替 Thread.yield,千万不要使用 Thread.sleep(0)它会立即返回

73.避免使用线程组
使用 executor

内容概要:该论文研究增程式电动汽车(REEV)的能量管理策略,针对现有优化策略实时性差的问题,提出基于工况识别的自适应等效燃油消耗最小策略(A-ECMS)。首先建立整车Simulink模型和基于规则的策略;然后研究动态规划(DP)算法和等效燃油最小策略;接着通过聚类分析将道路工况分为四类,并设计工况识别算法;最后开发基于工况识别的A-ECMS,通过高德地图预判工况类型并自适应调整SOC分配。仿真显示该策略比规则策略节油8%,比简单SOC规划策略节油2%,并通过硬件在环实验验证了实时可行性。 适合人群:具备一定编程基础,特别是对电动汽车能量管理策略有兴趣的研发人员和技术爱好者。 使用场景及目标:①理解增程式电动汽车能量管理策略的基本原理;②掌握动态规划算法和等效燃油消耗最小策略的应用;③学习工况识别算法的设计和实现;④了解基于工况识别的A-ECMS策略的具体实现及其优化效果。 其他说明:此资源不仅提供了详细的MATLAB/Simulink代码实现,还深入分析了各算法的原理和应用场景,适合用于学术研究和工业实践。在学习过程中,建议结合代码调试和实际数据进行实践,以便更好地理解策略的优化效果。此外,论文还探讨了未来的研究方向,如深度学习替代聚类、多目标优化以及V2X集成等,为后续研究提供了思路。
内容概要:论文《基于KANN-DBSCAN带宽优化的核密度估计载荷谱外推》针对传统核密度估计(KDE)载荷外推中使用全局固定带宽的局限性,提出了一种基于改进的K平均最近邻DBSCAN(KANN-DBSCAN)聚类算法优化带宽选择的核密度估计方法。该方法通过对载荷数据进行KANN-DBSCAN聚类分组,采用拇指法(ROT)计算各簇最优带宽,再进行核密度估计和蒙特卡洛模拟外推。实验以电动汽车实测载荷数据为对象,通过统计参数、拟合度和伪损伤三个指标验证了该方法的有效性,误差显著降低,拟合度R²>0.99,伪损伤接近1。 适合人群:具备一定编程基础和载荷数据分析经验的研究人员、工程师,尤其是从事汽车工程、机械工程等领域的工作1-5年研发人员。 使用场景及目标:①用于电动汽车载荷谱编制,提高载荷预测的准确性;②应用于机械零部件的载荷外推,特别是非对称载荷分布和多峰扭矩载荷;③实现智能网联汽车载荷预测与数字孪生集成,提供动态更新的载荷预测系统。 其他说明:该方法不仅解决了传统KDE方法在复杂工况下的“过平滑”与“欠拟合”问题,还通过自适应参数机制提高了方法的普适性和计算效率。实际应用中,建议结合MATLAB代码实现,确保数据质量,优化参数并通过伪损伤误差等指标进行验证。此外,该方法可扩展至风电装备、航空结构健康监测等多个领域,未来研究方向包括高维载荷扩展、实时外推和多物理场耦合等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值