Java 并发包应用

一、Java JUC

​ java5.0 提供的 java.util.concurrent 包,其中增加了并发编程中常见的工具类。

​ eg: 线程池、异步IO 和 轻量级任务框架

​ 提供可调的、灵活的线程池,以及提供了设计用于多线程上下文中的 Collection 实现。

二、volatile ——解决内存可见性

内存可见性(Memory Visibility)指当某个线程 A 正在使用对象状态而另一个线程 B 在同时修改该状态,需要确保当一个线程 B 修改了对象状态后,其他线程(例如 A)能够看到发生的状态变化。

解决方法:
① 通过同步来保证对象被安全地发布(synchronized 关键字);
② 使用更加轻量的 volatile 变量

volatile 和 synchronized 区别:

volatile 使用了线程中内存栅栏实时从主存中进行数据刷新;而 synchronized 同步变量时,由于JVM 底层的重排序,所以性能比较低

注意:1. volatile 不具备“互斥性”;2. volatile 不能保证变量的“原子性”

public class TestVolatile {
	
	public static void main(String[] args) {
		ThreadDemo threadDemo = new ThreadDemo();
		// 启动线程
		new Thread(threadDemo).start();
		
		while(true) {
// 方式二: 在要操作的共享数据前加 volatile 关键字
			// 保证数据可见性
			if (threadDemo.isFlag()) {
				System.out.println("volatile result --->");
				break;
			}			
			
// 方式一: 直接加同步锁, 性能低			
			// 内存可见性问题: 当多个线程操作共享数据时, 彼此不可见
			// 对新建的线程进行加同步锁, 使得每次都从主存中获取数据
			// 耗费性能
/*			synchronized (threadDemo) {
				if (threadDemo.isFlag()) {
					System.out.println("--->");
					break;					
				}
			}
*/
		}
	}
}

class ThreadDemo implements Runnable {
	private volatile boolean flag = false;
	
	@Override
	public void run() {
		flag = true;
		System.out.println("falg = " + flag);
	}
	
	public boolean isFlag() {
		return flag;
	}	
}

三、原子变量 CAS 算法

3.1 CAS 算法

CAS(Compare-And-Swap)是一种硬件对并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并发访问。

CAS 是一种无锁的非阻塞算法的实现。

CAS 包含了三个操作数:

  • 内存值 V; 预估值 A; 更新值 B
  • 当且仅当 V == A 时, V = B. 否则将不做任何操作

CAS 算法效率比锁的效率高, 因为更新失败的时候不会放弃 CPU 时间片

"读-改-写"

3.2 原子变量

类的小工具包,支持在单个变量上解除锁的线程安全编程。事实上,此包中的类可将volatile 值、字段和数组元素的概念扩展到那些也提供原子条件更新操作的类。

AtomicBoolean、AtomicInteger、AtomicLong 和AtomicReference 的实例各自提供对相应类型单个变量的访问和更新。每个类也为该类型提供适当的实用工具方法。

AtomicIntegerArray、AtomicLongArray 和AtomicReferenceArray 类进一步扩展了原子操作,对这些类型的数组提供了支持。这些类在为其数组元素提供 volatile 访问语义方面也引人注目,这对于普通数组来说是不受支持的。

核心方法:boolean compareAndSet(expectedValue, updateValue)

java.util.concurrent.atomic 包下提供了一些原子操作的常用类

public class TestAtomicDemo {
	public static void main(String[] args) {
		AtomicDemo atomicDemo = new AtomicDemo();
		
		for (int i = 0; i < 20; i++) {
			Thread thread = new Thread(atomicDemo);
			thread.start();
		}
	}	
}

class AtomicDemo implements Runnable {
//	private int serialNumber = 0;
	private AtomicInteger serialNumber = new AtomicInteger(0);
	
	@Override
	public void run() {
		try {
			// 线程休眠 200 毫秒
			Thread.sleep(200);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + ": " + getSerialNumber());
	}
	
	public int getSerialNumber() {
//		return serialNumber++;
		return serialNumber.getAndIncrement();
	}
}

四、ConcurrentHashMap

采用 “锁分段” 机制

– concurrentLevel 锁级别: 默认16个段;

每个段都是一个锁, 每个段有一个 hash 表
并行

CopyOnWriteArrayList/CopyOnWriteArraySet: “写入并复制”

注意:
​ · 添加操作多不适合使用, 效率低, 每次添加操作时会先做复制
​ · 并发迭代操作多时, 可以使用

-> 用法与 HashMap 相同

五、CountDownLatch 闭锁操作

CountDownLatch 是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

闭锁可以延迟线程的进度直到其到达终止状态,闭锁可以用来确保某些活动直到其他活动都完成才继续执行:

  • 确保某个计算在其需要的所有资源都被初始化之后才继续执行;
  • 确保某个服务在其依赖的所有其他服务都已经启动之后才启动;
  • 等待直到某个操作所有参与者都准备就绪再继续执行。
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.CountDownLatch;

/**
 * CountDownLatch: 闭锁, 在完成某些运算时, 只有当其他所有线程的运算全部完成, 当前计算才继续运行
 * 
 * 
 * @author Jashon
 * @time 2019-02-07 19:22:03
 * @version 1.0
 *
 */
public class TestCountDownLatch {

	public static void main(String[] args) {
		final CountDownLatch latch = new CountDownLatch(5);
		
		Instant start = Instant.now();
		
		LatchDemo latchDemo = new LatchDemo(latch);
		
		for (int i = 0; i < 5; i++) {
			new Thread(latchDemo).start();
		}
		try {
			// 主线程进入等待, 直到 latch 为 0, 此方法立即返回 
			latch.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		Instant end = Instant.now();
		Duration duration = Duration.between(start, end);
		long seconds = duration.getSeconds();
		System.out.println("耗费时间: " + seconds + " s");
	}
}

class LatchDemo implements Runnable {

	private CountDownLatch latch;
	
	public LatchDemo(CountDownLatch latch) {
		this.latch = latch;
	}

	@Override
	public void run() {
		synchronized (this) {
			try {
				for(int i = 0; i < 50000; i++) {
					if(i % 2 == 0) {
						System.out.println(i);
					}
				}
			} finally {
				// 将维护的变量减 1
				latch.countDown();
			}
		}
	}
}

六、Callable 接口

Callable 需要依赖 FutureTask ,FutureTask 也可以用作 闭锁操作

Callable 接口类似于Runnable,但是Runnable 不会返回结果,并且无法抛出经过检查的异常。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * 一、创建执行线程的方式三:实现  Callable 接口
 * 		相较于 Runable 接口的方式, 方法可以有返回值, 并且可以抛出异常
 * 
 * 二、执行Callable 方式, 需要 FutureTask 实现类的支持, 用于接收运算结果。
 * 		FutureTask 是 Future 的实现类
 * 
 * @author Jashon
 * @time 2019-02-07 19:50:30
 * @version 1.0
 *
 */
public class TestCallable {

	public static void main(String[] args) {
		ThreadDemo1 threadDemo = new ThreadDemo1();
		
		FutureTask<Integer> result = new FutureTask<>(threadDemo);
		
		new Thread(result).start();
		
		try {
			// 接受线程运算后的结果
			// 相当于一个 闭锁 操作, 可用于 闭锁
			Integer sum = result.get();
			System.out.println(sum);
			System.out.println("------------------------");
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}		
	}
}

class ThreadDemo1 implements Callable<Integer> {
	@Override
	public Integer call() throws Exception {
		int sum = 0;
		
		for (int i = 0; i <= 100; i++) {
			sum += i;
			System.out.println(i);
		}
		return sum;
	}
}

七、同步锁 Lock

Java 5.0 之前,协调共享对象的访问时可以使用的机制只有 synchronized 和 volatile 。Java 5.0 后增加了一些新的机制,但并不是一种替代内置锁的方法,而是当内置锁不适用时,作为一种可选择的高级功能。

**ReentrantLock 实现了Lock 接口,并提供了与 synchronized 相同的 互斥性 和 内存可见性。**但相较于 synchronized 提供了更高的处理锁的灵活性。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 一、用于解决多线程安全问题的方式:
 * 		synchronized: 隐式锁
 * 		1. 同步代码块
 * 		2. 同步方法
 * 
 * jdk 1.5 后:
 * 	3. 同步锁 lock
 * 
 * 注意: 是一个显示锁, 需要通过 lock() 方法上锁, 
 * 		必须通过 unlock() 方法进行释放锁(一般放于finally{ .unlock(); })
 * 
 * @author Jashon
 * @time 2019-02-07 20:06:03
 * @version 1.0
 */
public class TestLock {

	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		new Thread(ticket, "售票口1").start();
		new Thread(ticket, "售票口2").start();
		new Thread(ticket, "售票口3").start();
	}
}


class Ticket implements Runnable {
    
	private int tick = 100;
	
	// 创建显示锁
	public Lock lock = new ReentrantLock();
	
	@Override
	public void run() {
		while(true) {
			lock.lock();
			
			try {
				if(tick > 0) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
					System.out.println(Thread.currentThread().getName() + "完成售票, 余票为:" + --tick);
				}
			}finally {
				lock.unlock();
			}
		}
	}	
}

八、Condition 控制线程通信(与 Lock 一同使用)

Condition 接口描述了可能会与锁有关联的条件变量。这些变量在用法上与使用 Object.wait 访问的隐式监视器类似,但提供了更强大的功能。

特别的是,单个Lock 可能与多个Condition 对象关联。为了避免兼容性问题,Condition 方法的名称与对应的Object 版本中的不同。

Lock 同步锁中,Condition 对象中,与wait、notify、notifyAll 方法对应的分别是await、signal、signalAll

Condition 实例实质上被绑定到一个锁上。要为特定Lock 实例获得Condition 实例,使用其 newCondition() 方法。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 使用 Lock 同步锁实现交替切换线程
 * 
 * @author Jashon
 * @time 2019-02-08 20:22:35
 * @version 1.0
 *
 */
public class TestABCAlternate {

	public static void main(String[] args) {
		AlternateDemo alternateDemo = new AlternateDemo();
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				for(int i = 0; i <=20; i++) {
					alternateDemo.loopA(i);
				}
			}
		}, "A").start();
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				for(int i = 0; i <=20; i++) {
					alternateDemo.loopB(i);
				}
			}
		}, "B").start();
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				for(int i = 0; i <=20; i++) {
					alternateDemo.loopC(i);
				}
			}
		}, "C").start();
	}
}

class AlternateDemo {
	private int number = 1;		// 当前线程执行的标记
	
	private Lock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();
	private Condition condition1 = lock.newCondition();
	private Condition condition2 = lock.newCondition();
	
	public void loopA(int totalLoop) {
		lock.lock();
		try {
			// 1. 判断
			if(number != 1) {
				condition.await();
			}
			
			// 2. 打印
			for (int i = 0; i < 5; i++) {
				System.out.println(Thread.currentThread().getName() + "\t " + i + "\t" + totalLoop);
			}
			
			// 3. 唤醒
			number = 2;
			condition1.signal();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void loopB(int totalLoop) {
		lock.lock();
		try {
			// 1. 判断
			if(number != 2) {
				condition1.await();
			}
			
			// 2. 打印
			for (int i = 0; i < 5; i++) {
				System.out.println(Thread.currentThread().getName() + "\t " + i + "\t" + totalLoop);
			}
			
			// 3. 唤醒
			number = 3;
			condition2.signal();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void loopC(int totalLoop) {
		lock.lock();
		try {
			// 1. 判断
			if(number != 3) {
				condition2.await();
			}
			
			// 2. 打印
			for (int i = 0; i < 5; i++) {
				System.out.println(Thread.currentThread().getName() + "\t " + i + "\t" + totalLoop);
			}
			
			// 3. 唤醒
			number = 1;
			condition.signal();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
}

九、ReadWriteLock 读写锁

ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有writer,读取锁可以由多个 reader 线程同时保持。写入锁是独占的

ReadWriteLock 读取操作通常不会改变共享资源,但执行写入操作时,必须独占方式来获取锁。对于读取操作占多数的数据结构。ReadWriteLock 能提供比独占锁更高的并发性。而对于只读的数据结构,其中包含的不变性可以完全不需要考虑加锁操作。

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * ReadWriteLock: 读写锁
 * 
 * 		写写/读写: 互斥
 * 		
 * 		读读: 不需要互斥
 * 
 * @author Jashon
 * @time 2019-02-08 22:31:44
 * @version 1.0
 *
 */
public class TestReadAndWrite {
	
	public static void main(String[] args) {
		ReadWriteLockDemo readWriteLockDemo = new ReadWriteLockDemo();
		
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				readWriteLockDemo.set(10);
			}
		}, "WRITE:").start();
		
		for (int i = 0; i < 20; i++) {
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					readWriteLockDemo.get();
				}
			}).start();
		}
	}
}

class ReadWriteLockDemo {
	private int number = 0;
	
	private ReadWriteLock lock = new ReentrantReadWriteLock();
	// 读
	public void get() {
		lock.readLock().lock();
		try {
			System.out.println(Thread.currentThread().getName() + " : " + number);
		} finally {
			lock.readLock().unlock();
		}
	}
	
	// 写
	public void set(int number) {
		lock.writeLock().lock();
		try {
			System.out.println(Thread.currentThread().getName());
			this.number = number;
		} finally {
			lock.writeLock().unlock();
		}
	}
}

十、线程池

  1. **线程池:**提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提高了响应的速度。

  2. 线程池的体系结构:
    java.util.concurrent.Executor : 负责线程的使用与调度的根接口
    ​ |–**ExecutorService 子接口: 线程池的主要接口
    ​ |–ThreadPoolExecutor 线程池的实现类
    ​ |–ScheduledExecutorService 子接口:负责线程的调度
    ​ |–ScheduledThreadPoolExecutor :继承 ThreadPoolExecutor, 实现 ScheduledExecutorService

  3. 工具类 : Executors
    ExecutorService newFixedThreadPool() : 创建固定大小的线程池
    ExecutorService newCachedThreadPool() : 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。
    ExecutorService newSingleThreadExecutor() : 创建单个线程池。线程池中只有一个线程

    ScheduledExecutorService newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时的执行任务。

public class TestThreadPool {
	
	public static void main(String[] args) throws Exception {
		//1. 创建线程池
		ExecutorService pool = Executors.newFixedThreadPool(5);
		
		List<Future<Integer>> list = new ArrayList<>();
		
		for (int i = 0; i < 10; i++) {
			Future<Integer> future = pool.submit(new Callable<Integer>(){
				@Override
				public Integer call() throws Exception {
					int sum = 0;
					
					for (int i = 0; i <= 100; i++) {
						sum += i;
					}
					return sum;
				}
			});
			list.add(future);
		}
		
		pool.shutdown();
		
		for (Future<Integer> future : list) {
			System.out.println(future.get());
		}
		
		/*ThreadPoolDemo tpd = new ThreadPoolDemo();
		
		//2. 为线程池中的线程分配任务
		for (int i = 0; i < 10; i++) {
			pool.submit(tpd);
		}
		
		//3. 关闭线程池
		pool.shutdown();*/
	}
	
//	new Thread(tpd).start();
//	new Thread(tpd).start();
}

class ThreadPoolDemo implements Runnable{

	private int i = 0;
	
	@Override
	public void run() {
		while(i <= 100){
			System.out.println(Thread.currentThread().getName() + " : " + i++);
		}
	}	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值