Java并发编程实战笔记2.0

《Java并发编程艺术》源码解析与实战 htmltable {th, td {th {pre {简介:《Java并发编程艺术》是Java开发者的宝贵资源,本书配套源码包含了书中所有实例和示例,用于加深对并发编程概念的理解与实践。源码包中包含详细的章节源代码、测试用例以及相关材料。 阅读详情

1.用锁的最佳实践

1.永远只在更新对象的成员变量时加锁    
2.永远只在访问可变的成员变量时加锁    
3.永远不在调用其他对象的方法时加锁

2.信号量模型

信号量模型可以简单概括为:一个计数器,一个等待队列,三个方法。

init():设置计数器的初始值。
down():计数器的值减 1;如果此时计数器的值小于0,则当前线程将被阻塞,否则当前线程可以继续执行。
up():计数器的值加 1;如果此时计数器的值小于或者等于0,则唤醒等待队列中的一个线程,并将其从等待队列中移除。


class Semaphore{
  // 计数器
  int count;
  // 等待队列
  Queue queue;
  // 初始化操作
  Semaphore(int c){
    this.count=c;
  }
  // 
  void down(){
    this.count--;
    if(this.count<0){
      //将当前线程插入等待队列
      //阻塞当前线程
    }
  }
  void up(){
    this.count++;
    if(this.count<=0) {
      //移除等待队列中的某个线程T
      //唤醒线程T
    }
  }
}

3.读写锁

所有的读写锁都遵守以下三条基本原则:
    1.允许多个线程同时读共享变量;
    2.只允许一个线程写共享变量;
    3.如果一个写线程正在执行写操作,此时禁止读线程读共享变量。
    读写锁与互斥锁的一个重要区别就是读写锁允许多个线程同时读共享变量,而互斥锁是不允许的,这是读写锁在读多写少场景下性能优于互斥锁的关键。但读写锁的写操作是互斥的,当一个线程在写共享变量的时候,是不允许其他线程执行写操作和读操作。

读写锁的降级


class CachedData {
  Object data;
  volatile boolean cacheValid;
  final ReadWriteLock rwl =
    new ReentrantReadWriteLock();
  // 读锁  
  final Lock r = rwl.readLock();
  //写锁
  final Lock w = rwl.writeLock();
  
  void processCachedData() {
    // 获取读锁
    r.lock();
    if (!cacheValid) {
      // 释放读锁,因为不允许读锁的升级
      r.unlock();
      // 获取写锁
      w.lock();
      try {
        // 再次检查状态  
        if (!cacheValid) {
          data = ...
          cacheValid = true;
        }
        // 释放写锁前,降级为读锁
        // 降级是可以的
        r.lock(); ①
      } finally {
        // 释放写锁
        w.unlock(); 
      }
    }
    // 此处仍然持有读锁
    try {use(data);} 
    finally {r.unlock();}
  }
}

4.CountDownLatch和CyclicBarrier

CountDownLatch 主要用来解决一个线程等待多个线程的场景;而CyclicBarrier是一组线程之间互相等待。除此之外 CountDownLatch的计数器是不能循环利用的,也就是说一旦计数器减到 0,再有线程调用 await(),该线程会直接通过。但CyclicBarrier的计数器是可以循环利用的,而且具备自动重置的功能,一旦计数器减到 0 会自动重置到你设置的初始值。

5.线程池

1.SingleThreadExecutor的意义

SingleThreadExecutor内部会创建一个Thread,这个Thread的工作就是从一个队列中取出用户提交的任务进行执行,
如果执行过程中发生未受检的异常,singleThreadExecutor会自动重新启动一个线程再继续工作,这一点比自己创建
一个线程自己管理轻松很多,也不需要再去维护一个任务队列。

线程池管理的线程的几点意义:
1、缓存线程、进行池化,可实现线程重复利用、避免重复创建和销毁所带来的性能开销。
2、当线程调度任务出现异常时,会重新创建一个线程替代掉发生异常的线程。
3、任务执行按照规定的调度规则执行。线程池通过队列形式来接收任务。再通过空闲线程来逐一取出进行任务调度。即线程池可以控制任务调度的执行顺序。
4、可制定拒绝策略。即任务队列已满时,后来任务的拒绝处理规则。
以上意义对于singleThreadExecutor来说也是适用的。普通线程和线程池中创建的线程其最大的区别就是有无一个管理者对线程进行管理。

2.1 线程池构造方法中各个参数的意义


/**
     * Creates a new {@code ThreadPoolExecutor} with the given initial parameters and default thread factory.
     *
     * @param corePoolSize the number of threads to keep in the pool, even if they are idle,   
     *          unless {@code allowCoreThreadTimeOut} is set
     *      核心线程数,即使空闲也会保存在线程池中,除非设置allowCoreThreadTimeOut
     * @param maximumPoolSize the maximum number of threads to allow in the pool
     *        线程中允许的最大线程数
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     *      核心线程之外的空闲线程的回收时间
     * @param unit the time unit for the {@code keepAliveTime} argument
     *      上面时间的单位
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     *      任务队列
     * @param threadFactory the factory to use when the executor creates a new thread
     *      创建线程时使用的工厂,Executors提供了Executors.defaultThreadFactory()
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     *      当处理程序由于已达到线程边界和队列容量而阻塞时,handler被使用
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code handler} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, handler);
    }

测试类


package com.lhc.concurrent.executor.param;

import java.util.concurrent.*;

public class Constructor {
    public static void main(String[] args) throws InterruptedException{
        Runnable runnable = new Runnable(){
            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName() + " run ! " + System.currentTimeMillis());
                    Thread.sleep(1000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        };

        ThreadPoolExecutor executor = getPoolBySynchronousQueue();

        executor.execute(runnable);
        executor.execute(runnable);
        executor.execute(runnable);
        executor.execute(runnable);
        executor.execute(runnable);
        executor.execute(runnable);
        executor.execute(runnable);
        executor.execute(runnable);

        Thread.sleep(300);

        System.out.println("first out core:" + executor.getCorePoolSize());
        System.out.println("first out :" + executor.getPoolSize());
        System.out.println("first out queue:" + executor.getQueue().size());

        Thread.sleep(10000);

        System.out.println("second out core:" + executor.getCorePoolSize());
        System.out.println("second out :" + executor.getPoolSize());
        System.out.println("second out queue:" + executor.getQueue().size());
    }

    /**
     * 如果使用LinkedBlockingDeque来构造,,当线程数量大于corePoolSize时,
     * 其余的任务直接放入队列中,maximumPoolSize参数的作用忽略
     * keepAliveTime参数的作用忽略
     * @return
     */
    public static ThreadPoolExecutor getPoolByLinkedBlockingDeque(){
        return new ThreadPoolExecutor(7, 8, 5,
                TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
    }

    /**
     * 如果使用SynchronousQueue来构造,maximumPoolSize参数的作用生效
     * keepAliveTime参数的作用生效
     * 当启动线程大于maximumPoolSize参数时,不会放入队列,会因为无法处理的任务直接抛出异常
     * @return
     */
    public static ThreadPoolExecutor getPoolBySynchronousQueue(){
        return new ThreadPoolExecutor(7, 8, 5,
                TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
    }
}

2.2 关于 corePoolSize + LinkedBlockingDeque.size


/**
 * 启动线程数大于 corePoolSize + LinkedBlockingDeque.size 时,
 * 会启动非核心线程, 当启动数大于 maxSize + + LinkedBlockingDeque.size 时,
 * 多出来的任务不处理,抛出异常
 */
package com.lhc.concurrent.executor.queue;

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyLinked {
    public static void main(String[] args){
        Runnable runnable = new Runnable(){
            @Override
            public void run() {
                try{
                    System.out.println("begin " + System.currentTimeMillis());
                    Thread.sleep(1000);
                    System.out.println("end " + System.currentTimeMillis());
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        };

        LinkedBlockingDeque<Runnable> deque = new LinkedBlockingDeque<>(2);
        System.out.println("deque "+ deque.size());
        ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3, 5,
                TimeUnit.SECONDS, deque);
        executor.execute(runnable);
        executor.execute(runnable);
        executor.execute(runnable);
        executor.execute(runnable);
        executor.execute(runnable);
        System.out.println("deque " + deque.size());
        System.out.println("poolSize " + executor.getPoolSize());
    }
}

    不建议使用Executors的最重要的原因是:Executors提供的很多方法默认使用的都是无界的LinkedBlockingQueue,高负载情境下,无界队列很容易导致OOM,而OOM会导致所有请求都无法处理,这是致命问题。所以强烈建议使用有界队列。
    使用有界队列,当任务过多时,线程池会触发执行拒绝策略,线程池默认的拒绝策略会throw RejectedExecutionException这是个运行时异常,对于运行时异常编译器并不强制 catch它,所以开发人员很容易忽略。因此默认拒绝策略要慎重使用。如果线程池处理的任务非常重要,建议自定义自己的拒绝策略;并且在实际工作中,自定义的拒绝策略往往和降级策略配合使用。
    使用线程池,还要注意异常处理的问题,例如通过ThreadPoolExecutor 对象的 execute()方法提交任务时,如果任务在执行的过程中出现运行时异常,会导致执行任务的线程终止;不过,最致命的是任务虽然异常了,但是你却获取不到任何通知,这会让你误以为任务都执行得很正常。虽然线程池提供了很多用于异常处理的方法,但是最稳妥和简单的方案还是捕获所有异常并按需处理。

6.生产者-消费者模式

    支持分阶段提交以提升性能利用生产者-消费者模式还可以轻松地支持一种分阶段提交的应用场景。写文件如果同步刷盘性能会很慢,所以对于不是很重要的数据,往往采用异步刷盘的方式。某个项目中,其中的日志组件是自己实现的,采用的就是异步刷盘方式,刷盘的时机是:ERROR 级别的日志需要立即刷盘;数据积累到 500 条需要立即刷盘;存在未刷盘数据,且 5 秒钟内未曾刷盘,需要立即刷盘。伪代码如下:



class Logger {
  //任务队列  
  final BlockingQueue<LogMsg> bq = new BlockingQueue<>();
  //flush批量  
  static final int batchSize=500;
  //只需要一个线程写日志
  ExecutorService es = Executors.newFixedThreadPool(1);
  //启动写日志线程
  void start(){
    File file=File.createTempFile("foo", ".log");
    final FileWriter writer = new FileWriter(file);
    this.es.execute(()->{
      try {
        //未刷盘日志数量
        int curIdx = 0;
        long preFT=System.currentTimeMillis();
        while (true) {
          LogMsg log = bq.poll(5, TimeUnit.SECONDS);
          //写日志
          if (log != null) {
            writer.write(log.toString());
            ++curIdx;
          }
          //如果不存在未刷盘数据,则无需刷盘
          if (curIdx <= 0) {
            continue;
          }
          //根据规则刷盘
          if (log!=null && log.level==LEVEL.ERROR ||
              curIdx == batchSize ||
              System.currentTimeMillis()-preFT>5000){
            writer.flush();
            curIdx = 0;
            preFT=System.currentTimeMillis();
          }
        }
      }catch(Exception e){
        e.printStackTrace();
      } finally {
        try {
          writer.flush();
          writer.close();
        }catch(IOException e){
          e.printStackTrace();
        }
      }
    });  
  }
  //写INFO级别日志
  void info(String msg) {
    bq.put(new LogMsg(LEVEL.INFO, msg));
  }
  //写ERROR级别日志
  void error(String msg) {
    bq.put(new LogMsg(LEVEL.ERROR, msg));
  }
}
//日志级别
enum LEVEL {
  INFO, ERROR
}
class LogMsg {
  LEVEL level;
  String msg;
  //省略构造函数实现
  LogMsg(LEVEL lvl, String msg){}
  //省略toString()实现
  String toString(){}
}

7.HiKariCP数据库连接池

关于HiKariCP的两个数据结构,一个是FastList,另一个是ConcurrentBag。

    HiKariCP 中的 FastList 相对于ArrayList的一个优化点就是将 remove(Object element)方法的查找顺序变成了逆序查找。除此之外,FastList还有另一个优化点,是 get(int index)方法没有对index参数进行越界检查,HiKariCP能保证不会越界,所以不用每次都进行越界检查。

    ConcurrentBag中最关键的属性有4个,分别是:用于存储所有的数据库连接的共享队列 sharedList、线程本地存储 threadList、等待数据库连接的线程数 waiters 以及分配数据库连接的工具 handoffQueue。其中,handoffQueue 用的是 Java SDK 提供的 SynchronousQueue,SynchronousQueue 主要用于线程之间传递数据。

泛微E9开发 明细表字段赋值到主表字段,将各类型合计赋值到主表类型合计中 ①绑定明细表类型、预计金额字段的值改变事件,每当有值发生改变,就需要重新计算供应商1、供应商2、供应商3的合计,如果不绑定这两个字段的话,会出现计算不及时或计算错误的问题。②当有明细行被删除时,也需要重新计算供应商1、供应商2、供应商3的合计,比如删除了供应商1的明细行,如果不重新计算,那么供应商1的值还是被删之前的值,合计错误。将各类型合计赋值到主表类型合计中。 阅读详情

相关推荐

VSCode关联VIVADO编辑Verilog

有时候接手别人的代码,或者从网上找的开源代码,每个人风格不一致,对齐缩进也不一样,阅读起来很累。有没有什么方法能快捷地格式化代码呢。下面我们利用VSCode插件来实现这个需求。Vivado是Xilinx公司提供的逻辑开发平台(之一),如果不喜欢Vivado自带的文本编辑器,可以选择第三方的文本编辑器VSCode。

小王在努力...的博客 7150

java 多线程提升

一、多线程基本概念 独占:通过阻止多个并发行为间的有害干扰来维护状态的一致性。通常使用异步方法sychronized 状态依赖:触发,阻止,延迟,恢复,某些行为是由一些对象是否处在这些行为可能成功或者已经 成功的状态决定的。主要通过监视器(monitor)实现 object.wait object.notify, object.notifyAll  客户Client: Client Object...

yinlei555的专栏 259

S7-1200模拟量

将两种不同的金属或合金金属焊接起来,构成一个闭合回路,利用温差电动势来测量温度,当两端存在温度梯度时,回路中就会有电流通过,此时两端之间就存在电动势--热电动势,这就是所谓的塞贝克效应(seebeck effect)。温差越大,热电动势越高。热电偶是有正负极性的,所以需要确保导线连接到正确的极性,否则会造成明显的测量误差。为了使温度补偿达到最佳效果,必须将热电偶模块安装在温度稳定的环境中简述:两种金属,每一种金属随温度变化是不同的,出现温差--由俩金属构成的回路就会有电流经过,因此两端形成热电动势。

王阿妹小天才的博客 2591

常见并发工具类

并发工具类

weixin_52550583的博客 920

01、【solidworks】windosws正在配置solidworks问题及注册表未卸载干净问题解决

windosws正在配置solidworks问题及注册表未卸载干净问题解决

qq_740388770的博客 2万+

从稳压管到LDO芯片:一张图看懂线性电源的‘内卷’进化史

本文追溯了从稳压管到LDO芯片的线性电源技术进化历程,详细解析了各阶段的技术突破与局限。重点探讨了现代LDO在超低功耗、高精度控制等方面的创新,并提供了实战选型的黄金法则,帮助工程师在电子设备小型化与低功耗化趋势中做出最优选择。

weixin_42525551的博客 205

关于synchronized锁和可重入锁

JAVA SDK并发报,最核心的便是对管程的实现。 并发领域的两大问题,互斥和同步: 互斥:同一时刻只允许一个线程访问共享资源,使用Lock解决。 同步:线程之间如何通信,协作?使用Condition解决。 这两个关键字与synchronized的区别? synchronized没办法解决《破坏不可抢占条件》,原因是synchronized申请资源如果申请不到,则线程直接进入阻塞状...

靡不有初,鲜克有终。 1073

Mcgs 屏幕Modbus RTU通讯调试

昆仑通态屏幕作为Modbus Master 与 Modbus Slave通过RS485接口实现通信的调试记录

madao1234的博客 5568

【java并发工具类-互斥】Lock和Condition

并发工具类一.Lock和Condition1.比较synchronized,Lock具有的特点?2.较于synchronized,Lock如何实现可见性?3.ReentrantLock,可重入锁4.公平锁和非公平锁5.如何正确使用锁6.Lock中常见的问题 一.Lock和Condition 1.比较synchronized,Lock具有的特点? Lock接口的三个方法: // 支持中断的API v...

邓新的博客 393

Unity 工具 之 SoftMask软遮罩 实现 UI 边缘渐变过渡的简单使用介绍

目录Unity 工具 之 SoftMask软遮罩 实现 UI 边缘渐变过渡的简单使用介绍一、简单介绍二、Mask 实现的遮罩效果三、Soft Mask 实现遮罩效果四、 Soft Mask 的一些设置五、插件下载Unity 工具类,自己整理的一些游戏开发可能用到的模块,单独独立使用,方便游戏开发。

仙魁XAN 8574

【SCADA】KingSCADA将数据插入SQLServer的方法

1、创建数据库及数据表格;2、配置ODBC数据源。3、通过KingSCADA与数据库建立连接,将数据写入数据库对应表格。

做有价值的事,积累有价值的笔记! 4958

【Java并发编程实战】(十一):Lock和Condition

Dubbo如何用管程实现异步转同步

少侠露飞的学习笔记 729

UI渲染顺序:合批为何被拆碎?

在 Hierarchy 中,避免出现:Image(列表1) → Text(列表1) → 特效 → Image(列表2) → Text(列表2)…若可行,在逻辑上把布局组织成“同类元素集中”:例如:列表行中,Image 集中、Text 集中;或者接受 Text 本身就拆出一批,关键是不要在 Text 中间再插入别的材质。现实中不一定能做到完美的“所有 Image 连在一起”,但你会有一个意识:同一 Canvas 内,如果你把不同材质的元素交错穿插,

本博客聚焦游戏开发技巧、创业实战心得及大学热门课程干货。这里既有技术深度,也有思维广度,无论你是开发者、创业者还是高校学子,都能在这里找到适合自己的成长之路! 85

《Java 并发编程实战》学习笔记一

多核时代每颗 CPU 都有自己的缓存,这时 CPU 缓存(三级缓存)与内存的数据一致性就容易出现问题了,当多个线程在不同的 CPU 上执行时,这些线程操作的是不同的 CPU 缓存。比如,线程 A 操作的是 CPU-1 上的缓存,而线程 B 操作的是 CPU-2 上的缓存,很明显,这个时候线程 A 对变量 V 的操作对于线程 B 而言就不具备可见性了。:高级语言里一条语句往往需要多条 CPU 指令完成,例如count += 1这句代码,至少需要三条 CPU 指令。

yibushao的博客 1152

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Java并发编程实战笔记

Java并发编程实战笔记

daweias的博客 443

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

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

【极客时间】《Java并发编程实战》学习笔记

【极客时间】《Java并发编程实战》学习笔记

Mr.Cui的Java学习之路 2649

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Java并发编程实战(2)

该系列文章是博主学习笔记,禁止转载,读书交流群:946541246 文章目录Java并发编程实战三四章学习加锁与可见性volatile Java并发编程实战三四章学习 加锁与可见性 锁可以用于确保某个线程以一种可预测的方式来查看另一个线程的执行结果 当线程A执行某个同步代码时,线程B随后进入由同一个锁保护的同步代码块,在这种情况下可以保证,在锁被释放钱,A看到的变量值在B获得锁后同样可以由B看到。...

谢耀眼 1008

Java并发编程实战读书笔记

Java并发编程实战读书笔记并发编程的挑战上下文切换如何减少上下文切换如何避免死锁Java并发机制的底层实现原理volatile的使用violate如何保证可见性?lock前缀的作用:synchronized的实现原理与应用 本文记录了阅读该书中本人认为比较有收获,比较重要的知识点。其中有一些内容加了一些自己的理解。 并发编程的挑战 上下文切换 上下文切换发生在同一个cpu中,cpu通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切

qq_39863093的博客 1025

《JAVA并发编程的艺术》之 Java并发编程实战

《JAVA并发编程的艺术》之 Java并发编程实战 文章目录《JAVA并发编程的艺术》之 Java并发编程实战生产者和消费者模式生产者消费者模式实战多生产者和多消费者场景线程池与生产消费者模式线上问题定位性能测试查看网络流量。查看系统平均负载。查看系统内存情况。查看CPU的利用率。异步任务池 当你在进行并发编程时,看着程序的执行速度在自己的优化下运行得越来越快,你会觉得越来越有成就感,这就是并发编程的魅力。但与此同时,并发编程产生的问题和风险可能也 会随之而来。本章先介绍几个并发编程的实战案例,然后再介绍如

weixin_44019182的博客 1319

Java并发编程:深入探索与实战案例

Java并发编程是一个复杂而强大的领域,它提供了多种工具和技术来应对多线程环境下的挑战。从基础的到高级的 ` LockAtomic `类,再到线程池和并发集合,Java为开发者提供了丰富的选择。通过理解这些工具的工作原理,并结合实际场景进行应用,我们可以构建出高性能、可扩展的并发应用程序。本文介绍的实战案例,展示了如何将这些知识应用于构建一个简单但高效的Web服务器,希望对读者有所启发。

运维人生 1462

Java 并发编程实战

1. 可见性、原子性和有序性问题:并发编程 Bug 的源头 在并发编程中,常常会遇到由于线程之间共享数据而导致的问题,主要体现在可见性、原子性和有序性上。让我们通过一些代码示例来深入理解这些问题。 可见性问题 public class VisibilityTest { private static boolean flag = false; public static void main(String[] args) throws InterruptedException {

wolf23151269的博客 1664

Java并发编程实战.pdf资源介绍:Java并发编程领域的权威指南

Java并发编程实战.pdf资源介绍:Java并发编程领域的权威指南 【下载地址】Java并发编程实战.pdf资源介绍 《Java并发编程实战》是Java并发编程领域的经典著作,全面涵盖了线程管理、并发集合、同步器、锁、线程安全等核心内容。本书结构清晰,实例丰富,适合Java初学者和经验丰富的开发者。通过深入学习,你将掌...

gitblog_06715的博客 1188

Java并发编程实战基础概要

Java线程之间的通信由Java内存模型(JMM)控制。JMM定义了线程和主内存之间的抽象关系在内存里,Java内存模型规定了所有的变量都存储在主内存(物理内存)中,每条线程还有自己的工作内存,线程对变量的所有操作都必须在工作内存中进行。不同的线程无法访问其他线程的工作内存里的内容。我们可以使用下图来表示在逻辑上线程、主内存、工作内存的三者交互关系。额外的延伸知识,从JVM的角度来看java运行时的内存区域,来对应我们Java内存模型前面我们都理解了缓存导致了可见性问题,编译优化导致了有序性问题。.....

C. 785

Java并发编程实战极客时间

我整理的一些关于【Java】的项目学习资料(附讲解~~)和大家一起分享、学习一下:https://d.51cto.com/bLN8S1Java并发编程实战指南 在当今的软件开发中,掌握并发编程是一项必不可少的技能。为了帮助你实现“Java并发编程实战”的目标,我将为你提供一个详细的步骤指南。本文将从基本概念开始,引导你逐...

weixin_35178718的博客 305

深入解析 Java 并发编程:从基础到高阶实战

本篇文章将从 基础到高阶 解析 Java 并发编程,涵盖: 线程基础与 Thread 类 ExecutorService 线程池 Future 与 CompletableFuture ReentrantLock 与 synchronized 的对比 Fork/Join 框架 并发容器与 CopyOnWriteArrayList 实战:高并发任务处理优化

一名热衷于技术的全栈开发者,专注于前端与后端的全面技术探索。在这里,我将分享我在技术领域的学习与成长,助力更多开发者的进步。 1169

[Java进阶] 并发编程实战—同步

本文主要对Java中常用的一些同步工具做了详细的讲解,包括synchronized关键字(隐式锁)、显示锁(Locks)、高级的同步工具(如CountDownLatch、CyclicBarrier、Semaphore),并通过代码案例来讲解了各种同步工具的使用以及工作原理。

MCC_MCC_MCC的博客 1114
上一篇: Java并发编程实战笔记1.0
下一篇: 面试-1.HashMap问题
过河的小卒子
博客等级 码龄10年 39粉丝 112原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值