- 浏览: 363751 次
- 性别:
- 来自: Alien
文章分类
最新评论
-
风一样的男人__:
[flash=200,200][url]引用[/url][/f ...
java线程内存模型,线程、工作内存、主内存 -
极乐君:
厉害了,,,请问可以转载到我们专栏吗?会注明来源和地址的~专栏 ...
java线程内存模型,线程、工作内存、主内存 -
zdd001:
Spring 线程池使用 -
zdd001:
Spring 线程池使用 -
u014663756:
看了三行就知道是我想要的!!
java线程内存模型,线程、工作内存、主内存
java线程内存模型
线程、工作内存、主内存三者之间的交互关系图:
key edeas
产生线程安全的原因
线程的working memory是cpu的寄存器和高速缓存的抽象描述:现在的计算机,cpu在计算的时候,并不总是从内存读取数据,它的数据读取顺序优先级 是:寄存器-高速缓存-内存。线程耗费的是CPU,线程计算的时候,原始的数据来自内存,在计算过程中,有些数据可能被频繁读取,这些数据被存储在寄存器和高速缓存中,当线程计算完后,这些缓存的数据在适当的时候应该写回内存。当多个线程同时读写某个内存数据时,就会产生多线程并发问题,涉及到三个特 性:原子性,有序性,可见性。 支持多线程的平台都会面临 这种问题,运行在多线程平台上支持多线程的语言应该提供解决该问题的方案。
JVM是一个虚拟的计算机,它也会面临多线程并发问题,java程序运行在java虚拟机平台上,java程序员不可能直接去控制底层线程对寄存器高速缓存内存之间的同步,那么java从语法层面,应该给开发人员提供一种解决方案,这个方案就是诸如 synchronized, volatile,锁机制(如同步块,就绪队 列,阻塞队列)等等。这些方案只是语法层面的,但我们要从本质上去理解它;
每个线程都有自己的执行空间(即工作内存),线程执行的时候用到某变量,首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作:读取,修改,赋值等,这些均在工作内存完成,操作完成后再将变量写回主内存;
各个线程都从主内存中获取数据,线程之间数据是不可见的;打个比方:主内存变量A原始值为1,线程1从主内存取出变量A,修改A的值为2,在线程1未将变量A写回主内存的时候,线程2拿到变量A的值仍然为1;
这便引出“可见性”的概念:当一个共享变量在多个线程的工作内存中都有副本时,如果一个线程修改了这个共享变量的副本值,那么其他线程应该能够看到这个被修改后的值,这就是多线程的可见性问题。
普通变量情况:如线程A修改了一个普通变量的值,然后向主内存进行写回,另外一条线程B在线程A回写完成了之后再从主内存进行读取操作,新变量的值才会对线程B可见;
如何保证线程安全
编写线程安全的代码,本质上就是管理对状态(state)的访问,而且通常都是共享的、可变的状态。这里的状态就是对象的变量(静态变量和实例变量)
线程安全的前提是该变量是否被多个线程访问, 保证对象的线程安全性需要使用同步来协调对其可变状态的访问;若是做不到这一点,就会导致脏数据和其他不可预期的后果。无论何时,只要有多于一个的线程访问给定的状态变量,而且其中某个线程会写入该变量,此时必须使用同步来协调线程对该变量的访问。Java中首要的同步机制是synchronized关键字,它提供了独占锁。除此之外,术语“同步”还包括volatile变量,显示锁和原子变量的使用。
在没有正确同步的情况下,如果多个线程访问了同一个变量,你的程序就存在隐患。有3种方法修复它:
l 不要跨线程共享变量;
l 使状态变量为不可变的;或者
l 在任何访问状态变量的时候使用同步。
volatile要求程序对变量的每次修改,都写回主内存,这样便对其它线程课件,解决了可见性的问题,但是不能保证数据的一致性;特别注意:原子操作:根据Java规范,对于基本类型的赋值或者返回值操作,是原子操作。但这里的基本数据类型不包括long和double, 因为JVM看到的基本存储单位是32位,而long 和double都要用64位来表示。所以无法在一个时钟周期内完成
通俗的讲一个对象的状态就是它的数据,存储在状态变量中,比如实例域或者静态域;无论何时,只要多于一个的线程访问给定的状态变量。而且其中某个线程会写入该变量,此时必须使用同步来协调线程对该变量的访问;
同步锁:每个JAVA对象都有且只有一个同步锁,在任何时刻,最多只允许一个线程拥有这把锁。
当一个线程试图访问带有synchronized(this)标记的代码块时,必须获得 this关键字引用的对象的锁,在以下的两种情况下,本线程有着不同的命运。
1、 假如这个锁已经被其它的线程占用,JVM就会把这个线程放到本对象的锁池中。本线程进入阻塞状态。锁池中可能有很多的线程,等到其他的线程释放了锁,JVM就会从锁池中随机取出一个线程,使这个线程拥有锁,并且转到就绪状态。
2、 假如这个锁没有被其他线程占用,本线程会获得这把锁,开始执行同步代码块。
(一般情况下在执行同步代码块时不会释放同步锁,但也有特殊情况会释放对象锁
如在执行同步代码块时,遇到异常而导致线程终止,锁会被释放;在执行代码块时,执行了锁所属对象的wait()方法,这个线程会释放对象锁,进入对象的等待池中)
Synchronized关键字保证了数据读写一致和可见性等问题,但是他是一种阻塞的线程控制方法,在关键字使用期间,所有其他线程不能使用此变量,这就引出了一种叫做非阻塞同步的控制线程安全的需求;
ThreadLocal 解析
顾名思义它是local variable(线程局部变量)。它的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。
每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。
Object wait()和notify()方法解析
Object的wait()和notify()、notifyAll()方法,使用一个对象作为锁,然后调用wait()就会挂起当前线程,同时释放对象锁;
notify()使用要首先获取对象锁,然后才能唤醒被挂起的线程(因为等待对象锁而挂起的)
notifyAll():唤醒在此对象监视器上等待的所有线程。
wait()在其他线程调用此对象的 notify()
方法或 notifyAll()
方法前,导致当前线程等待。
抛出:IllegalMonitorStateException
- 如果当前线程不是此对象监视器的所有者
package com.taobao.concurrency; public class WaitTest { public static String a = "";// 作为监视器对象 public static void main(String[] args) throws InterruptedException { WaitTest wa = new WaitTest(); TestTask task = wa.new TestTask(); Thread t = new Thread(task); t.start(); Thread.sleep(12000); for (int i = 5; i > 0; i--) { System.out.println("快唤醒挂起的线程************"); Thread.sleep(1000); } System.out.println("收到,马上!唤醒挂起的线程************"); synchronized (a) { a.notifyAll(); } } class TestTask implements Runnable { @Override public void run() { synchronized (a) { try { for (int i = 10; i > 0; i--) { Thread.sleep(1000); System.out.println("我在运行 ***************"); } a.wait(); for (int i = 10; i > 0; i--) { System.out.println("谢谢唤醒**********又开始运行了*******"); } } catch (InterruptedException e) { e.printStackTrace(); } } } } }
用wait notify 解决生产者消费者问题代码:
package com.taobao.concurrency; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; class Meal { private final int orderNum; public Meal(int orderNum) { this.orderNum = orderNum; } public String toString() { return "Meal " + orderNum; } } class WaitPerson implements Runnable { private Restaurant restaurant; public WaitPerson(Restaurant r) { this.restaurant = r; } @Override public void run() { try { while (!Thread.interrupted()) { synchronized (this) { while (restaurant.meal == null) wait();// ..for the chef to produce a meal } System.out.println("WaitPerson got" + restaurant.meal); synchronized (restaurant.chef) { restaurant.meal = null; restaurant.chef.notifyAll();// ready for another } } TimeUnit.MICROSECONDS.sleep(100); } catch (InterruptedException e) { System.out.println("WaitPerson interrupted"); } } } class Chef implements Runnable { private Restaurant restaurant; private int count = 0; public Chef(Restaurant r) { this.restaurant = r; } @Override public void run() { try { while (!Thread.interrupted()) { synchronized (this) { while (restaurant.meal != null) wait();// ...for the meal to be taken } if (++count == 10) { System.out.println("Out of food,closing"); restaurant.exec.shutdownNow(); } System.out.println("Order up!"); synchronized (restaurant.waitPerson) { restaurant.meal = new Meal(count); restaurant.waitPerson.notifyAll(); } } } catch (InterruptedException e) { } } } public class Restaurant { Meal meal; ExecutorService exec = Executors.newCachedThreadPool(); WaitPerson waitPerson = new WaitPerson(this); Chef chef = new Chef(this); public Restaurant() { exec.execute(chef); exec.execute(waitPerson); } public static void main(String[] args) { new Restaurant(); } }
用ArrayBlockingQueue解决生产者消费者问题;默认使用的是非公平锁
take():取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到Blocking有新的对象被加入为止,若请求不到此线程被加入阻塞队列;
如果使用公平锁,当有内容可以消费时,会从队首取出消费者线程进行消费(即等待时间最长的线程)
add(anObject):把anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则招聘异常
package com.taobao.concurrency; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; public class TestBlockingQueues { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<String>(20); Thread pro = new Thread(new Producer(queue), "生产者"); pro.start(); for (int i = 0; i < 10; i++) { Thread t = new Thread(new Concumer(queue), "消费者 " + i); t.start(); } } } class Producer implements Runnable { BlockingQueue<String> queue; public Producer(BlockingQueue<String> queue) { this.queue = queue; } @Override public void run() { int i = 0; while (true) { try { System.out.println("生产者生产食物, 食物编号为:" + i); queue.put(" 食物 " + i++); Thread.sleep(1000); } catch (InterruptedException e) { System.out.println("生产者被中断"); } } } } class Concumer implements Runnable { BlockingQueue<String> queue; public Concumer(BlockingQueue<String> queue) { this.queue = queue; } @Override public void run() { while (true) { try { System.out.println(Thread.currentThread().getName() + "消费:" + queue.take()); } catch (InterruptedException e) { System.out.println("消费者被中断"); } } } } 执行结果:
消费者 0 请求消费
消费者 2 请求消费
消费者 4 请求消费
消费者 6 请求消费
消费者 8 请求消费
消费者 5 请求消费
生产者生产食物, 食物编号为:0
消费者 0消费: 食物 0
消费者 1 请求消费
消费者 3 请求消费
消费者 7 请求消费
消费者 9 请求消费
消费者 0 请求消费
生产者生产食物, 食物编号为:1
消费者 2消费: 食物 1
消费者 2 请求消费
生产者生产食物, 食物编号为:2
消费者 4消费: 食物 2
消费者 4 请求消费
生产者生产食物, 食物编号为:3
消费者 6消费: 食物 3
消费者 6 请求消费
生产者生产食物, 食物编号为:4
消费者 8消费: 食物 4
消费者 8 请求消费
生产者生产食物, 食物编号为:5
消费者 5消费: 食物 5
消费者 5 请求消费
生产者生产食物, 食物编号为:6
消费者 1消费: 食物 6
消费者 1 请求消费
生产者生产食物, 食物编号为:7
消费者 3消费: 食物 7
消费者 3 请求消费
生产者生产食物, 食物编号为:8
消费者 7消费: 食物 8
消费者 7 请求消费
生产者生产食物, 食物编号为:9
消费者 9消费: 食物 9
消费者 9 请求消费
多个生产者,多个消费者
package com.taobao.concurrency; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; public class TestBlockingQueues { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<String>(20); for (int i = 0; i < 10; i++) { Thread pro = new Thread(new Producer(queue), "生产者" + i); pro.start(); } for (int i = 0; i < 10; i++) { Thread t = new Thread(new Concumer(queue), "消费者 " + i); t.start(); } } } class Producer implements Runnable { BlockingQueue<String> queue; public Producer(BlockingQueue<String> queue) { this.queue = queue; } @Override public void run() { int i = 0; while (true) { try { System.out.println(Thread.currentThread().getName() + "生产食物, 食物编号为:" + Thread.currentThread().getName() + i); queue.put(" 食物 " + Thread.currentThread().getName() + i++); Thread.sleep(10000); } catch (InterruptedException e) { System.out.println("生产者被中断"); } } } } class Concumer implements Runnable { BlockingQueue<String> queue; public Concumer(BlockingQueue<String> queue) { this.queue = queue; } @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " 请求消费"); try { System.out.println(Thread.currentThread().getName() + "消费:" + queue.take()); Thread.sleep(100); } catch (InterruptedException e) { System.out.println("消费者被中断"); } } } } 生产者0生产食物, 食物编号为:生产者00 生产者2生产食物, 食物编号为:生产者20 生产者1生产食物, 食物编号为:生产者10 生产者3生产食物, 食物编号为:生产者30 生产者4生产食物, 食物编号为:生产者40 生产者6生产食物, 食物编号为:生产者60 生产者8生产食物, 食物编号为:生产者80 生产者5生产食物, 食物编号为:生产者50 生产者7生产食物, 食物编号为:生产者70 生产者9生产食物, 食物编号为:生产者90 消费者 0 请求消费 消费者 0消费: 食物 生产者00 消费者 2 请求消费 消费者 2消费: 食物 生产者20 消费者 1 请求消费 消费者 1消费: 食物 生产者10 消费者 4 请求消费 消费者 4消费: 食物 生产者30 消费者 3 请求消费 消费者 6 请求消费 消费者 6消费: 食物 生产者40 消费者 3消费: 食物 生产者60 消费者 8 请求消费 消费者 8消费: 食物 生产者80 消费者 5 请求消费 消费者 5消费: 食物 生产者50 消费者 7 请求消费 消费者 7消费: 食物 生产者70 消费者 9 请求消费 消费者 9消费: 食物 生产者90 消费者 0 请求消费 消费者 1 请求消费 消费者 2 请求消费 消费者 4 请求消费 消费者 3 请求消费 消费者 5 请求消费 消费者 7 请求消费 消费者 9 请求消费 消费者 6 请求消费 消费者 8 请求消费 生产者0生产食物, 食物编号为:生产者01 消费者 0消费: 食物 生产者01 生产者2生产食物, 食物编号为:生产者21 生产者4生产食物, 食物编号为:生产者41 消费者 1消费: 食物 生产者21 生产者1生产食物, 食物编号为:生产者11 消费者 2消费: 食物 生产者41 消费者 4消费: 食物 生产者11 生产者3生产食物, 食物编号为:生产者31
条件队列解释:
Condition queues are like the "toast is ready" bell on your toaster. If you are listening for it, you are notified promptly when your toast is ready and can drop what you are doing (or not, maybe you want to finish the newspaper first) and get your toast. If you are not listening for it (perhaps you went outside to get the newspaper), you could miss the notification, but on return to the kitchen you can observe the state of the toaster and either retrieve the toast if it is finished or start listening for the bell again if it is not.
基于条件的:多线程情况下,某个条件在某个时刻为假,不代表一直为假,可能到某个时刻就好了!
Lock 使用的默认 为非公平锁;condition对象继承了与之相关的锁的共平性特性,如果是公平的锁,线程会依照FIFO的顺序从Condition.wait中被释放;ArrayBlockingQueue中有一个比较不好的地方,生产者每次生产完之后,都要通知消费者,至于有没有性能损失TODO
评论
[flash=200,200][url][/url][/flash] |
专栏地址:https://zhuanlan.zhihu.com/dreawer
发表评论
-
Spring 线程池使用2
2013-04-25 18:41 1100<!-- 配置异步线程执行器 --> < ... -
java synchronized与lock区别 转
2013-03-20 01:59 1008java synchronized与lock区别 ... -
java并发编程实践笔记(转)
2012-11-28 01:14 896一、 保证线程安全的 ... -
并发集合类ConcurrentSkipListSet
2012-11-24 03:26 5572ConcurrentSkipListSet Concu ... -
Amdahl 定律 (阿姆达尔定律)
2012-11-24 02:14 2022有些问题使用越多的资源就能越快地解决——越多的工人参 ... -
线程同步与异步
2012-11-22 23:53 2431Java中的线程同步与 ... -
Java 并发核心编程(转)
2012-11-06 10:16 972Java 并发核心编程(转) Java 并发核心编程 ... -
java线程:关于Callable和Future的简单使用:有返回值的线程
2012-11-06 09:19 1918分类: java_线程2012-07-03 19 ... -
java数据结构:ConcurrentSkipListSet<E>与CopyOnWriteArraySet<E>
2012-11-06 09:20 5789分类: java_数据结构 java_线程201 ... -
java线程:信号量
2012-11-06 09:20 832Java 信号量 Semaphore 介绍 Semaphore ... -
java线程:一个死锁的小程序
2012-11-06 09:20 2675死锁是这样一种 ... -
java线程:互斥锁与读写锁
2012-11-05 17:34 6987java线程:互斥锁与读写锁 分类: ja ... -
java线程:三种方式实现生产者消费者问题_3
2012-11-05 17:34 1273java线程:三种方式 ... -
java线程:几个实用的线程工具类
2012-11-05 15:15 8317java线程:几个实用的线程工具类 分类: ... -
java线程:三种方式实现生产者消费者问题_2
2012-11-05 14:26 1081java线程:三种方式实现生产者消费者问题_2 ... -
java线程:三种方式实现生产者消费者问题_1
2012-11-05 14:22 1120java线程:三种方式实现生产者消费者问题_1 ... -
java ExecutorService 线程池Callable 和 Future接口
2012-08-29 21:10 1103javaEye转载(java多线程 Callable 的实现) ... -
线程并发
2012-03-31 15:41 10051:>内存模型 究竟什么是内存模型?内存模型描述了程序中 ... -
特殊的队列:BlockingQueue 使用(转)
2012-03-28 14:49 1016本例介绍一个特殊的队列:BlockingQueue,如果Blo ... -
Spring 线程池使用
2012-03-28 12:47 26034Spring 线程池使用 /** * */ 一个日志管理 ...
相关推荐
Java线程之间的通信由Java内存模型(本文简称为JMM)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main ...
从抽象的角度来看,JMM是这样定义线程和主内存之间的抽象关系的:线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的...
而JAVA内存模型规定所有变量都存储在主内存 主内存是共享内存区域 所有线程都可以访问 但线程对变量的操作必须在工作内存中进行 首先要将变量从主内存拷贝到自己的工作内存空间,然后对变量操作,操作完成后再将...
> 工作内存可以与处理器的高速缓存类比线程、主内存、工作内存的交互关系内存间交互操作java内存模型定义的8种操作lock(锁定): 作用于主内存的变量,它把一
系统存在一个主内存(Main Memory), Java中所有变量都储存在主存中,对于所有线程都是共享的。每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是...
Java语言定义了线程模型规范,通过内存模型控制线程与变量的交互,从而实现Java线程之间的通信。在JDK5之前,Java一直使用的是旧内存模型。如图1所示。变量保存在由所有线程共享的主内存中,主内存中的变量称为...
read(读取):作用于主内存的变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用 load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中 use(使用...
JMM规定了所有的变量都存储在主内存中,但每个线程还有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝。线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量...
1. java 内存模型 2. 可见性 1. 初始状态, t 线程刚开始从主内存读取了 run 的值到工作内存 2. 因为 t 线程要频繁从主内存中读取 run
Java线程之间的通信由Java内存模型(JMM)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见。 从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main ...
JMM内存原型:java可以在不同操作系统不同硬件存储逻辑下运行的结果相同,是因为有自己的规则,JMM内存模型操作的是工作内存(缓冲区),并非真正的内存,工作内存中有主内存变量,JMM对8种内存交互做了8中规定,
同步控制是并发程序必不可少的重要手段,本文我们将通过重入锁、读写锁、信号量、倒计数器和循环栅栏以及他们的实例来介绍Java并发程序中的同步控制。...线程在访问主内存中的变量时并不是直接对主存中的变量进行读写,
Java内存模型规定,对于多个线程共享的变量,存储在主内存当中,每个线程都有自己独立的工作内存(比如CPU的寄存器),线程只能访问自己的工作内存,不可以访问其它线程的工作内存。 工作内存中保存了主内存...
在Java内存模型中,分为主内存和线程工作内存,线程使用共享数据时,都是先从主内存中拷贝到工作内存,使用完成之后再写入主内存,可以理解为线程之间通讯是通过共享内存的方式实现的。 在多线程环境下,不同线程对...
java 内存模型 ( java memory model ):根据Java Language Specification中的说明, jvm系统中存在一个主内存(Main Memory或Java Heap Memory),Java中所有对象成员变量都储存在主存中,对于所有线程都是共享的。...
主内存与工作内存 定义程序中各个变量的访问规则。 规定JVM必须遵循的小保证。 定义操作的偏序关系。Happens-Before规则:发布共享对象与另一线程访问对象。 说明了某个线程的内存操作,在哪些情况下对...
java 内存模型 ( java memory model ):根据Java Language Specification中的说明, jvm系统中存在一个主内存(Main Memory或Java Heap Memory),Java中所有对象成员变量都储存在主存中,对于所有线程都是共享的。...