Redis 分布式锁的实现

1. Redis 分布式锁说明

Java 实现 Redis 分布式锁案例已经上传到笔者的GitHub,欢迎下载参考,如有错误希望大佬指正。

在大多数情况下,应该都是使用成熟的分布式锁框架,如 Redisson。这里只是根据 Redisson 部分源码思想进行的个人摸索,编写了一个利用Redis实现的分布式可重入锁,包含看门狗对锁进行续期。

1.1 什么是 Redis 分布式锁

  • 在 Java 中提供了 synchronized 和 Lock 锁,来保证多线程程序中的线程安全问题。
  • 分布式锁指的是,在分布式系统,不同的进程中,访问共享资源的一张锁的实现。

如果不同的系统或同一个系统的不同主机之间共享了某个临界资源,往往需要互斥来防止彼此干扰,以保证一致性。

阅读更多

SpringBoot项目连接多源MySQL数据库

说明(主要是实现的思路)

对于使用 SpringBoot 中使用 MySQL 的项目来说,可能会面临一个 SpringBoot 项目连接多个不同的 MySQL 数据库。这里针对普通的 SpringBoot 项目进行实现。关注点在查询前,数据库选择上进行定义。Demo 项目代码已经放到了我的 GitHub,是 SpringBoot + MyBatis 的测试项目。通过注解,选择不同的数据库。可能大家在参考的时候会出现各种奇奇怪怪的问题(细心能解决大部分问题哈)

阅读更多

Java并发编程实战:第16章 总结

  • Java 存储模型明确地规定了在什么时机下,操作存储器的线程的动作可以保证被另外的动作看到。
  • 规范还规定了要保证操作是按照一种偏序关系进行排序。这种关系称为 happens-before,它是规定在独立存储器和同步操作的级别之上的。
阅读更多

Java并发编程实战:第16章 存储模型

1. 什么是存储模型,要它何用

1
value = 100

在多线程环境中,要获取到 value 的正确值,需要用到同步。

  • 编译器生成指令的次序,可以不同于源代码的所暗示的”显然”版本,而且编译器还会把变量存储在寄存器,而不是内存中;
阅读更多

Java并发编程实战:第15章 总结

  • 比较并交换(compare-and-swap):当多个线程试图使用比较并交换(CAS),同时更新相同的变量时,其中一个线程会胜出,并且更新变量的值,而其它的线程都会失败。失败的线程允许尝试再次比较并交换操作。一个线程在竞争 CAS 时失败,不会被阻塞,它可以决定是否重试,这样的灵活性大大减少了锁相关的活跃度风险。
  • 原子变量类,提供了广义的 volatile 变量,以支持原子的、条件的读-写-改操作。原子变量是“更佳的volatile”
阅读更多

Java并发编程实战:第15章 原子变量与非阻塞同步机制

1. 锁的劣势

volatile 变量

  • volatile 变量与锁相比是更轻量的同步机制,因为它们不会引起上下文的切换和线程调度。
  • 然而,volatile 变量与锁相比有一些局限性:尽管它们提供了相似的可见性保证,但是它们不能用于构建原子化的复合操作。
  • 当一个变量依赖其他变量时,或者当变量的新值依赖于旧值时,是不能用 volatile 变量的。这些都限制了 volatile 变量的使用,因此它们不能用于实现可靠的通用工具,比如计数器,或互斥体(mutex)
阅读更多

Java并发编程实战:第14章 总结

  1. 使用 Object 超类中的 wait()、notify()、notifyAll()等方法管理条件队列
  2. 如果说 Lock 是显式的 Synchroinzed,那么也可以认为 Condition 也是一种显式的条件队列 —- Condition 提供了类似 wait()、notify()、notifyAll() 且更全面的方法
阅读更多

Java并发编程实战:第14章 构建自定义的同步工具

1. 管理状态依赖性

1.1 管理状态依赖性

  • 状态依赖性指某种操作必须依赖于指定的状态才可以执行。比如一个阻塞队列的take方法依赖于这个阻塞队列中有至少一个元素这个状态。
  • 如果一个状态依赖性操作所依赖的状态不满足,通常有几种处理办法:
    1. 抛出异常
    2. 使用某种约定的错误返回值
阅读更多

Java并发编程实战:第13章 总结

  • 显式的 Lock 与内部锁 synchronized 相比提供了一些扩展的特性,包括处理不可用的锁时更好的灵活性,以及对队列行为更好的控制。
  • ReentrantLock 与 synchronized各有优缺点。
阅读更多

Java并发编程实战:第13章 显式锁

1. Lock 和 ReentrantLock

Lock 接口:

1
2
3
4
5
6
7
8
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}

Lock 提供了无条件的、可轮询的、定时的、可中断的锁获取操作,所有加锁和解锁的方法都是显式的。

阅读更多

Java并发编程实战:第12章 测试并发程序

1. 测试正确性

1.1 测试并发程序

并发程序打造可以分为两类:

  1. 安全性测试
  2. 活跃性测试
    • 进展性测试
    • 无进展测试

与活跃性测试相关的事性能测试。性能的衡量:

  • 吞吐量
  • 响应性
  • 可伸缩性
阅读更多

Java并发编程实战:第11章 总结

  • 性能可伸缩性: 多线程能充分的发挥出闲置的处理能力,使现有任务正在运行的情况下立刻开始着手处理新的任务,提高系统的响应性。

  • Amdahl 定律:

    N 表示处理器数量,F表示串行化任务的比重。

阅读更多

Java并发编程实战:第11章 性能可伸缩性

1. 性能可伸缩性

  • 使用多线程可以使程序更充分的发挥出闲置的处理能力,从而更好地利用资源;
  • 使用多线程,还能够使程序在现有任务正在运行的情况下立刻开始着手处理新的任务,从而提高系统的响应性。
阅读更多

Java并发编程实战:第10章 总结

  • 死锁:静态的锁顺序死锁、动态的锁顺序死锁、协作对象之间发生的死锁、资源死锁。
  • 在发生死锁时,可以通过线程转储 — 如使用命令 jcmd PID号 Thread.print 输出线程和锁信息,分析死锁原因。
阅读更多

Java并发编程实战:第9章 总结

  • GUI框架几乎都是作为单线程化子系统实现的,所有与表现相关的代码都作为任务在一个事件线程中运行。
  • 因为只有唯一一个线程,耗时任务会损害响应性,所以它们应该在后台线程中运行。
阅读更多

Java并发编程实战:第9章 GUI应用程序

1. GUI 应用程序

  • 几乎所有的GUI工具集都是单线程化的子系统,意味着所有GUI的活动都被限制在一个单独的线程中。
  • 我们应该避免在事件线程中执行耗时操作,以免UI失去响应。
  • Swing 的数据结构不是线程安全的,所以在使用它时必须小心的把他们限制在事件线程中。
阅读更多

Java并发编程实战:第8章 总结

  • 对于并发执行的任务,Executor 框架是强大且灵活的。
  • ThreadPoolExecutor 提供了大量可调节的选项,比如创建和关闭线程的策略,处理队列任务的策略,处理过剩任务的策略,并且提供了几个钩子函数用于扩展它的行为。
    • 钩子函数:terminated()、afteExecute()、beforeExecute()
阅读更多