Java中的线程安全是一个非常重要的主题。Java使用Java Threads提供多线程环境支持,我们知道从相同的Object共享对象变量创建的多个线程,当线程用于读取和更新共享数据时,这可能导致数据不一致。
数据不一致的原因是因为更新任何字段值不是原子过程,它需要三个步骤; 首先读取当前值,第二个读取必要的操作以获取更新的值,第三个将更新的值分配给字段引用。
让我们用一个简单的程序来检查它,其中多个线程正在更新共享数据。
在上面的循环程序中,count
增加了4
次,因为有两个线程,所以在两个线程完成执行后它的值应该是8
。但是当多次运行上面的程序时,注意到count
值在6
,7
,8
之间变化。这种情况正在发生,因为即使count ++
似乎是一个原子操作,它的NOT
也会导致数据损坏。
Java线程安全
java中的线程安全是使程序在多线程环境中安全使用的过程,可以通过不同的方式使程序线程安全。
- 同步是java中最简单和最广泛使用的线程安全工具。
- 使用
java.util.concurrent.atomic
包中的Atomic
包装类。例如AtomicInteger
。 - 使用
java.util.concurrent.locks
包中的锁。 - 使用线程安全集合类,以确保线程安全。
- 使用带有变量的
volatile
关键字使每个线程从内存中读取数据,而不是从线程缓存中读取。
Java同步
同步是可以使用来实现线程安全的工具,JVM保证同步代码一次只能由一个线程执行。java关键字synchronized
用于创建同步代码,在内部它使用Object
或类上的锁来确保只有一个线程正在执行同步代码。
Java同步在锁定和解锁资源时起作用,在任何线程进入同步代码之前,它必须获取对象的锁定,并且当代码执行结束时,它解锁可以被其他线程锁定的资源。同时,其他线程处于等待状态以锁定同步资源。
使用
synchronized
关键字有两种方式,一种是使一个完整的方法同步,另一种方法是创建synchronized
块。当方法同步时,它会锁定对象,如果方法是静态的,它会锁定类,因此最好使用
synchronized
块来锁定需要同步的方法的唯一部分。在创建
synchronized
块时,需要提供将获取锁的资源,它可以是XYZ.class
或类的字段。synchronized(this)
将在进入synchronized
块之前锁定对象。应该使用最低级别的锁定,例如,如果类中有多个
synchronized
块,并且其中一个锁定了对象,则其他同步块也将无法由其他线程执行。当锁定一个对象时,它会获取对象的所有字段的锁定。Java同步提供了性能成本的数据完整性,因此只有在绝对必要时才应使用它。
Java同步仅在同一个JVM中工作,因此如果需要在多个JVM环境中锁定某些资源,它将无法工作,可能需要处理一些全局锁定机制。
Java同步可能会导致死锁。
Java
synchronized
关键字不能用于构造函数和变量。最好创建一个用于同步块的虚拟私有对象,这样它的引用就不能被任何其他代码更改。例如,如果正在同步的对象的
setter
方法,则可以通过某些其他代码更改其引用,以并行执行synchronized
块。不应该使用在常量池中维护的任何对象,例如
String
不应该用于同步,因为如果任何其他代码也锁定在同一个String
上,它将尝试从String
池获取对同一引用对象的锁定 即使两个代码都不相关,它们也会相互锁定。