Java JUC总结(长篇警告)

1、Java JUC简介

在 Java 5.0 提供了 java.util.concurrent (简称JUC )包,在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子系统,包括线程池、异步 IO 和轻量级任务框架。提供可调的、灵活的线程池。还提供了设计用于多线程上下文中的 Collection 实现等。

2、volatile关键字--内存可见性

下面由一段程序引出volatile关键字和内存可见性问题

package com.juc;


/**
 * 
 * 一、volatile关键字:
 *
 */
public class TestVolatile {

	
	public static void main(String[] args) {
		
		ThreadDemo td = new ThreadDemo();
		new Thread(td).start();
		
		while(true)
		{
			
				if(td.isFlag()) 
				{
					System.out.println("-----------------------");
					break;
				}
		
			
		}
	}
}

class ThreadDemo implements Runnable{
	
	
	private   boolean flag = false;

	@Override
	public void run() {
		
		
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		
		flag = true;
		
		System.out.println("flag="+isFlag());
	}
	public boolean isFlag() {
		return flag;
	}

	public void setFlag(boolean flag) {
		this.flag = flag;
	}
}

 

 

以上程序执行结果如下,那么就会发现,当线程td修改了flag的值为true、,但是主线程里面的while循环仍然没有退出,说明读取到的flag还是false,那么就相应的提出了内存可见性问题,当多个线程操作共享数据时,彼此时不可见的。

 

当我们程序运行,jvm会为执行任务每一个线程分配一个独立的缓存,用于提高效率

td线程负责修改数据:

          td线程修改flag时,首先回从主存拷贝一份数据flag=flase到自己的独立的缓存空间里面,然后修改flag的为true,最后将flag的值写入内存

main线程负责读取数据

          读取数据也是一样,读取主存的flag=false到自己独立的缓存空间里面,然后进行while循环,对flag的值进行判断。while调用相对底层代码,执行效率非常高,以至于主线程无法再次从主存里面读取数据

那么就有了内存可见性问题,当多个线程操作共享数据时,彼此时不可见的

 

那么以上问题如果可以让主线程每次都从主存里面读取数据,是不是就能退出循环呢,

那么这个问题可以使用synchronized 同步锁来解决这个问题,保证每次都会刷新主存,去主存里面读取数据

执行结果如下,说明每次都会从主存读取数据

 

但是使用了同步锁,效率大大的降低,多个线程进来的时候,还需要判断锁,当有线程使用的时候,还会造成阻塞问题,,那么这会就有了volatile关键字,

当多个线程进行操作共享数据时,可以保证内存中的数据可见。 可以理解为 他们进行操作直接在主存中进行,,每次读取和写都是在主存中进行。

那么在flag前面加上 volatile关键字的时候,执行结果如下

 

volatile关键字相较于synchronized是一种j较为轻量级的同步策略

volatile 不具备“互斥性” synchronized是互斥锁,当有一个线程获取到这个锁的时候,其他线程就会发生阻塞

volatile 不能保证变量的原子性,原子性意味着不可分割。

三、原子性操作解释

例如 i++; 这个操作,它不是一个原子性操作,在实际执行时需要三步操作“读-改-写”:

 

package com.juc;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 
 *	i++的原子性问题: i++的操作实际上分为三个步骤的:读-改-写
 *		int i = 10;
 *		i = i++;

 *		int temp = i;
 *		i = i+1;
 *		i = temp;
 *
 */
public class TestAtomicDemo {

	
	public static void main(String[] args) {
		 AtomicDemo ad = new AtomicDemo();
		 
		 for(int i = 0;i<10;i++)
		 {
			 new Thread(ad).start();
		 }
		
	}
	
}
class AtomicDemo implements Runnable
{

	
	private volatile int serialNumber = 0;
	
	//private AtomicInteger serialNumber = new AtomicInteger(0);
	@Override
	public void run() {
		
		
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			
			e.printStackTrace();
		}
		
		System.out.println(Thread.currentThread().getName()+":"+getSerialNumber());
	}

	public int getSerialNumber() {
		//return serialNumber.getAndIncrement();
		return serialNumber++;
	}
}

当线程1读取sn=0到自己的缓存空间中,进行的了加加操作,当没有写回到主存的时候,线程2也读取了sn=0;那么当线程1写回到主存打印出sn=1,当线程2也写回到主存也会打印出sn=1,

四、原子变量

1、类的小工具包,支持在单个变量上解除锁的线程安全编程。事实上,此包中的类可将 volatile 值、字段和数组元素的概念扩展到那些也提供原子条件更新操作的。
2、类 AtomicBoolean、AtomicInteger、AtomicLong 和 AtomicReference 的实例各自提供对相应类型单个变量的访问和更新。每个类也为该类型提供适当的实用工具方法。
3、AtomicIntegerArray、AtomicLongArray 和 AtomicReferenceArray 类进一步扩展了原子操作,对这些类型的数组提供了支持。这些类在为其数组元素提供 volatile 访问语义方面也引人注目,这对于普通数组来说是不受支持的。
4、核心方法:boolean compareAndSet(expectedValue, updateValue)
5、java.util.concurrent.atomic 包下提供了一些原子操作的常用类:
              AtomicBoolean 、AtomicInteger 、AtomicLong 、 AtomicReference
              AtomicIntegerArray 、AtomicLongArray
              AtomicMarkableReference
              AtomicReferenceArray
              AtomicStampedReference
6、原子变量的特征

          1、volatile 保证内存可见性

          2、CAS(compare-and-swap) 算法保证数据的原子性

package com.juc;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 
 *	i++的原子性问题: i++的操作实际上分为三个步骤的:读-改-写
 *		int i = 10;
 *		i = i++;

 *		int temp = i;
 *		i = i+1;
 *		i = temp;
 *
 */
public class TestAtomicDemo {

	
	public static void main(String[] args) {
		 Ato
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值