synchronized
关键字可以保证同一时刻,只有一个线程可以执行某一个方法,或是某一个代码块。
它包含两个特征:1、互斥 2、可见。即同步不仅可以阻止一个线程看到对象处于不一致的状态中,还可以保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护的之前所有的修改效果。
java语言规范保证读或者写一个变量时原子的,除非这个变量的类型为long或者double。
读取一个非long或double类型的变量,可以保证返回的值是某个线程保存在该变量中的,即使多线程在没有同步的情况下并发的修改这个变量也是如此。
虽然语言规范保证了线程在读取原子数据的时候,不会看到任意的数值,但是它并不保证一个线程写入的值对于另一个线程是可见的。为了在线程之间进行可靠通信,也为了互斥访问,同步是必要的。
public class StopThread {
private static boolean stopRequested = false;
public static synchronized boolean isStopRequested() {
return stopRequested;
}
public static synchronized void setStopRequested(boolean stopRequested) {
StopThread.stopRequested = stopRequested;
}
public static void main(String[] args) {
try {
new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while (!isStopRequested()) {
System.out.println(i++);
}
}
}).start();
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
setStopRequested(true);
}
}
上面的synchronized关键字是需要的,如果没有同步的话,这个程序永远不会终止:因为不能保证后台线程何时"看到"主线程对stopRequested的值所做的改变,后台线程永远在循环。
注意:读写方法都要被同步,否则同步就不会起作用。
stopRequested即使没有被同步也是原子的,这些同步方法是为了它的
通信效果
,而不是为了互斥访问。
volatile
变量可以被看作是一种 “程度较轻的 synchronized”;与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分。
锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility)。互斥即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调访问协议,这样,一次就只有一个线程能够使用该共享数据。可见性要更加复杂一些,它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的 —— 如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是修改前的值或不一致的值,这将引发许多严重问题。
Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。Volatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。
public class StopThread2 {
private static volatile boolean stopRequested = false;
public static boolean isStopRequested() {
return stopRequested;
}
public static void setStopRequested(boolean stopRequested) {
StopThread2.stopRequested = stopRequested;
}
public static void main(String[] args) {
try {
new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while (!isStopRequested()) {
System.out.println(i++);
}
}
}).start();
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
setStopRequested(true);
}
}
单独使用 volatile 还不足以实现计数器,问题在于操作符(++)不是原子的,例如
private static volatile int nextSerialNumber = 0;
public static int generaterSerialNumber(){
return nextSerialNumber ++;
}
它在nextSerialNumber域中执行两个操作:首先它读取值,然后写回一个新值,相当于原来的值再加上1。如果第二个线程在第一个线程读取旧值和写回新值期间读取这个域,第二个线程就会与第一个线程看到同一值,并返回相同的序列号,这个程序会计算出错误结果。
修正generaterSerialNumber的方法的一种方法是:在它的声明中去掉volatile增加synchronized修饰符。这样可以确保多个调用不会交叉存取,确保每个调用都会看到之前所有调用的效果。
最好的修正方法是:使用类AtomicLong
private static final AtomicLong nextSerialNumber = new AtomicLong();
public static long generaterSerialNumber(){
return nextSerialNumber.getAndIncrement();
}
简而言之,多个线程共享可变数据的时候,每个读或写数据的线程都必须执行同步。如果没有同步,就无法保证一个线程所做的修改可以被另一个线程获知。如果需要线程之间的交互通信,而不需要互斥,volatile修饰符就是一种可以接受的形式,但需要正确的使用。
分享到:
相关推荐
3. 当多个线程需要访问的变量是不可变的,例如静态常量。 总的来说,`volatile`是Java并发编程中一个重要的工具,它简化了同步机制,但在使用时需谨慎,确保其特性足以满足并发场景的需求。在需要更强的线程安全性...
无论是否执行同一份代码,只要这些线程的代码访问同一份可变的共享资源,这些线程之间就需要同步。 线程同步机制可以通过使用锁来实现。锁可以是对象锁、类锁或同步锁。锁的作用是防止多个线程同时访问同一个共享...
3. **只有共享变量(可变状态)才需要同步,常量或不可变对象可以并发访问。** 4. **同步可以应用于方法或代码块,根据需求选择合适的同步策略。** 5. **Java提供了`synchronized`关键字以及更高级的并发工具来实现...
在实际编程中,除了使用这些同步机制外,我们还可以通过设计无状态对象、不可变对象以及使用线程局部变量(ThreadLocal)等方式来减少线程安全问题的发生。同时,避免使用静态变量存储线程相关的状态,尽量减少共享...
* synchronized(同步的):使用锁机制来保护共享资源的访问。 * asynchronized(异步的):不使用锁机制,允许多个线程同时访问共享资源。 * volatile(易变的):变量值可以被其他线程改变。 * atomic(原子的):...
这使得`volatile`在Java中成为一种轻量级的同步机制,有时可以替代`synchronized`关键字。 6. **Volatile 的起源** `volatile`关键字起源于C语言,并被C++继承。它最初是为了在嵌入式系统中处理硬件交互,比如读写...
- 尽量减少共享状态,如果必须共享,使用不可变对象。 - 使用最小粒度的锁,避免锁的粒度过大导致的阻塞。 - 避免死锁,确保线程间的资源获取顺序不会形成环路。 - 使用`ThreadLocal`存储线程私有数据,避免全局变量...
1. **volatile关键字**:volatile 是 Java 提供的一种轻量级同步机制,它可以确保变量的可见性。当一个线程修改了 volatile 变量时,其他线程能立即看到这个变化。但volatile不能保证原子性,例如,自增操作在多线程...
final在多线程编程中扮演着重要角色,它用于声明不可变对象。一旦一个变量被声明为final,并且在初始化时赋值,那么它的值就不能再改变。在多线程环境下,final变量的初始化保证了线程安全,因为一旦初始化完成,...
`final`表示常量或不可变类;`abstract`表示抽象方法或抽象类;`void`表示没有返回值的方法。 2. 类与继承:`extends`表示类的继承;`implements`表示实现接口。 3. 变量初始化:`this`引用当前对象;`super`引用...
Java提供了synchronized关键字和Volatile关键字来实现线程间的同步。synchronized关键字可以用来修饰方法或代码块,确保同一时刻只有一个线程可以执行该段代码。而Volatile关键字则确保一个变量的更新对所有线程立即...
#### 四、Volatile关键字 `volatile`关键字是Java提供的一种轻量级的同步机制。它主要用于解决变量的可见性和部分有序性问题: - **可见性**:保证所有线程能看到同一个变量的最新值。 - **有序性**:禁止指令重...
Java编程语言在多线程...总之,volatile关键字是Java并发编程中不可或缺的一部分,它提供了弱形式的锁,确保了变量在多线程环境中的正确同步和可见性。理解和正确使用volatile对于编写高效、可靠的并发程序至关重要。
synchronized关键字可以用来控制多个线程对共享资源的互斥访问,而volatile关键字可以保证变量的内存可见性。Java并发包(java.util.concurrent)也提供了丰富的并发工具,如线程安全集合(如ConcurrentHashMap)、...
10. ** synchronized**: 这个关键字用于实现线程同步,确保在多线程环境中,对共享资源的访问是互斥的。 11. ** volatile**: `volatile`关键字确保共享变量的可见性和有序性,主要用于多线程环境中的共享数据。 12...
而有序性则通过Java提供的同步机制(如synchronized关键字)和volatile关键字来实现,确保内存的访问顺序在多个线程之间的一致性。 在JMM中,程序顺序原则指出,线程内部的操作顺序是确定的,但从外部观察,由于...
3. 线程同步:Java提供了多种同步机制,如synchronized关键字、volatile变量、Lock接口(ReentrantLock等)、Semaphore信号量、CyclicBarrier屏障等,用于解决多线程环境下资源竞争和数据一致性问题。 二、不可变...
3. **同步访问**:通过使用`synchronized`关键字,可以确保同一时刻只有一个线程访问特定的代码块或方法,保证了线程安全。同步可以是方法级别的,也可以是代码块级别的,使用`synchronized (lock) { ... }`来指定锁...
答:volatile 关键字用于修饰变量,确保多个线程之间共享变量的可见性和同步性,防止出现数据不一致的问题。34. Java 中的 synchronized 关键字有何用途?答:synchronized 关键字用于实现线程同步,它可以保证在...