java 并发原子性与易变性 来自thinking in java4 21.3.3

java 并发原子性与易变性  具体介绍请參阅thinking in java4 21.3.3

thinking in java 4免费下载:http://download.csdn.net/detail/liangrui1988/7580155

package org.rui.thread.volatiles;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * 假设你盲目地应用原子性慨念。那么就会看到在以下程序中的getValue符合上面的描写叙述
 *
 * 可是,该程序将找到奇数值并终止。虽然return i确实是原子性操作。可是缺少同步使得其数值能够在处于不稳定的中间状态时被读取。
 * 除此之外,因为i也不是volatile的,因此还存在可视性问题
 * @author lenovo
 *
 */
public class AtomicityTest implements Runnable {

	private int i=0;
	public int getValue(){return i;}
	private synchronized void evenIncrement(){i++;i++;}
	@Override
	public void run() {
		while(true)
			evenIncrement();

	}

	public static void main(String[] args) {
		ExecutorService exec=Executors.newCachedThreadPool();
		AtomicityTest at=new AtomicityTest();
		exec.execute(at);
		while(true)
		{
			int val=at.getValue();
			if(val%2!=0)
			{
				System.out.println(val);
				System.exit(0);
			}
		}
	}

}
package org.rui.thread.volatiles;
/**
 * 考虑一些更简单的事情。一个产生序列数字的类,
 * 每当nextSerial-Number被调用时,它必须向调用者返回唯一的值
 * @author lenovo
 *
 */
public class SerialNumberGenerator {

	private static volatile int serialNumber=0;
	public static int nextSerialNumber()
	{
		return serialNumber++;//非线程安全
	}
}
package org.rui.thread.volatiles;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
 * 为了測试SerialNumberGenerator 我们须要不会耗尽内存的集(SET) 。
 * 以防须要花费非常长的时间来探測问题。

这里所看到的的circularSet重用了存储int数值的内存,
 * 并假设在你生成序列数时,产生数值覆盖冲突的可能性极小。add 和contains 方法都 是synchronized,以防示线程冲突
 * @author lenovo
 *
 */
//重用存储所以我们不耗尽内存
//reuses storage so we don't run out of memory
class CircularSet
{
 private int[] array;
 private int len;
 private int index=0;

 public CircularSet(int size){
	 array=new int[size];
	 len=size;
	 //初始化一个值而不是生产
	 //这个初始全是-1和serialNumberGenerator 不同样。其后取serialNumberGenerator.next...存入
	 //by the serialNumberGenerator;
	 for(int i=0;i<size;i++)
		 array[i]=-1;
 }

 //add
 public synchronized void add(int i)
 {
	 array[index]=i;
	 //wrap index and write over old elements; 将指数和写在旧元素;
	index=++index % len;
	//System.out.println(index+"  :  len :"+len);

 }

 //contains
 public synchronized boolean contains(int val)
 {
	 for(int i=0;i<len;i++)
	 {
		 System.out.println(array[i]+" == "+val);
		 if(array[i]==val)
			 return true;

	 }
	 return false;
 }

}
///////////////////////////////////////////////
public class SerialNumberChecker {
	private static final int SIZE=10;
	private static CircularSet serials=new CircularSet(1000);
	private static ExecutorService exec=Executors.newCachedThreadPool();

	static class SerialChecker implements Runnable
	{
		@Override
		public void run() {
			while(true)
			{
				//自增长
				int serial=SerialNumberGenerator.nextSerialNumber();
				if(serials.contains(serial))
				{
					//反复
					System.out.println("Duplicate: "+serial);
					System.exit(0);
				}
				serials.add(serial);
			}
		}
	}

	////
	public static void main(String[] args) throws NumberFormatException, InterruptedException {
		String[] arg=new String[]{"10000"};

		for(int i=0;i<SIZE;i++)
		{
			exec.execute(new SerialChecker());
			//stop after n seconds if there 's an argument 停止在n秒后假设有一个论点
			if(arg.length>0)
			{
				TimeUnit.SECONDS.sleep(new Integer(arg[0]));
				System.out.println("没有反复检測");
				System.exit(0);
			}
		}
	}
}

/**
 * 通过创建多个任务来竞争序列数,你将发现这些任务终于会得到反复的序列数,假设你执行的时间足够长的话
 * 为了解决问题,nextSerialNumber 前面加入 了synchronized关健字
 */
时间: 2024-11-05 18:56:28

java 并发原子性与易变性 来自thinking in java4 21.3.3的相关文章

Java并发编程(六)原子性与易变性

原子性 原子是最小单元.不可再分的意思.原子性是指某个操作在获取CPU时间时,要么就给它足够时间,让这个操作执行完,要么就不执行这个操作,执行时不能出现上下文切换(把CPU时间从一个线程分配到另一个线程). Java中对变量的读取和赋值都是原子操作,但long.double类型除外,只有使用volatile修饰之后long.double类型的读取和赋值操作才具有原子性.除此之外Java还提供了几个常用的原子类,原子类的方法是具有原子性的方法,也就是说原子类在执行某个方法的过程中不会出现上下文切换

Java并发概述之安全

Java并发的学习内容主要来自<Java并发编程实战>一书,本文为一概述. 并发最简单的解释应该是不同任务的执行时间区间存在交集.由于时间上的交集+共享变量,并发会带来安全问题. 从任务的角度而言,任务的执行需要得到正确的效果:从对象的角度而言,对象需要被正确的访问. 所谓正确,或常说的线程安全,包括了一个对象操作,或者一个任务执行的三个方面:前置条件.不变约束.后置条件.这三个约束都与状态相关,只不过是状态在不同抽象层次上的参与. 状态(共享变量)在并发中有两个方面的语义: 1.作为对象的一

java并发特性:原子性、可见性、有序性

要想并发程序正确地执行,必须要保证原子性.可见性以及有序性.只要有一个没有被保证,就有可能会导致程序运行不正确. 1.原子性(Atomicity) 原子性是指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行. 如果一个操作时原子性的,那么多线程并发的情况下,就不会出现变量被修改的情况 比如 a=0:(a非long和double类型) 这个操作是不可分割的,那么我们说这个操作时原子操作.再比如:a++: 这个操作实际是a = a + 1:是可分割的,所以

Java 并发编程(二):如何保证共享变量的原子性?

线程安全性是我们在进行 Java 并发编程的时候必须要先考虑清楚的一个问题.这个类在单线程环境下是没有问题的,那么我们就能确保它在多线程并发的情况下表现出正确的行为吗? 我这个人,在没有副业之前,一心扑在工作上面,所以处理的蛮得心应手,心态也一直保持的不错:但有了副业之后,心态就变得像坐过山车一样.副业收入超过主业的时候,人特别亢奋,像打了鸡血一样:副业迟迟打不开局面的时候,人就变得惶惶不可终日. 仿佛我就只能是个单线程,副业和主业并行开启多线程模式的时候,我就变得特别没有安全感,尽管整体的收入

【Java并发基础】并发编程bug源头:可见性、原子性和有序性

前言 CPU .内存.I/O设备之间的速度差距十分大,为了提高CPU的利用率并且平衡它们的速度差异.计算机体系结构.操作系统和编译程序都做出了改进: CPU增加了缓存,用于平衡和内存之间的速度差异. 操作系统增加了进程.线程,以时分复用CPU,进而均衡CPU与I/O设备之间的速度差异. 编译程序优化指令执行次序,使得缓存能够得到更加合理地利用. 但是,每一种解决问题的技术出现都不可避免地带来一些其他问题.下面这三个问题也是常见并发程序出现诡异问题的根源. 缓存--可见性问题 线程切换--原子性问

Java并发编程之验证volatile不能保证原子性

通过系列文章的学习,凯哥已经介绍了volatile的三大特性.1:保证可见性 2:不保证原子性 3:保证顺序.那么怎么来验证可见性呢?本文凯哥(凯哥Java:kaigejava)将通过代码演示来证明为什么说volatile不能够保证共享变量的原子性操作. 我们来举个现实生活中的例子: 中午去食堂打饭,假设你非常非常的饥饿,需要一荤两素再加一份米饭.如果食堂打饭的阿姨再给你打一个菜的时候,被其他人打断了,给其他人打饭,然后再回过头给你打饭.你选一荤两素再加一份米饭打完的过程被打断了四次耗时30分钟

Java并发(具体实例)—— 构建高效且可伸缩的结果缓存

这个例子来自<Java并发编程实战>第五章.本文将开发一个高效且可伸缩的缓存,文章首先从最简单的HashMap开始构建,然后分析它的并发缺陷,并一步一步修复. hashMap版本 首先我们定义一个Computable接口,该接口包含一个compute()方法,该方法是一个耗时很久的数值计算方法.Memoizer1是第一个版本的缓存,该版本使用hashMap来保存之前计算的结果,compute方法将首先检查需要的结果是否已经在缓存中,如果存在则返回之前计算的值,否则重新计算并把结果缓存在Hash

《Java并发变成实践》读书笔记---第二章 线程安全性

什么是线程安全性 要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对共享的(Shared)和可变的(Mutable)状态的访问.从非正式的意义上来说,对象的状态是指存储在状态变量(例如实例或静态域)中的数据."共享"意味着变量可以由多个线程同时访问,而"可变"则意味着变量的值在其生命周期内可以发生变化.所以编写线程安全的代码更侧重于如何防止在数据上发生不受控的并发访问. 如果当多个线程访问同一个可变的状态变量时没有使用合适的同步,那么程序就会出现错误

Java并发和多线程基础(一)

1.java线程状态 Java中的线程可以处于下列状态之一: NEW: 至今尚未启动的线程处于这种状态. RUNNABLE: 正在 Java 虚拟机中执行的线程处于这种状态. BLOCKED: 受阻塞并等待某个监视器锁的线程处于这种状态. WAITING: 无限期地等待另一个线程来执行某一特定操作的线程处于这种状态. TIMED_WAITING: 等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态. TERMINATED: 已退出的线程处于这种状态. 在给定时间点上,一个线程只能处于