`
m635674608
  • 浏览: 5028744 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Java多线程之内存可见性

    博客分类:
  • java
 
阅读更多

Java内存模型( JMM ) :

    1) 所有的变量都存储在主内存中

    2) 每个线程都有自己独立的工作内存, 里面保存该线程使用到的变量的副本 ( 主内存中该变量的一份拷贝 )

JMM 两条规定:

    1) 线程对共享变量的所有操作都必须在自己的工作内存中进行

    2) 不同线程之间无法直接访问其他线程工作内存中的共享变量, 线程间共享变量值的传递必须通过主内存 

线程间共享变量可见性实现的原理:

    线程A 对共享变量的修改想被线程B 及时看到, 必须要经过以下2个步骤:

        1) 把线程A 工作内存中更新过的共享变量刷新到主内存中( store )

        2) 将主内存中最新的共享变量的值共享到线程B 工作内存中( load )

 

Java 语言层面支持的可见性实现方式:

    1) synchronized

    2) volatile

JUC 包下的类也可以实现可见性

    1) Atomic

    2) ReentrantLock

    3) Semaphore

 

1. synchronized 实现可见性

    JMM 关于 synchronized 的两条规定:

        1) 线程释放锁前, 必须把共享变量的最新值从该线程的工作内存刷新到主内存中

        2) 线程持有锁时, 将清空该线程工作内存中共享变量的值, 从主内存中读取最新的值

 

synchronized 实现可见性的原因:

    线程释放锁前对共享变量的修改在下次持有锁时对其他线程可见

public class SynchronizedDemo {
	// 共享变量
	private int result = 0;

	// 共享变量执行自增操作
	public synchronized void increase() {
		result++;
	}

	public int getResult() {
		return result;
	}

	public static void main(String[] args) throws InterruptedException {
		final SynchronizedDemo demo = new SynchronizedDemo();
		// 设置启动500个线程
		int count = 500;
		// 创建一个 JUC 包下的同步同步计数器, 设置计数次数为线程数500
		final CountDownLatch cdl = new CountDownLatch(count);
		for (int i = 0; i < count; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					demo.increase();
					cdl.countDown();
				}
			}).start();
		}
		cdl.await();
		System.out.println(demo.getResult());
	}
}

Console 输出: 500

synchronized 不仅可以实现可见性, 还可以实现原子性

 

2. volatile 实现可见性

 

volatile 如何实现可见性:

    通过加入内存屏障和禁止指令重排序

    1) 对 volatile 变量执行写操作时, 会在写操作后加入一条 store 屏障指令

    2) 对 volatile 变量执行读操作时, 会在读操作前加入一条 load 屏障指令

 

public class VolatileDemo {
	// 使用 volatile 修饰共享变量
	private volatile int result = 0;

	// 共享变量 result 执行自增操作, 无法保证原子性
	public void increase() {
		result++;
	}

	public int getResult() {
		return result;
	}

	public static void main(String[] args) throws InterruptedException {
		final VolatileDemo demo = new VolatileDemo();
		// 设置启动500个线程
		int count = 500;
		// 创建一个 JUC 包下的同步同步计数器, 设置计数次数为线程数500
		final CountDownLatch cdl = new CountDownLatch(count);
		for (int i = 0; i < count; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					demo.increase();
					cdl.countDown();
				}
			}).start();
		}
		cdl.await();
		System.out.println(demo.getResult());
	}
}

    Console 输出: 498

volatile 关键字, 能保证 volatile 变量的可见性, 不能保证 volatile 变量操作的原子性( 如 ++/-- )

 

3. AtomicInteger 实现可见性

    用 Atomic 类实现共享变量在线程中的原子性( 通过 CAS, 自旋 实现)

public class AtomicIntegerDemo {
	// 共享变量
	private AtomicInteger result = new AtomicInteger(0);

	// 使用 Atomic 类的 incrementAndGet() 方法( 原子操作 ), 实现自增
	public void increase() {
		result.incrementAndGet();
	}

	public int getResult() {
		return result.get();
	}

	public static void main(String[] args) throws InterruptedException {
		final AtomicIntegerDemo demo = new AtomicIntegerDemo();
		// 设置启动500个线程
		int count = 500;
		// 创建一个JUC包下的同步同步计数器, 设置计数次数为线程数500
		final CountDownLatch cdl = new CountDownLatch(count);
		for (int i = 0; i < count; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					demo.increase();
					cdl.countDown();
				}
			}).start();
		}
		cdl.await();
		System.out.println(demo.getResult());
	}
}

Console 输出: 500

 

4. JUC 包下的 Lock 实现可见性

    用 ReentrantLock 实现共享变量在线程中的原子性

public class LockDemo {
	// 共享变量
	private int result = 0;
	// 可重入锁
	private Lock lock = new ReentrantLock();

	// 使用锁机制, 保证锁内代码的原子性
	public void increase({
		// 加锁
		lock.lock();
		try {
			result++;
		} finally {
			// 释放锁
			lock.unlock();
		}
	}

	public int getResult({
		return result;
	}

	public static void main(String[] args) throws InterruptedException {
		final LockDemo demo = new LockDemo();
		// 设置启动500个线程
		int count = 500;
		// 创建一个JUC包下的同步同步计数器, 设置计数次数为线程数500
		final CountDownLatch cdl = new CountDownLatch(count);
		for (int i = 0; i < count; i++) {
			new Thread(new Runnable() {
				@Override
				public void run({
					demo.increase();
					cdl.countDown();
				}
			}).start();
		}
		cdl.await();
		System.out.println(demo.getResult());
	}
}

Console 输出: 500

 

5. Semaphore 实现可见性

    用信号量机制实现共享变量在线程中的原子性

public class SemaphoreDemo {
	// 共享变量
	private int result = 0;
	// 初始化信号量为1, 一次只能有1个线程访问共享变量, 相当于互斥锁
	private Semaphore semaphore = new Semaphore(1);

	// 使用信号量机制, 保证共享变量自增操作的原子性
	public void increase() {
		try {
			// 获取1个信号量
			semaphore.acquire();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		result++;
		// 释放1个信号量
		semaphore.release();
	}

	public int getResult() {
		return result;
	}

	public static void main(String[] args) throws InterruptedException {
		final SemaphoreDemo demo = new SemaphoreDemo();
		// 设置启动500个线程
		int count = 500;
		// 创建一个JUC包下的同步同步计数器, 设置计数次数为线程数500
		final CountDownLatch cdl = new CountDownLatch(count);
		for (int i = 0; i < count; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					demo.increase();
					cdl.countDown();
				}
			}).start();
		}
		cdl.await();
		System.out.println(demo.getResult());
	}
}

Console 输出: 500

 

总结:

synchronized 代码块具备 可见性 和 可见性

volatile 变量具备可见性, 不具备原子性

正确使用 volatile 变量

 

http://my.oschina.net/u/2503731/blog/671283

分享到:
评论

相关推荐

    基于Java多线程同步的安全性研究.pdf

    接着,文章讨论了Java多线程同步机制中可能出现的安全性问题,如可见性、有序性和互斥性问题,并提出了解决这些问题的方法,包括使用ThreadLocal和Lock对象。 在文章的后半部分,讨论了Java多线程执行过程的机制和...

    java 多线程并发实例

    - volatile:修饰变量,确保多线程环境下的可见性和有序性,但不保证原子性。在实例中,可能用于共享标志的设置与读取。 - wait()、notify()和notifyAll():这些方法存在于Object类中,用于线程间的通信。在线程A...

    Java多线程详解及示例

    Java多线程编程是提升程序性能和响应性的关键技术。理解多线程的概念,掌握线程的创建、同步、通信、死锁避免等核心知识点,以及合理使用线程池,对于编写高效、稳定的并发程序至关重要。通过实践,开发者可以更好地...

    Java多线程编程实战指南-核心篇

    《Java多线程编程实战指南-核心篇》是一本深入探讨Java并发编程的书籍,旨在帮助读者掌握在Java环境中创建、管理和同步线程的核心技术。Java的多线程能力是其强大之处,使得开发者能够在同一时间执行多个任务,提高...

    java内存模型和一些多线程的资料

    Java内存模型(JVM Memory Model,简称JMM)是Java平台中的一个重要概念,它定义了在多线程环境下,如何在共享内存中读写变量的行为。JMM的主要目标是确保多线程环境下的可见性、有序性和原子性,从而避免数据不一致...

    java 多线程编程指南

    这份“Java多线程编程指南”深入探讨了这一主题,为中级到高级的Java开发者提供了宝贵的资源。 首先,多线程的基础概念是理解整个主题的关键。线程是程序执行的最小单元,每个线程都有自己的程序计数器、虚拟机栈、...

    java多线程并发实战和源码

    总结来说,Java多线程并发实战和源码的学习涵盖了线程创建与管理、同步机制、并发容器、内存模型以及并发工具类等多个方面。虽然书中实例不足,但通过结合其他资源,如jcip-examples-src.rar中的代码,可以进一步...

    java多线程进度条实例

    `volatile`关键字确保了`currentProgress`在多线程环境下的可见性。 同时,为了实时更新当前时间,我们可以添加一个定时器,每隔一定时间更新一次时间显示。在控制台环境中,我们可以使用`System.currentTimeMillis...

    Java多线程中提到的原子性和可见性、有序性1

    Java多线程编程中,原子性、可见性和有序性是三个关键的概念,它们确保了多线程环境下的正确性。 1. 原子性(Atomicity) 原子性指的是一个操作不可被中断,要么全部执行,要么完全不执行。在Java中,非原生类型的...

    深入学习:Java多线程编程

    9. **JVM与多线程**:理解JVM的内存模型(JMM)对于理解多线程至关重要,包括可见性和有序性等概念。 10. **性能调优**:如何根据实际需求调整线程数量,平衡CPU负载和系统响应时间,是优化多线程程序的关键。 11....

    Java多线程资料

    Java多线程是Java编程中的核心概念,它允许程序同时执行多个任务,提高了软件的效率和响应性。在Java中,多线程的实现主要有两种方式:通过继承Thread类和实现Runnable接口。这篇资料深入探讨了Java多线程的相关知识...

    Java多线程.pdf

    Java多线程是Java编程中一个非常重要的概念,它允许程序在同一时间执行多个任务,从而提高了应用程序的效率和响应速度。在操作系统中,程序被加载到内存中形成进程,而进程中的执行单元就是线程。Java多线程的实现...

    java多线程视频1

    Java多线程是Java编程中的核心概念,尤其在开发高性能、高并发的应用时,多线程技术显得尤为重要。本系列视频教程将深入讲解Java多线程的各个方面,帮助你掌握如何在Java环境中创建、管理以及优化多线程程序。 首先...

    Java多线程实例代码

    Java多线程是Java编程中的重要概念,它允许程序同时执行多个任务,提高了程序的效率和响应性。在Java中,实现多线程有两种主要方式:通过继承`Thread`类和实现`Runnable`接口。 1. 继承Thread类: 当你需要创建一...

    深入浅出 Java 多线程.pdf_java_

    Java多线程是Java编程中的核心概念,尤其在如今并发性能至关重要的软件开发中,它的重要性不言而喻。深入理解Java多线程能够帮助开发者有效地利用计算机资源,提高程序的执行效率,优化系统性能。 Java多线程的实现...

    Java多线程编程总结

    ### Java多线程编程总结 #### 一、Java线程:概念与原理 1. **操作系统中线程和进程的概念** - 当前的操作系统通常为多任务操作系统,多线程是实现多任务的一种手段。 - **进程**:指内存中运行的应用程序,每个...

    Java多线程管理示例

    Java多线程是Java编程中的一个关键特性,它允许程序同时执行多个任务,极大地提高了程序的效率和响应性。在Java中,多线程可以通过实现Runnable接口或继承Thread类来创建。下面我们将深入探讨Java多线程的核心概念、...

    java多线程安全性基础介绍.pptx

    java多线程安全性基础介绍 线程安全 正确性 什么是线程安全性 原子性 竞态条件 i++ 读i ++ 值写回i 可见性 JMM 由于cpu和内存加载速度的差距,在两者之间增加了多级缓存导致,内存并不能直接对cpu可见。 ...

    java线程-Java内存模型

    Java内存模型(JMM)是Java虚拟机规范的一部分,用于定义程序中各个线程对共享变量的访问规则,包括线程之间的通信、内存可见性和原子性等问题。 首先,Java内存模型规定了三个主要区域:程序计数器、虚拟机栈、...

    java多线程面试题和答案

    以下是一些关于Java多线程的面试题及其答案,涵盖了基础概念、并发控制、线程安全以及性能优化等方面。 1. **什么是Java多线程?** 多线程是指在单个程序中同时执行多个线程,这样可以提高应用程序的效率和响应...

Global site tag (gtag.js) - Google Analytics