java并发编程学习总结(超赞!!!)

先睹为快!Github上一些超火的Java并发编程神仙笔记 并发编程 谈到并发编程,可能很多人都有过经验,甚至比我了解的更多。 那么并发与并行的区别又是什么? 并发编程是编程中的核心问题,实践中,当人们希望利用计算机处理一些现实世界问题,以及希望同时处理多个问题的时候,并发也就出现了。在需要对同一共享资源进行操作,多个事务之间存在关联,人们把这种看起来多个事务同时运行的特性称为并发性。有个经常容易跟并发混肴的概念叫并行,其实真正懂了之后理解并不困难,可以把并行看过是并发实现中某个局部可以引入的运行设施;在讨论并发时,我们一般不需要假设这个运行或者调度设施究竟是 阅读详情

我是技术搬运工,好东西当然要和大家分享啦原文地址

使用线程

有三种使用线程的方法:

  1. 实现 Runnable 接口;
  2. 实现 Callable 接口;
  3. 继承 Tread 类;

实现 Runnable 和 Callable 接口的类只能当做一个可以在线程中运行的任务,不是真正意义上的线程,因此最后还需要通过 Thread 来调用。可以说任务是通过线程驱动从而执行的。

1. 实现 Runnable 接口

需要实现 run() 方法

通过 Thread 调用 start() 方法来启动线程

public class MyRunnable implements Runnable {
    public void run() {
        // ...
    }
    public static void main(String[] args) {
        MyRunnable instance = new MyRunnable();
        Tread thread = new Thread(instance);
        thread.start();
    }
}

2. 实现 Callable 接口

与 Runnable 相比,Callable 可以有返回值,返回值通过 FutureTask 进行封装。

public  class  MyCallable  implements  Callable<Integer> {
    public Integer call() {
        // ...
    }
    public  static  void  main(String[]  args) {
        MyCallable mc = new MyCallable();
        FutureTask<Integer> ft = new FutureTask<>(mc);
        Thread thread = new Thread(ft);
        thread.start();
        System.out.println(ft.get());
    }
}

3. 继承 Tread 类

同样也是需要实现 run() 方法,并且最后也是调用 start() 方法来启动线程。

class MyThread extends Thread {
    public void run() {
        // ...
    }
    public  static  void  main(String[]  args) {
        MyThread mt = new MyThread();
        mt.start();
    }
}

4. 实现接口 vs 继承 Thread

实现接口会更好一些,因为:

  1. Java 不支持多重继承,因此继承了 Thread 类就无法继承其它类,但是可以实现多个接口。
  2. 类可能只要求可执行即可,继承整个 Thread 类开销会过大。

Executor

Executor 管理多个异步任务的执行,而无需程序员显示地管理线程的生命周期。

主要有三种 Excutor:

  1. CachedTreadPool:一个任务创建一个线程;
  2. FixedThreadPool:所有任务只能使用固定大小的线程;
  3. SingleThreadExecutor:相当于大小为 1 的 FixedThreadPool。
ExecutorService exec = Executors.newCachedThreadPool();
for(int i = 0; i < 5; i++) {
    exec.execute(new MyRunnable());
}

基础线程机制

1. sleep()

Thread.sleep(millisec) 方法会休眠当前正在执行的线程,millisec 单位为毫秒。也可以使用 TimeUnit.TILLISECONDS.sleep(millisec)。

sleep() 可能会抛出 InterruptedException。因为异常不能跨线程传播回 main() 中,因此必须在本地进行处理。线程中抛出的其它异常也同样需要在本地进行处理。

public void run() {
    try {
        // ...
        Thread.sleep(1000);
        // ...
    } catch(InterruptedException e) {
        System.err.println(e);
    }
}

2. yield()

对静态方法 Thread.yield() 的调用声明了当前线程已经完成了生命周期中最重要的部分,可以切换给其它线程来执行。

public void run() {
    // ...
    Thread.yield();
}

3. join()

在线程中调用另一个线程的 join() 方法,会将当前线程挂起,直到目标线程结束。

可以加一个超时参数。

4. deamon

后台线程(deamon)是程序运行时在后台提供服务的线程,并不属于程序中不可或缺的部分。

当所有非后台线程结束时,程序也就终止,同时会杀死所有后台线程。

main() 属于非后台线程。

使用 setDaemon() 方法将一个线程设置为后台线程。

线程之间的协作

  • 线程通信:保证线程以一定的顺序执行;
  • 线程同步:保证线程对临界资源的互斥访问。

线程通信往往是基于线程同步的基础上完成的,因此很多线程通信问题也是线程同步问题。

1. 线程通信

wait()、notify() 和 notifyAll() 三者实现了线程之间的通信。

wait() 会在等待时将线程挂起,而不是忙等待,并且只有在 notify() 或者 notifyAll() 到达时才唤醒。

sleep() 和 yield() 并没有释放锁,但是 wait() 会释放锁。实际上,只有在同步控制方法或同步控制块里才能调用 wait() 、notify() 和 notifyAll()。

这几个方法属于基类的一部分,而不属于 Thread。

private boolean flag = false;

public synchronized void after() {
    while(flag == false) {
        wait();
        // ...
    }
}

public synchronized void before() {
    flag = true;
    notifyAll();
}

wait() 和 sleep() 的区别

  1. wait() 是 Object 类的方法,而 sleep() 是 Thread 的静态方法;
  2. wait() 会放弃锁,而 sleep() 不会。

2. 线程同步

给定一个进程内的所有线程,都共享同一存储空间,这样有好处又有坏处。这些线程就可以共享数据,非常有用。不过,在两个线程同时修改某一资源时,这也会造成一些问题。Java 提供了同步机制,以控制对共享资源的互斥访问。

2.1 synchronized

同步一个方法

使多个线程不能同时访问该方法。

public synchronized void func(String name) {
    // ...
}

同步一个代码块

public void func(String name) {
    synchronized(this) {
        // ...
    }
}

2.2 Lock

若要实现更细粒度的控制,我们可以使用锁(lock)。

private Lock lock;
public int func(int value) {
    lock.lock();
    // ...
    lock.unlock();
}

2.3 BlockingQueue

java.util.concurrent.BlockingQueue 接口有以下阻塞队列的实现:

  • FIFO 队列:LinkedBlockingQueue、ArrayListBlockingQueue(固定长度)
  • 优先级队列:PriorityBlockingQueue

提供了阻塞的 take() 和 put() 方法:如果队列为空 take() 将一直阻塞到队列中有内容,如果队列为满 put() 将阻塞到队列有空闲位置。它们响应中断,当收到中断请求的时候会抛出 InterruptedException,从而提前结束阻塞状态。

使用 BlockingQueue 实现生产者消费者问题

// 生产者
import java.util.concurrent.BlockingQueue;

public class Producer implements Runnable {
    private BlockingQueue<String> queue;

    public Producer(BlockingQueue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is making product...");
        String product = "made by " + Thread.currentThread().getName();
        try {
            queue.put(product);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
// 消费者
import java.util.concurrent.BlockingQueue;

public class Consumer implements Runnable{
    private BlockingQueue<String> queue;

    public Consumer(BlockingQueue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            String  product = queue.take();
            System.out.println(Thread.currentThread().getName() + " is consuming product " + product + "...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
// 客户端
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class Client {
    public static void main(String[] args) {
        BlockingQueue<String> queue = new LinkedBlockingQueue<>(5);
        for (int i = 0; i < 2; i++) {
            new Thread(new Consumer(queue), "Producer" + i).start();
        }
        for (int i = 0; i < 5; i++) {
            // 只有两个 Product,因此只能消费两个,其它三个消费者被阻塞
            new Thread(new Producer(queue), "Consumer" + i).start();
        }
        for (int i = 2; i < 5; i++) {
            new Thread(new Consumer(queue), "Producer" + i).start();
        }
    }
}
// 运行结果
Consumer0 is making product...
Producer0 is consuming product made by Consumer0...
Consumer1 is making product...
Producer1 is consuming product made by Consumer1...
Consumer2 is making product...
Consumer3 is making product...
Consumer4 is making product...
Producer2 is consuming product made by Consumer2...
Producer3 is consuming product made by Consumer3...
Producer4 is consuming product made by Consumer4...

线程状态

JDK 从 1.5 开始在 Thread 类中增添了 State 枚举,包含以下六种状态:

  1. NEW(新建)
  2. RUNNABLE(当线程正在运行或者已经就绪正等待 CPU 时间片)
  3. BLOCKED(阻塞,线程在等待获取对象同步锁)
  4. Waiting(调用不带超时的 wait() 或 join())
  5. TIMED_WAITING(调用 sleep()、带超时的 wait() 或者 join())
  6. TERMINATED(死亡)



结束线程

1. 阻塞

一个线程进入阻塞状态可能有以下原因:

  1. 调用 Thread.sleep() 方法进入休眠状态;
  2. 通过 wait() 使线程挂起,直到线程得到 notify() 或 notifyAll() 消息(或者 java.util.concurrent 类库中等价的 signal() 或 signalAll() 消息;
  3. 等待某个 I/O 的完成;
  4. 试图在某个对象上调用其同步控制方法,但是对象锁不可用,因为另一个线程已经获得了这个锁。

2. 中断

使用中断机制即可终止阻塞的线程。

使用 interrupt() 方法来中断某个线程,它会设置线程的中断状态。Object.wait(), Thread.join() 和 Thread.sleep() 三种方法在收到中断请求的时候会清除中断状态,并抛出 InterruptedException。

应当捕获这个 InterruptedException 异常,从而做一些清理资源的操作。

不可中断的阻塞

不能中断 I/O 阻塞和 synchronized 锁阻塞。

Executor 的中断操作

Executor 避免对 Thread 对象的直接操作,但是使用 interrupt() 方法必须持有 Thread 对象。Executor 使用 shutdownNow() 方法来中断所有它里面的所有线程,shutdownNow() 方法会发送 interrupt() 调用给所有线程。

如果只想中断一个线程,那么使用 Executor 的 submit() 而不是 executor() 来启动线程,就可以持有线程的上下文。submit() 将返回一个泛型 Futrue,可以在它之上调用 cancel(),如果将 true 传递给 cancel(),那么它将会发送 interrupt() 调用给特定的线程。

检查中断

通过中断的方法来终止线程,需要线程进入阻塞状态才能终止。如果编写的 run() 方法循环条件为 true,但是该线程不发生阻塞,那么线程就永远无法终止。

interrupt() 方法会设置中断状态,可以通过 interrupted() 方法来检查中断状,从而判断一个线程是否已经被中断。

interrupted() 方法在检查完中断状态之后会清除中断状态,这样做是为了确保一次中断操作只会产生一次影响。

原子性

对于除 long 和 double 之外的基本类型变量的读写,可以看成是具有原子性的,以不可分割的步骤操作内存。

JVM 将 64 位变量(long 和 double)的读写当做两个分离的 32 位操作来执行,在两个操作之间可能会发生上下文切换,因此不具有原子性。可以使用 volatile 关键字来定义 long 和 double 变量,从而获得原子性。

AtomicInteger、AtomicLong、AtomicReference 等特殊的原子性变量类提供了下面形式的原子性条件更新语句,使得比较和更新这两个操作能够不可分割地执行。

boolean compareAndSet(expectedValue, updateValue);

AtomicInteger 使用举例:

private AtomicInteger ai = new AtomicInteger(0);

public int next() {
    return ai.addAndGet(2)
}

原子性具有很多复杂问题,应当尽量使用同步而不是原子性。

volatile

保证了内存可见性和禁止指令重排,没法保证原子性。

1. 内存可见性

普通共享变量被修改之后,什么时候被写入主存是不确定的。

volatile 关键字会保证每次修改共享变量之后该值会立即更新到内存中,并且在读取时会从内存中读取值。

synchronized 和 Lock 也能够保证内存可见性。它们能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。不过只有对共享变量的 set() 和 get() 方法都加上 synchronized 才能保证可见性,如果只有 set() 方法加了 synchronized,那么 get() 方法并不能保证会从内存中读取最新的数据。

2. 禁止指令重排

在 Java 内存模型中,允许编译器和处理器对指令进行重排序,重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。

volatile 关键字通过添加内存屏障的方式来进制指令重排,即重排序时不能把后面的指令放到内存屏障之前。

可以通过 synchronized 和 Lock 来保证有序性,它们保证每个时刻只有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。

多线程开发良好的实践

  • 给线程命名;
  • 最小化同步范围;
  • 优先使用 volatile;
  • 尽可能使用更高层次的并发工具而非 wait 和 notify() 来实现线程通信,如 BlockingQueue, Semeaphore;
  • 多用并发容器,少用同步容器,并发容器壁同步容器的可扩展性更好。
  • 考虑使用线程池
  • 最低限度的使用同步和锁,缩小临界区。因此相对于同步方法,同步块会更好。

未完待续

参考资料

Java并发编程 Java并发编程是Java语言中一个重要且复杂的领域,涉及多线程、线程安全、同步机制、并发工具类等多个方面。 阅读详情

相关推荐

学习《Java并发编程》目录索引 (持续更新中)

     写在前面:此系列为本人学习黑马《Java并发编程》所整理的笔记 ! 不局限于应用层开发, 更加深入理解多线程, 并发编程, 更有利于我们长久发展 ! 学习Java并发编程

Guizy 1万+

Java并发(总结自用)

Java并发(面试总结自用)

Zyx 1223

”COMSOL“竟然如此简单!我悟了!!

COMSOL Multiphysics具有强大的多物理场全耦合仿真分析功能、高效的计算性能,可以保证数值仿真的高度精确,已被广泛应用于各个学科领域,近年来运用COMSOL来解决电化学实际工程问题也越来越普遍。

C1012562281的博客 3447

Java并发编程之如何通过10000个请求调用API接口

Java并发编程是当今互联网开发中非常重要的一部分。在现代的互联网应用程序中,我们经常需要处理大量的请求,而Java并发编程可以帮助我们高效地处理这些请求。在本教程中,我们将学习如何使用Java并发编程来同时处理10,000个请求调用API接口。

码奴一只猿 2292

全面解读 SAP Business Data Cloud

SAP 在 2025 年初正式推出(简称BDC),它把、以及全新的统一封装为一项完全托管的 SaaS 服务,配套交付“语义层 + 数据产品 + 智能应用”三层能力,为企业构建真正贴近业务语境的 “Business Data Fabric”。这一方案不仅解决了跨系统数据碎片化、治理难及 AI 训练数据可信度低的问题,还为现有 BW 客户、S/4HANA以及第三方工作负载提供云上现代化路径和零拷贝数据共享机制。本文将围绕技术架构、核心组件、业务场景、迁移路线及未来展望展开,以 Henkel 等真实案例佐证。

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

STM32入门教程:智能马桶盖控制

通过编写相应的代码,您可以根据需要实现其他功能,例如座圈温度调节和液晶显示屏显示等。初始化配置 首先,使用STM32CubeMX创建一个新的工程,并选择您所使用的STM32单片机型号。智能马桶盖是一种集成了多种功能的智能家居设备,通过控制芯片来实现自动冲水、座圈加热、座圈温度调节等功能。在本教程中,我们将使用STM32系列单片机来控制智能马桶盖的各个功能。以上代码演示了如何通过STM32的GPIO控制引脚来控制智能马桶盖的冲水和座圈加热功能。写代码 接下来,我们将通过编写代码来控制智能马桶盖的各个功能。

2401_85258012的博客 657

JAVA 多线程并发

运行(running)的线程执行 Thread.sleep(long ms)或 t.join()方法,或者发出了 I/O 请求时,运行(running)的线程执行 o.wait()方法,JVM 会把该线程放入等待队列(waitting queue)thread.stop()调用之后,创建子线程的线程就会抛出 ThreadDeatherror 的错误,并且会释放子。而 wait()方法,则是属于。2. 通过调用 Thread 类的 start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运。

weixin_44099583的博客 1621

〔重庆理工大学〕大学物理实验--变温霍耳效应及其参数测定

每个老师要求不同,实验报告内容和数据仅供参考。

Pretty Boy Fox的博客 3043

【java】Java并发编程系列-基础知识(非常详细哦)

今天讲解了Java并发编程的3个特性,然后基于⾥⾯的两个特性“可⻅性”和“有序性”引出⼏个重要的概念,分别为“内 存模型JMM”、“重排序”和“内存屏障”,这个对后续理解volatile、synchronized、final,以及避免使⽤的各种坑, 真的是⾮常⾮常重要,所以如果想在Java上有所成就,并发的基础知识一定要牢固掌握哦 ,本系列会一步一步深入!!!

DreamSun的博客 1173

倍福控制第三方伺服走CSV模式--以汇川伺服为例

在使用TwinCAT软件控制标准CiA402协议的EtherCAT伺服时使用TwinCAT软件NC自带的运动控制指令控制伺服,此时伺服驱动器一般工作在CSP/CSV/CST模式。最常见伺服驱动器工作在CSP模式,轨迹规划运行在TwinCAT软件,位置环速度环电流环运行在伺服驱动器中。该模式下速度波动较大,需要极度平稳的速度需要采用CSV模式。本文讲述的是伺服驱动器工作在CSV模式,此时轨迹规划和位置环均运行在TwinCAT软件,速度环电流环运行在伺服驱动器中。为了简化文字描述,本文采用图文结合的方式介绍伺服

weixin_41883890的博客 4847

3.3 数据导入与导出方法

Simulink 提供了多种数据导入与导出方法,能够满足不同场景下的需求。无论是从 MATLAB 工作区或外部文件导入数据,还是将仿真结果导出到工作区或文件,这些功能都极大地增强了模型的灵活性和实用性。掌握这些方法后,用户可以更高效地完成复杂系统的建模与分析任务。

xiaoheshang_123的博客 318

java并发总结

一、并发基础 ㅤ 1、进程与线程 ㅤ 进程 程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至 CPU,数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理 IO 的 当一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一个进程。 进程就可以视为程序的一个实例。大部分程序可以同时运行多个实例进程(例如记事本、画图、浏览器等),也有的程序只能启动一个实例进程(例如网易云音乐、360 安全卫士等) 线程 一个进程之内可以分为一到多个

liujiqing123的博客 1万+

图像处理之边缘检测[微分算子、Canny算子和LOG算子]

LOG算子是在经典算子的基础上发展起来的边缘检测算子,根据信噪比求得检测边缘的最优滤波器。边缘总是以强度突变的形式出现,可以定义为图像局部特性的不连续性,如灰度的突变和纹理结构的突变等。图像的边缘包含了物体形状的重要信息,它不仅在分析图像时大幅度地减少了要处理的信息量,而且还保护了目标的边界结构。对于复杂的图像,采用Roberts算子不能较好地得到图像的边缘,而需要采用更加复杂的3X3的算子比如Prewitt算子。对于离散的图像f(x,y),边缘检测算子是用图像的垂直和水平差分来逼近梯度算子,即。

qq_44111805的博客 9833

如何学习Java并发编程

2012年我刚转行到互联网,那年夏天我从传统通信行业跳槽到互联网行业。那是我成年后看的第一部国产动画片,可谓惊为天人。里面有所谓的八奇技,其中有一门绝技称为:术之尽头,炁体源流。虽然目前作者还没有揭开谜底,但从定义上来说,炁体源流大概有“追本溯源,回归本质”的含义。为什么扯这些呢?很多人学不好Java并发编程,恰恰是因为没有抓住本质。

smart_an的专栏 1485

【MATLAB】STM32电机调速闭环控制工程

本文提出了一种基于MATLAB仿真与STM32实现的直流电机闭环PID调速控制方案。通过MATLAB建立电机数学模型并进行参数整定,仿真验证了增量式PID算法在启停、稳态和抗扰动方面的优越性能。基于STM32F103搭建了硬件系统,实现了编码器测速、定时中断控制、PWM输出等功能。实测表明,该系统转速超调<2%、稳态误差±2r/min,具备快速响应和强抗扰能力。相比开环控制,闭环调速显著提升了动态性能和稳态精度。方案提供了完整的仿真代码和嵌入式工程源码,可直接应用于智能小车、工业传动等场景,具有较高的

unber的博客 50

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

JUC并发编程 深入学习Java并发编程【上】

start() 启动一个新线程,在新的线程运行run方法中的代码。start方法只能让线程进入就绪,代码不一定立即执行(只有等CPU的时间片分配给它才能运行)。每个线程对象的start方法只能调用一次。join()等待线程运行结束。假如当前的主线程正在等待某个线程执行结束后返回的结果,就可以调用这个join方法。join(long n)表示最多等待n毫秒。

RuanFun的博客 3535

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

并发编程之学习总结

前言楼主自1月14号就停更了,这段时间一直在看JVM,因此没有更新任何文章,但楼主觉得有必要总结一下之前学习并发的过程,因为这一次的总结其实就是下一次的开始。主要时总结一下并发的学习路线,因为在楼主的工作环境中,并不是每天都在编写高并发的程序。然而,即使是这样,我们也要知道并发的每个知识点,这样才算一个合格的Java 程序员。那么如何系统的学习并发呢?学习路线是怎么样的呢?1. 学会开始就成功了一半

ignore 2018

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

如和学习java并发编程

学习java并发必读

专注游戏服务器相关技术 1404

【免费下载】 探索Java并发编程的奥秘:《JAVA并发编程艺术》高清PDF下载

探索Java并发编程的奥秘:《JAVA并发编程艺术》高清PDF下载 【下载地址】JAVA并发编程艺术高清PDF下载 本仓库提供了一份名为《JAVA并发编程艺术》的高清PDF文件下载。该资源详细介绍了Java并发编程的各个方面,涵盖了从基础概念到高级技术的全面内容 ...

gitblog_06678的博客 1169

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

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

Mr.Cui的Java学习之路 2649

java点赞_java实现点赞功能示例

最近做了一个评论的点赞功能,感觉有必要记录一下。思路:点赞功能,看起来挺简单,但是做的高效稳定还是需要一些处理。归纳思路如下:1.点赞接口要利用redis做点赞次数限制,比如一分钟之内最多点赞或取消点赞四次2.点赞是很高频随兴的操作,最好不要直接操作数据库,先把点赞信息放入redis缓存,然后跑定时任务每15秒去同步到数据库,同步完之后把同步好的信息批量从redis删除。3.把点赞信息放入redi...

weixin_36315943的博客 2954

java点赞功能

赞   //zancount是我数据库的赞数 ${i.zanCount==0?"":i.zanCount}  function zan2(luntanid,zan) { //当前赞的人 var loginid=$(".loginid").val(); $.ajax({ url:"luntan/zan",//跳转界面 data:{ luntanid:lu

杏杏杏妹子的博客 7254

java 点赞_Java开发利用Redis是如何实现点赞、取消点赞的?

本文基于 SpringCloud, 用户发起点赞、取消点赞后先存入 Redis 中,再每隔两小时从 Redis 读取点赞数据写入数据库中做持久化存储。点赞功能在很多系统中都有,但别看功能小,想要做好需要考虑的东西还挺多的。点赞、取消点赞是高频次的操作,若每次都读写数据库,大量的操作会影响数据库性能,所以需要做缓存。至于多久从 Redis 取一次数据存到数据库中,根据项目的实际情况定吧,我是暂时设了...

weixin_42109925的博客 1829

【进阶】Java并发编程超详讲解!!!

java内存模型的缓存区,导致了不可见性编译器优化导致了乱序性线程的切换执行导致了指令执行的非原子性。

2301_78168249的博客 1292

如何深入学习Java并发编程?

在讲解深入学习Java并发编程的方法之前,先分析如下若干错误的观点和学习方法。 错误观点1:学习Java编程主要是学习多线程。 这话其实是说明了表面现象,多线程其实还真是并发编程的实现方式,但在实际高并发的项目里,程序员一般不会通过多线程去实现并发的需求,而是通过使用一些后文会提到的高并发组件来实现高并发的需求。 甚至可以这样说,线程方面的技能对实现高并发需求的帮助很少,与其用很多精力去学多线程并发,还不如花精力去学组件乃至应对高并发的集群。 错误观点2:为了学好并发编程,要深入了解相关算法和底层实

hsm_computer的博客 962

简明高效的 Java 并发编程学习指南

你好,我是宝令,《Java 并发编程实战》专栏作者,很高兴你能看到这篇内容。\n对于一个Java程序员而言,能否熟练掌握并发编程是判断他优秀与否的重要标准之一。因为并发编程是Java语言中最为晦涩的知识点,它涉及操作系统、内存、CPU、编程语言等多方面的基础能力,更为考验一个程序员的内功。\n尤其是大数据时代的来临,高并发更成为了家常便饭,工作中,你总是绕不开并发编程的任务,比如说,你想写个程序,...

啊啊啊啊2 523
上一篇: 目前深圳的IT公司java方向都在用那些技术(转)
下一篇: top K问题的解法
普通网友
博客等级 码龄9年 489粉丝 29原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值