《Java并发编程实践》

Java并发编程实践-this溢出    《Java并发编程实践》一书中,提到“在构造函数中调用一个可覆盖的(那些既不是private,也不是final的)实例方法同样会导致this引用在构造期间溢出。”,这句话读来简单,但是却干货满满,这里举一个简单的示例来进行说明。 ThisEscape类: public class ThisEscape { public ThisEscape() { Sy... 阅读详情

Summary

这本书也就将近300页的样子,关于这本书多么的厉害不多说了,相比去看这本书的人心里都有数。

个人感觉这本书还是比较偏理论,站在比较高的高度来指导你如何写出健壮的并发程序,而非介绍API的(虽然也有大量的例子)。看这种书会有种感觉就是云里雾里的,不知道自己到底吸收了多少(目前我就是这种感受),还是得靠自己有一些并发编程的经验或者多加练习,这样感觉效果会好一点。


对象的共享

主要是由于“重排序”的存在,在没有正确同步的多线程环境下,可能得不到数据正确的值。

加锁,不仅仅确保原子性,也可以确保可见性。 volatile变量也可以确保可见性,但是并不能保证原子性。 而且volatile变量适用于:

  • 对变量的写不需要依赖变量当前值,或者只有单线程修改变量值
  • 该变量不会与其他状态变量一起纳入不变性条件
  • 在访问变量时不需要加锁

发布:使对象能够在当前作用域之外的代码中使用。 逸出:当某个不该发布的对象被发布时,这种情况称之为逸出。 主要是说要正确的发布对象,对象发布之后不能确保其他线程如何使用该对象,所以如果不是安全发布,那么该对象的不变性条件无法得到保证。发布一个对象的时候,可能会间接的发布该对象的非私有变量所引用的其他对象。 不要在构造过程中使this引用逸出。

满足同步需求的另外一种做法是使用不可变对象,不可变对象是绝对线程安全的。

  • 对象创建以后其状态就不能修改
  • 对象的所有域都是final的
  • 对象是正确创建的

不可变对象内部仍可以用可变对象来管理状态,注意不可变对象和不可变的对象引用之间的区别。

//在可变对象基础上构建的不可变对象
public final calss ThreeStooges {
    private final Set<String> stooges = new HashSet<String>();

    public ThreeStooges() {
        stooges.add("Moe");
        stooges.add("Larry");
        stooges.add("Curly");
    }

    public boolean inStooge(String name) {
        return stooges.contains(name);
    }
}

通过将域声明为final,也相应的告诉其他人员,这些域是不会变化的。 除非需要某个域是可变的,否则应将其声明为final域。

安全发布的常用模式:

  • 在静态初始化函数中初始化一个对象引用
  • 将对象的引用保存到volatile类型的域或者AtomicReferance对象中
  • 将对象的引用保存到某个正确构造对象的final类型域中
  • 将对象的引用保存到一个由锁保护的域中

在并发程序中使用和共享对象时,可以使用一些实用的策略,包括:

  • 线程封闭 (线程封闭的对象只能由一个线程拥有,对象被封闭在该线程中,并且只能由该线程修改)
  • 只读共享
  • 线程安全共享 (线程安全的对象在其内部实现同步,因此多个线程可以通过对象的公有借口来进行访问而不需要进一步同步)
  • 保护对象 (被保护的对象只能通过持有特定的锁来访问。 保护对象包括封装在其他线程安全对象中的对象,以及已发布的并且由某个特定锁保护的对象。)

感觉搞懂双重检查锁定的话,这一块的内容能了解大约一半吧。


对象的组合

在设计线程安全类的过程中,需要包含以下三个基本要素:

  • 找出构成对象状态的所有变量
  • 找出约束状态变量的不变性条件
  • 建立对象状态的并发访问管理策略

个人理解就是,找出这个对象里面可以被其他线程访问的变量,确定这些变量的不变性条件(规则),然后确保在修改这个对象的时候,时刻都能保持这些不变性条件。

  • 通过实例封闭的方式来构建线程安全的类 将数据封装在对象内部,可以将数据的访问限制在对象的方法上,从而容易确保线程在访问数据时总能持有正确的锁。

基本意思就是通过加锁(私有锁对象或者是对象的内置锁)来同步对数据的访问(Java监视器模式)。

  • 通过委托来实现线程安全 将当前类的线程安全性委托给一个已经确保是线程安全的类,基本意思是将当前类的状态保存在已知的线程安全类中。

当委托失效时(比如当前类有两个变量来标示状态,且不变性条件涉及到这两个变量),需要当前类自行构建线程安全策略。

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

  • 修改原有的类 (需要先理解代码中的同步策略,如果直接添加新方法到类中,那么意味着实现同步策略的所有代码仍处于同一个源文件中,从而更容易维护和理解。但是,源文件并不一定可修改)
  • 扩展这个类 (设计这个类时,并不一定考虑了可扩展性,父类的状态并不一定都向子类公开)
  • 客户端加锁 (主要问题在于需要知道被封装对象使用的是哪一个锁,即使知道了这样做还有一个问题,就是被封装类的加锁代码被放到了与与其完全无关的类中)
  • 组合 (新建的类通过内置锁为所有操作增加一层额外的锁,而不关心被组合类的安全策略。这样可能会造成性能损失,同时可能需要重写被组合类的所有方法。)

将同步策略文档化


基础构建模块

BlockingQueue: LinkedBlockingQueue, ArrayBlockingQueue,PriorityBlockingQueue andSynchronousQueue BlockingDeque: ArrayDeque and LinkedBlockingDeque

同步工具类

  • 闭锁 CountDownLatch (倒计时计数器)
  • FutureTask
  • 信号量 semaphore
  • 栅栏 CyclicBarrier(类似于闭锁,可重复利用;闭锁用于等待事件,而栅栏用于等待其他线程)
  • 栅栏 Exchanger (两方栅栏two-party,各方在栅栏位置上交换数据)

  • 可变状态是至关重要的 (所有的并发问题都可以归结为如何协调对并发状态的访问。可变状态越少,就越容易确保线程安全性。)
  • 尽量将域声明为final类型,除非需要它们是可变的。
  • 不可变对象一定是线程安全的 (不可变对象能极大降低并发编程的复杂性。它们更为简单且安全,可以任意共享而无须使用加锁或保护性复制等机制。)
  • 封装有助于管理复杂性
  • 用锁来保护每一个可变变量
  • 当保护同一个不变性条件中的所有变量时,要使用同一个锁
  • 在执行符合操作期间,要持有锁
  • 如果从多个线程中访问同一个可变变量时没有同步机制,那么程序会出现问题
  • 不要故作聪明地推断出不需要使用同步
  • 在设计过程中要考虑线程安全,或者在文档中明确地指出它不是线程安全的
  • 将同步策略文档化

任务执行

在线程中执行任务,一要搞清楚任务边界,二要确定现场调度策略

Executor Framework - Executor (interface) - ExecutorService (newFixThreadPool, newCachedThreadPool, newSingleThreadPool, newScheduledThreadPool, 这些pool都有一个线程池,然后默认情况下使用无界的LinkedBlockingQueue来保存待执行的任务,无界LinkedBlockingQueue的大小是Integer的最大值,这通常会有一定危险,任务执行速度小于生成速度的话,很有可能导致内存溢出。(这个地方有点类似于生产者消费者)) - Future - Callable - CompletionService (Executor & BlockingQueue) - invokeAll (执行一组任务,并且按照任务集合中迭代器的顺序将所有的Future添加到返回的集合中)


取消和关闭

取消操作的原因:

  • 用户请求取消
  • 有时间限制的事件
  • 应用程序事件
  • 错误
  • 关闭 通常取消操作最合理的办法是中断。 interrupt()中断目标线程 (调用interrupt()并不意味着立即停止目标线程正在进行的工作,而只是传递了请求中断的消息) isInterrupt()返回目标线程的中断状态 interrupted()清除当前线程的中断状态,并返回之前的值(始终没太搞清楚,清除中断状态和保存中断状态的意思)

响应中断一般有两种策略:

  • 传递异常 (从而使当前方法也成为可中断的阻塞方法)
  • 恢复中断状态 (从而使调用栈中的上层代码能够对其进行处理)

可以用Future来实现取消, Future.cancel。

处理不可中断的阻塞:

  • Java.io包中的同步Socket I/O (通过关闭底层的套接字,可以使得由于执行read或write方法而被阻塞的线程抛出一个SocketException)
  • Java.io包中的同步I/O (中断一个在InterruptibleChannel上等待的线程时,将抛出ClosedByInterruptException并关闭链路(这还会使得其他在这条链路上阻塞的线程同样抛出ClosedByInterruptException)。当关闭一个InterruptibleChannel时,将导致所有在链路操作上阻塞的线程都跑出AsynchronousException。)
  • Selector的异步I/O (如果一个线程在调用Selector.select方法时阻塞了,那么调用close或wakeup方法会使线程抛出ClosedSelectorException)
  • 获取某个锁 (如果一个线程由于等待某个内置锁而阻塞,那么将无法响应中断,因为线程认为它肯定会获得锁)

可以采用newTaskFor来封装非标准的取消 (处理不可阻塞中断这一块没遇到过,理解的一般)

停止基于线程的服务

  • 关闭ExecutorService有两种方法:shutdown() and shutdownNow()
  • 毒丸对象

Thread API中提供了UncaughtExceptionHandler,设置Handler来处理未捕获异常。

守护进程 (不影响JVM的关闭)


线程池的使用

在默认的一些线程池无法满足需求的时候,自行配置线程池(注意使用有界队列或者是无界队列)

public ThreadPoolExecutor(int corePoolSize,
                          int maxPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BolckingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {...}

考虑线程池配置时候需要注意的一些:

  • 线程运行时间
  • 任务是计算密集型还是I/O密集型
  • 注意线程的存活时间
  • 注意设置用来保存等待执行的任务的队列
  • 设置饱和策略
    • AbortPolicy -- 中止 (默认的饱和策略,饱和时将会抛出RejectedExecutionException)
    • CallerRunsPolicy -- 调用者运行 (将某些任务退回给调用者线程去执行,由于执行需要时间,调用者线程在一段时间内不会提交任务,线程池得以有时间执行任务)
    • DiscardPolicy -- 抛弃
    • DiscardOldestPolicy -- 抛弃最老的

线程工厂,很多情况下都需要使用定制的线程工厂。例如,希望为线程池中的线程制定一个UncaughtExceptionHandler,或者实例化一个定制的Thread类用于执行调试信息的记录,或者希望给线程起个有意义的名字。

扩展ThreadPoolExecutor,它提供了几个可以在子类化中改写的方法:beforeExecute、afterExecute和terminated。


避免活跃性危险

死锁:

  • 锁顺序死锁
  • 动态锁顺序死锁
  • 写作对象之间发生的死锁
  • 资源死锁 (两个线程分别以不同次序请求持有两个不同的资源)

开放调用,如果在调用某个方法时不需要持有锁,那么这种调用被称为开放调用。依赖于开放调用的类,通常能表现出更好的行为。这种通过开放调用来避免死锁的方法,类似于采用封装机制来提供线程安全的方法。

class Taxi {
    private Point location, destination;
    private final Dispatcher dispatcher;

    public synchronized Point getLocation() {
        return location;
    }

    public void setLocation(Point location) {
        boolean reachedDestination;
        synchronized (this) {
            this.location = location;
            reachedDestination = location.equals(destination);
        }
        if (reachedDestination) {
            dispatcher.notifyAvailable(this);
        }
    }
}


class Dispatcher {
    private final Set<Taxi> taxis;
    private final Set<Taxi> availableTaxis;

    public synchronized void notifyAvailable(Taxi taxi) {
        availableTaxi.add(taxi);
    }

    public Image getImage() {
        Set<Taxi> copy;
        synchronized (this) {
            copy = new HashSet<Taxi>(taxis);
        }
        Image image = new Image();
        for (Taxi t : copy) {
            image.drawMarker(t.getLocation());
        }
        return image;
    }
}

通过可定时的锁可以避免死锁,通过线程转储信息可以分析死锁。

其他活跃性危险

  • 饥饿 (线程由于无法访问它所需要的资源而不能继续执行时,就发生了“饥饿”。如果在Java应用程序中对优先级使用不当,或者在持有锁时执行一些无法结束的结构,就有可能导致“饥饿”)
  • 糟糕的响应性 (某个线程长时间占用锁)
  • 活锁 (该问题尽管不会阻塞线程,但是也不能继续执行,因为线程将不断重复执行相同的操作,而且总会失败。比较像:两个很有礼貌的人,相互给对方让路,然后在另一条路上相遇,因此他们反复地避让下去。)(解决办法:在重试的时候加入随机性。)

最常见的活跃性问题就是锁顺序死锁,在设计程序时应该避免产生锁顺序死锁:确保线程在获取多个所时采用一致的顺序,最好的解决办法是在 程序中始终使用开放性调用 。


性能与可伸缩性

衡量程序的性能一般可以从这两个方面:运行速度和处理能力

  • 运行速度:服务时间、延迟时间这些指标,主要指程序运行有多快
  • 处理能力:生产量、吞吐量这些指标,主要是指在计算机资源一定的情况下,能完成多少任务

在进行程序优化的时候(不管是“多块”还是“多少”),首先使程序正确,然后再提升性能,提升性能的时候要搞清楚想要提升的是哪方面的指标,而且要以测试为基准,不要妄加猜测。

关于多快,最终还是要受限于任务中有多少串行的部分,最高加速比为 1/ (F + (1-F)/N), 当N(处理器个数)趋近于无限大的时候,加速比趋近于1/F(串行部分的比例)。

所以理想情况下,所有可以并行的地方都并行执行且相对于串行部分,时间可以忽略不计,那么任务的运行时间就约等于任务中串行部分需要的时间。

关于多少,基本意思就是在增加计算资源(CPU,内存,存储容量,带宽)的时候,程序的吞吐量或者处理能力能够得到相应地增加。就是可扩展性。

线程引入的开销:

  • 上下文切换
  • 内容同步 (synchronized和volatile提供的可见性保证中可能会使用内存栅栏,内存栅栏会刷新缓存,是缓存无效。)(在无竞争的情况下JVM对于synchronized做了一些优化,比如如果只有一个线程访问当前锁对象,那JVM就可以通过优化去掉这个锁获取操作。还有粗化锁粒度啊之类的优化。)
  • 阻塞 (阻塞也有一点优化,就是阻塞之前先自旋,如果自旋一会就能获得锁那就不用挂起了,如果不能那在一定时间之后挂起。自旋的时间会根据之前自旋的状况调节,如果之前自旋一直不成功会直接挂起。)

减少锁的竞争,在并发程序中,对可伸缩性的最主要的威胁就是独占方式的资源锁:

  • 缩小锁的范围,快进快出
  • 缩小锁的粒度,细化锁
  • 锁分段(ConcurrentHashMap)
  • 避免热点域
  • 一些替代锁的方法(并发容器,读写锁,不可变对象以及原子变量)
  • 检测CPU的利用率(CPU利用率不高可能是:负载不足,I/O密集,外部限制,锁竞争)

并发程序的测试

并发测试大致分为两类:安全性测试(不发生任何错误行为)和活跃性测试(某个良好的行为终究会发生)

这部分主要还是需要结合书中提供的例子

正确性测试

  • 基本的单元测试 (类似于在串行上下文中执行的测试,也就是把当前的程序当成串行的测试一下)
  • 对阻塞的测试 (测试阻塞可以用中断,当然这要求被测试程序响应中断)
  • 安全性测试 (比如测试生产者消费者模式,可以通过一个对顺序敏感或者不敏感的校验和函数来测试。生产者生产随机数,然后计算总和;消费者拿到生产的随机数,然后计算总和。)
  • 资源管理的测试 (通常是测试是否做了不该做的事情,比如在不需要的时候应该销毁对对象的引用)
  • 使用回调 (通常回调函数的执行是在对象生命周期的一些已知位置上,并且这些位置非常适合判断不变性条件是否被破坏)
  • 产生更多交替行为 (Thread.yield)

性能测试

  • 增加计时功能 (书中给的例子是给CyclicBarrier设置栅栏动作,the command to execute when the barrier is tripped。)
  • 多种算法的比较 (LinkedBlockingQueue比ArrayBlockingQueue的可伸缩性要好,主要是因为链表队列拥有更好的内存分配和GC等开销,而且与数组队列相比,链表队列的put和take等方法支持并发性更高的访问,因为一些优化后的链表队列算法能将头节点的更新操作和尾节点的更新操作分离开来。)
  • 响应性衡量 (主要是了解服务时间的变化情况。非公平的信号量通常能实现更好的吞吐量,而公平的信号量则实现更低的变动性。)

避免性能测试的陷阱

  • 垃圾回收 (垃圾回收可能对某一次测试的时间产生很大影响,但是垃圾回收器可能在任何时刻运行,两种防止的策略:1,确保垃圾回收不执行;2,确保垃圾回收执行多次,这通常需要比较长的测试时间。)
  • 动态编译 (在某个时刻如果一个方法的运行次数足够多,那么动态编译器会将它编译为机器代码,当编译完成后,代码的执行方式将从解释执行变成直接执行。防止策略:程序运行足够长时间。在HotSpot中,如果在运行程序时使用命令行选项-xx:+PrintCompilation,那么当动态编译运行时,将输出一条信息。)
  • 对代码路径的不真实采样 (需要尽量覆盖代码路径)
  • 不真实的竞争程度
  • 无用代码的消除 (优化编译器会找出并消除那些不会对输出结果产生任何影响的无用代码(Dead Code),优化之后对程序性能的影响无法预测。防止策略:计算某个派生对象的hashcode,并且将其与一个任意值比较。)

其他测试办法

  • code review
  • 静态分析工具 (FindBugs)
  • 面向方面的测试技术
  • 分析和检测工具

显式锁 -- ReentrantLock

ReentrantLock实现了Lock接口,并提供了与synchronized相同的互斥行和内存可见性。 ReentrantLock提供了更加灵活的功能:定时锁(tryLock()),轮询锁(tryLock()),可中断的锁获取操作(tryLock(timeout, unit)),非块结构的加锁,提供公平锁;而且性能比内置锁更好。

与显式锁相比,内置锁有自己的优势,内置锁为开发人员所熟悉,并且简介紧凑,现有许多程序都已经使用了内置锁;显式锁更危险,因为需要明确的unlock,内置锁自动释放锁;内置锁在线程转储中能给出哪些调用帧获得了哪些锁;内置锁是JVM的内置属性(synchronized),它能执行一些优化(粗化锁粒度,单线程访问消除锁之类的),而且未来更有可能提升内置锁的性能。

读写锁 ReadWriteLock

读写锁的一些可选实现包括:

  • 释放优先 (当一个写锁释放锁的时候,如果队列中同时存在读锁和写锁,应该优先选择哪个?还是最先发出请求的线程?)
  • 读线程插队 (如果锁由读线程持有,但有写线程在等待,那么新到达的读线程能否理解获得访问权?)
  • 重入性 (读锁和写锁是否是可重入的?)
  • 降级 (如果一个写线程持有锁,那么它是否可以在不释放锁的情况下获取读锁?)
  • 升级 (读锁能否优先于其他正在等待的读线程和写线程而升级为一个写入锁?)

这些可选实现是在自己实现ReadWriteLock的时候可以选择实现的功能,ReentrantReadWriteLock为这两种锁都提供了可重入的加锁语义,而且在构造的时候可以选择是一个公平的锁还是非公平的。

构建自定义的同步工具

状态依赖性,条件队列,条件谓词 Lock -- 内置锁, Condition -- 内置条件队列 AbstractQueuedSynchronizer

这一章节前面都是一些理论,消化的一般;后面在介绍AQS,concurrent包里面很多同步工具都是基于AQS构建的。

CAS -- compare and swap

在竞争程度较高的情况下(线程本地计算较少),锁的性能要比原子变量好;但是在更真实的情况下,原子变量的性能将超过锁(竞争程度适中)。 另外,在这两种情况下,ThreadLocal的性能都是最好的。

不太明白为什么这里竞争程度适中的情况会更加真实,大概是因为书中来计较的时候,竞争程度较高的这种情况下,除了竞争锁或原子变量,什么都不做。

如果在某种算法中,一个线程的失败或者挂起不会导致其他线程也失败或者挂起,那么这种算法就被称为非阻塞算法。 如果在算法的每个步骤中都存在某个线程能够执行下去,那么这种算法被称为无锁算法。 如果在算法中仅将CAS用于协调线程之间的操作,并且能正确地实现,那么它既是一种无阻塞算法,也是一种无锁算法。

private static class Node <E> {
        public final E item;
        public Node<E> next;

        public Node(E item) {
            this.item = item;
        }
    }

    private static class NodeQ <E> {
        public final E item;
        public AtomicReference<NodeQ<E>> next;

        public NodeQ(E item, NodeQ<E> next) {
            this.item = item;
            this.next = new AtomicReference<NodeQ<E>>(next);
        }
    }

    //非阻塞栈
    public class ConcurrentStack <E> {
        AtomicReference<Node<E>> top = new AtomicReference<Test.Node<E>>();

        public void push(E item) {
            Node<E> newHead = new Node<E>(item);
            Node<E> oldHead;
            do {
                oldHead = top.get();
                newHead.next = oldHead;
            } while (!top.compareAndSet(oldHead, newHead));
        }

        public E pop() {
            Node<E> oldHead;
            Node<E> newHead;
            do {
                oldHead = top.get();
                if (null == oldHead) {
                    return null;
                }
                newHead = oldHead.next;
            } while (!top.compareAndSet(oldHead, newHead));
            return oldHead.item;
        }
    }

    //非阻塞链表
    public class LinkedQueue <E> {
        private final NodeQ<E> dummy = new NodeQ<E>(null, null);
        private final AtomicReference<NodeQ<E>> head = new AtomicReference<NodeQ<E>>(dummy);
        private final AtomicReference<NodeQ<E>> tail = new AtomicReference<NodeQ<E>>(dummy);

        public boolean put(E item) {
            NodeQ<E> newNodeQ = new NodeQ<E>(item, null);
            while (true) {
                NodeQ<E> currentTail = tail.get();
                NodeQ<E> tailNext = currentTail.next.get();
                if (currentTail == tail.get()) {
                    if (tailNext != null) {
                        //队列处于中间状态,推进尾节点
                        tail.compareAndSet(currentTail, tailNext);
                    } else {
                        //队列处于稳定状态,尝试插入新节点
                        if (currentTail.next.compareAndSet(null, newNodeQ)) {
                            //插入操作成功,尝试推进尾节点;此时如果推进尾节点不成功,可由其他线程帮其推进。
                            tail.compareAndSet(currentTail, newNodeQ);
                            return true;
                        }
                    }
                }
            }
        }
    }

CAS中的ABA问题,AtomicStampedReference(添加版本号)和AtomciMarkableReference(将节点保留在队列中的同时,将其标记为“已删除的节点”)通过不同的手段可以避免ABA问题。


Java内存模型

参阅ifeve.com

JAVA并发编程实践 JAVA并发编程实践 JAVA并发编程实践 JAVA并发编程实践 立即下载

相关推荐

JAVA并发编程实践(中文).pdf

JAVA并发编程实践(中文),很好的的一本书,希望对开发人员有所帮助

玩玩并发编程(五)--JUC--Lock

JUC--Lock1. 什么是Lock2. 使用案例3. 原理4. Lock和synchronized的选择4. 小结 1. 什么是Lock dog li 写的JUC真的是绝了。AQS&CAS无敌大法,就是看不懂。! lock是dog li 写的一个java的锁,锁管理由程序员自己控制 2. 使用案例 public interface Lock { void lock(); void lockInterruptibly() throws InterruptedExceptio

你是真的皮 310

《java并发编程实践》

《java并发编程实践》,多线程,多线程,多线程。。。。

dog李java_Dog.java

public class Dog {//public String eat;//protected String id;//private String name;//String introduce;////public String dosomething(String str){//System.out.println("散步,训练,吃东西");//return str;//}//publi...

weixin_36252784的博客 450

Java并发编程实践高清pdf及源码

JAVA并发编程实践中文版 英文版 原书源码 英文版本还是不错的 英文过硬可以直接看英文版本

告别图像畸变:手把手教你用HALCON的`gen_image_to_world_plane_map`做高精度视觉测量

本文详细解析了HALCON的`gen_image_to_world_plane_map`算子在工业视觉测量中的应用,通过精确的几何变换消除透视畸变,提升测量精度。文章涵盖相机标定、世界坐标系建立、实战应用及性能优化,适用于液晶屏质检、金属板检测等场景,帮助工程师实现高精度视觉测量。

weixin_28718487的博客 423

《JAVA并发编程实践》资源文件介绍

《JAVA并发编程实践》资源文件介绍 去发现同类优质开源项目:https://gitcode.com/ 《JAVA并发编程实践》是一个关于JAVA并发编程的深入学习资料,旨在帮助开发者深入理解JAVA并发编程的原理和技巧。这份资源详细介绍了JAVA并发编程的概念、方法和最佳实践,是提高JAVA编程技能的宝贵资料。 该资源文件包含以下内容: 并发编程的基础知识 并发工具和API的使用方法 线程安全...

gitblog_06727的博客 325

深入理解HanLP1.x,填平可能遇到的坑

提取指定数量的摘要。自定义词典为什么要单独领出来讲呢?因为在我们的业务开发中,可能分词的效果并不理想,需要定制自己的词库,索引更加常用。例如:小米su7特斯拉modelY比亚042025款6575767670908我们需要分出小米su72025款等词。我们分别看看用字典和不用字典的效果。

static_coder的博客 977

Java并发编程实践

并发编程是指通过设计和实现多线程程序来利用计算机资源和提高程序执行效率的编程方法。它的主要特点是多线程之间的竞争和协作关系。线程安全性是指并发编程中,在多个线程访问共享数据时,对数据的访问不会产生意料之外的后果。实现线程安全性需要从数据的状态变化过程进行分析,并采用相应的线程安全策略。

BXA 2997

Fluent新手必看:如何正确解读scaled residuals曲线(附常见问题排查)

本文详细解析了Fluent中scaled residuals曲线的解读方法及常见问题排查技巧。针对continuity残差居高不下、湍流模型收敛困难等典型问题,提供了网格质量检查、边界条件验证和求解策略调整的实用解决方案,帮助CFD新手快速掌握计算收敛性的关键判断标准。

weixin_28698059的博客 377

别再为Word转PDF表格变形发愁了!Aspose.Words for Java 19.5 保姆级避坑指南

本文详细介绍了使用Aspose.Words for Java 19.5解决Word转PDF时表格变形问题的完整方案。从格式转换原理到实战技巧,涵盖固定表格宽度、禁用自动调整、强制文本换行等核心方法,并提供高级场景应对策略和性能优化建议,帮助开发者高效处理复杂文档转换需求。

weixin_28702105的博客 266

Java并发编程最佳实践

Java并发编程是一个复杂但重要的领域,掌握这些最佳实践可以帮助开发者创建高效、可靠的并发应用程序。选择合适的线程安全策略,如不可变对象、线程封闭和同步机制了解并预防死锁和其他并发问题优化性能和可伸缩性,平衡资源使用和响应时间使用合适的工具和方法排查并发问题应用经典的并发设计模式解决常见问题通过遵循这些实践,开发者可以充分利用多核处理器的能力,同时避免并发编程中的常见陷阱。

weixin_54879811的博客 966

30 中望CAD二次开发(ZRX C++)-windows下的qt开发

本文介绍了中望CAD在windows下,如何使用qt开发插件界面,给出了具体的编码步骤,并生成了模态对话框和非模态对话框,其中非模态对话框可以调用CAD命令,实现交互操作。

cilvsuanna的博客 189

【Java多线程】10 Java 并发编程的最佳实践

通过将对象的状态设为不可更改,可以避免数据竞争和不一致的状态。使用线程池时,合理配置线程池的大小,可以有效提高并发性能。根据任务的性质和系统的资源状况来选择合适的线程池类型(如固定大小、可缓存等)。通过分析 CPU 和内存的使用情况、线程的活跃度等,找出性能瓶颈并进行优化。Java 提供了丰富的并发工具和类,尽量使用这些已有的并发类,而不是自己实现复杂的同步逻辑。长时间持有锁会降低并发性能,尽量将锁的持有时间缩短,避免在持有锁时执行耗时操作。在设计并发系统时,选择合适的锁策略非常重要。

A1924的博客 1276

华为OD技术面真题 - JAVA开发 - 1

汇总华为OD技术面八股JAVA八股,高频技术真题并提供详细解答。

qq_45776114的博客 1200

S/4HANA 迁移前的必修课:用 SCMON 找出沉睡的自定义代码

SAP S/4HANA迁移项目中,SCMON工具能有效解决历史代码评估难题。通过监控ABAP对象调用情况,SCMON可识别40-60%处于"沉睡"状态的自定义代码。配置时需注意监控周期(建议3-6个月)和数据保留期(建议1年),并配合Time Slices功能进行时段分析。工具导出的调用数据可与测试覆盖率对比,帮助决策代码迁移优先级。实际应用中需结合业务场景,建议与SUSG工具配合使用,并保持持续监控。SCMON虽不能完全替代人工判断,但其提供的客观数据能显著提升迁移决策的科学性,建议在

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 290

Java并发编程实践高清PDF及源码下载:深入掌握Java并发编程的不二之选

Java并发编程实践高清PDF及源码下载:深入掌握Java并发编程的不二之选 去发现同类优质开源项目:https://gitcode.com/ 在现代软件开发领域,Java并发编程是提升程序性能、优化资源利用的核心技术之一。《Java并发编程实践》高清PDF及源码下载项目,为Java开发者提供了一本宝贵的实践指南。以下是该项目的主要内容、技术分析、应用场景及特点。 项目介绍 本项目提供了《Java...

gitblog_06749的博客 1041

SAR图像去斑(Despeckling)研究的主流开源数据集

这些数据集专门用于训练监督学习去斑模型,提供了配对的含斑SAR图像和对应的无噪声参考图像(Ground Truth)。这些数据集主要用于目标检测/识别,但包含原始SAR图像,可用于。由于缺乏真实无噪声SAR图像,许多研究采用。

VectorShift的博客 128

Java并发编程实战 Day 14:并发编程最佳实践

今天的内容围绕并发编程最佳实践展开,我们从理论基础出发,分析了线程安全、锁机制、无锁编程等核心概念,并通过实际案例展示了如何在高并发系统中避免常见问题。我们还对比了多种实现方式的性能差异,给出了具体的代码示例和优化建议。如何编写线程安全的并发代码掌握等并发工具的使用理解并发模型的选择与优化策略能够在高并发场景中识别并解决资源争用、死锁等问题具备初步的并发性能调优能力这些技能将直接应用于实际工作中,帮助你在构建高性能、稳定可靠的系统时做出更优的技术决策。

在未来等你的专栏 992

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

《Java并发编程实践》高清PDF及源码下载

《Java并发编程实践》高清PDF及源码下载 去发现同类优质开源项目:https://gitcode.com/ 欢迎来到本资源库!这里为您提供《Java并发编程实践》一书的中文版和英文版高清PDF,以及原书源码。该书详细介绍了JAVA并发编程的相关知识,无论您是偏好中文阅读还是英文阅读,都能在这里找到适合自己的版本。 本书的英文版本编写质量很高,如果您具备良好的英文阅读能力,可以直接阅读英文版本以...

gitblog_06766的博客 285

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

java并发编程实践笔记三

目录 任务和线程 任务处理框架(Executor) 用Executor框架实现 “每任务每线程”和顺序执行这2种调度策略的服务器 执行策略 Executor中的线程池 Executor中任务的生命周期管理 延迟任务与周期任务-ScheduledExecutorService 找出可利用的并行性-HTML渲染器 处理线程的非正常结束 线程泄漏 线程未捕获的异常 关闭JVM...

liao_hb的专栏 939

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

java并发编程实践-带完整书签pdf电子扫描版

2007年6月由电子工业出版社出版发行,是一本经典的Java并发参考手册。java并发编程实践随着多核处理器的普及,使用并发成为构建高性能应用程序的关键。Java5以及6...

田维常 2134

《JAVA并发编程实践》读书笔记(五)

第10章 避免活跃性危险 如果所有的线程以固定的顺序来获得锁,那么在程序中就不会出现锁顺序死锁的问题。10.1 锁顺序死锁 如果存在嵌套的锁获取操作,则很容易发生动态的锁顺序死锁。如果可能出现这种情况,则可以计算存在竞争的两个对象的散列值,按照散列值的大小顺序一致性地加锁。也可以在获得这两个锁之前,使用一种叫做“加时赛”锁的机制去竞争获得锁。如果持有锁的阶段是发生在两个不同的外部方法中,那么将有

黑暗骑士 387

《JAVA并发编程实践JavaConcurrencyinPractice-中文-高清-带书签-完整版(Doug Lea)》

/*免责声明:全部内容都属于是段友分享,我只是属于整理。**/ /*写在前边,个人觉得****弄一个积分下载,就是在自掘坟墓。表面上看起来是可以为个人赚积分,实际砍掉分享交流的一个途径。对我就是没有积分,我就是这么理直气壮。所以每次需要东西的时候,网上一艘,全都是积分下载,虽然有的只是1分,但是不好意思我真的没有,我觉得不想分享别分享啊。到了这里,我把我有的就分享出来,我在博客上专门...

Angus 9494

Go资料汇总

最近用GO在练手,重写了现在公司的一点业务,写到多线程这块越发觉得爽(写的这点东西用到了多线程处理罢了),这里是进来看到的一些文章:基础本人是靠GO Web编程这个入门的,作者写完Beego这个框架就去Apple工作了,你们感受一下。这里也有一系列讲述基础的文章:Golang语言基础进阶稍微进阶一点的可以看看这些博客: AriesDevil’s Blog,Tony Bai (现在以本人的水平,感觉想

Francis Note 1080

通过 final 关键字来实现 双重检查(DCL) 时,为什么 局部变量 是必须的?

在 双重检查锁定(DCL)的wiki 中,对于 DCL提供了一种通过 final 关键字来实现的方式,源码如下

Francis Note 1037
上一篇: 锁的意义--MySQL加锁分析--MVCC实现简要分析
下一篇: Singleton 单例模式
java_4_ever
博客等级 码龄16年 8粉丝 29原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值