java并发深入

对象由数据+行为组成。数据就是字段,行为就是方法。

并发需要保证这些可被多个线程访问的共享对象数据的完整性,以及某些特定完整性语义。

比如一个类有一个字段count=0,两个线程同时对它做加1操作。

这时就有可能发生:

 线程1查询到count为1对其加1。

 线程2查询到count为1,对其加1。

  接着线程1提交,线程2提交。

最终值count还是为1。

也就是说线程1对count的修改丢失了。

解决这个问题,需要加锁。

java提交了内置锁syncronized,以及Lock。

内置锁syncronized,利用monitorEnter及monitorExit两条指令保证数据的可见性与原子性。

比如A类有一个字段count,默认值为0,代码如下:

public class A{

private int count=0;

public syncronized add(){

count++;

}

}

线程一首先调用add方法,这时会发生以下步骤:

1.线程二尝试获取在当前A实例上的锁,没有获取到则阻塞

2.获取到锁后,将count值从主存拷到当前线程工作内存,这时count为0

线程二这时执行add方法,但发现获取不到锁,这时阻塞在那边。

线程一执行完加1后,退出解锁。

这时线程二就可以获取到锁了。

并发中对于某些集合,要使它成为同步类,我们常常使用封装,如下:

class SyncMap{

Map<String,String> maps=new HashMap<String,String>();

public syncronized V put(K key, V value){

maps.put(key,value);

}

}

这样做的优点是不管底层maps有无同步,同步策略是什么,都可以安全的实现同步。

还有一种实现同步的方法,即将需要同步的操作交由已经存在的同步类来做。

考虑上面的count加1操作,如果将count类型改成AtomicInteger,由AtomicInteger实现同步,原子加1操作。

atomic

===============atomic all finish,cost:247,the res:3000000

===============atomic all finish,cost:248,the res:3000000

===============atomic all finish,cost:262,the res:3000000

===============atomic all finish,cost:239,the res:3000000

===============atomic all finish,cost:249,the res:3000000

sync

===============sync all finish,cost:54,the res:3000000

===============sync all finish,cost:45,the res:3000000

===============sync all finish,cost:47,the res:3000000

===============sync all finish,cost:45,the res:3000000

===============sync all finish,cost:49,the res:3000000

测试表明上述对于300个线程,每个线程做10000次加1操作,内置锁syncronized比atomicInteger效率要高

测试代码如下:

public class SyncWithAtomicTest {

	private int count=0;

	private static final int threadCount=300;

	private static final int countNum=10000;

	private final AtomicInteger countAtomicInteger=new AtomicInteger(0);

	private static final ExecutorService threadPool=Executors.newFixedThreadPool(threadCount);

	private final CountDownLatch latchStart=new CountDownLatch(threadCount);

	private final CountDownLatch latchEnd=new CountDownLatch(threadCount);

	public synchronized void addWithCountSync(){
		for(int i=0;i<countNum;i++){
			count++;
		}
	}

	public void addWithAtomicCount(){
		for(int i=0;i<countNum;i++){
			countAtomicInteger.incrementAndGet();
		}
	}

	public static void main(String[] args) throws InterruptedException {

		SyncWithAtomicTest obj=new SyncWithAtomicTest();

		Long oldTime=System.currentTimeMillis();

		for(int i=0;i<threadCount;i++){
			CountTask t=new CountTask();
			t.setTarget(obj);

			threadPool.execute(t);
		}

		obj.latchEnd.await();

		Long endTime=System.currentTimeMillis()-oldTime;

//		System.out.println("===============atomic all finish,cost:"+endTime+",the res:"+obj.countAtomicInteger.get());

		System.out.println("===============sync all finish,cost:"+endTime+",the res:"+obj.count);
	}

	static class CountTask implements Runnable{
		private SyncWithAtomicTest target;

		public void run() {
			try {
				target.latchStart.countDown();
				target.latchStart.await();

				//we do add oper when all threads is ready
				target.addWithCountSync();

//				target.addWithAtomicCount();

				System.out.println("thread:"+Thread.currentThread().getId()+",finish the work");

				target.latchEnd.countDown();

			} catch (InterruptedException e) {
				e.printStackTrace();
				Thread.currentThread().interrupt();
			}
		}

		public void setTarget(SyncWithAtomicTest target) {
			this.target = target;
		}
	}

}

java并发深入

时间: 2024-11-06 10:14:46

java并发深入的相关文章

Java并发编程:Concurrent锁机制解析

.title { text-align: center } .todo { font-family: monospace; color: red } .done { color: green } .tag { background-color: #eee; font-family: monospace; padding: 2px; font-size: 80%; font-weight: normal } .timestamp { color: #bebebe } .timestamp-kwd

Java并发编程:Callable、Future和FutureTask(转)

Java并发编程:Callable.Future和FutureTask 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果. 如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦. 而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果. 今天我们就来讨论一下Callabl

Java并发编程 Volatile关键字解析

volatile关键字的两层语义 一旦一个共享变量(类的成员变量.类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的. 2)禁止进行指令重排序. 根据volatile的语义,我们可以看到,volatile主要针对的是并发三要素(原子性,可见性和有序性)中的后两者有实际优化作用. 可见性: 线程本身并不直接与主内存进行数据的交互,而是通过线程的工作内存来完成相应的操作.

Java并发编程

synchronized是Java中的关键字,在并发编程中被称为内置锁或者监视器锁.当用它来修饰一个方法或者一个代码块的时候能够保证同一时刻最多只有一个线程执行该段代码. Java的内置锁相当于一种互斥锁,最多只有一个线程能够持有这种锁,故而由这个锁保护的同步代码块会以原子方式执行,多个线程在执行该代码时就不会相互干扰. 但由于被锁保护的同步块代码是以串行形式来访问的,即多个线程以独占的方式访问对象,而这也导致如果被锁保护的同步代码块的作用范围过大,会导致并发不良. 这里有必要简单讲一下内置锁的

【死磕Java并发】-----J.U.C之重入锁:ReentrantLock

此篇博客所有源码均来自JDK 1.8 ReentrantLock,可重入锁,是一种递归无阻塞的同步机制.它可以等同于synchronized的使用,但是ReentrantLock提供了比synchronized更强大.灵活的锁机制,可以减少死锁发生的概率. API介绍如下: 一个可重入的互斥锁定 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义,但功能更强大.ReentrantLock 将由最近成功获得锁定,并且还没有释放该锁定的线程所拥

JAVA并发-为现有的线程安全类添加原子方法

JAVA中有许多线程安全的基础模块类,一般情况下,这些基础模块类能满足我们需要的所有操作,但更多时候,他们并不能满足我们所有的需要.此时,我们需要想办法在不破坏已有的线程安全类的基础上添加一个新的原子操作.有如下4中方案: 1 修改类的源码,以添加新的原子操作 2 继承该线程安全类,并添加原子操作 3 使用客户端加锁方式 4 使用组合方式(推荐) 一般来讲,修改源码的方式不太可行,这样会破坏原有类的封装性而且有些时候,源码不可获得.我们从第二种方式开始举例: 假设现在对于类Vector,我们知道

6、Java并发编程:volatile关键字解析

Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以重获生机. volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情.由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识,然后分析了volatil

7、Java并发编程:深入剖析ThreadLocal

Java并发编程:深入剖析ThreadLocal 想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨下ThreadLocal的使用方法和实现原理.首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码分析了其实现原理和使用需要注意的地方,最后给出了两个应用场景. 以下是本文目录大纲: 一.对ThreadLocal的理解 二.深入解析ThreadLocal类 三.ThreadLocal的应用场景 若有不正之处请多多谅解,并欢迎批评指正. 请尊重作者

java并发编程实战学习(3)--基础构建模块

转自:java并发编程实战 5.3阻塞队列和生产者-消费者模式 BlockingQueue阻塞队列提供可阻塞的put和take方法,以及支持定时的offer和poll方法.如果队列已经满了,那么put方法将阻塞直到空间可用:如果队列为空,那么take方法将阻塞直到有元素可用.队列可以是有界的也可以是无界的. 如果生产者生成工作的速率比消费者处理工作的速率款,那么工作项会在队列中累计起来,最终好紧内存.同样,put方法的阻塞特性也极大地简化了生产者的编码.如果使用有界队列,当队列充满时,生产者将阻

JAVA并发总结-基础篇

多线程 1. java中有几种方法可以实现一个线程? 继承Thread类,实现Runnable接口创建一个线程的唯一方法是实例化java.lang.Thread类(或其子类),并调用其start()方法 2. 如何停止一个正在运行的线程? 调用ThreadInstanceA.inerrupt()方法,这样当A线程在Thread的sleep,join方法,或者Object的wait方法的时候会直接抛出InerruptedException,捕捉后便可退出. public void shutdown