1. java.util.concurrent(简称JUC )
在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子系统,包括线程池、异步IO 和轻量级任务框架。提供可调的、灵活的线程池。还提供了设计用于多线程上下文中的Collection 实现等
1.1 synchronized
-
修饰普通方法,内置锁是当前类的实例;
-
修饰静态方法,内置锁是当前类Class字节码对象,也就是 Xxx.class
-
修饰静态代码块
1.2 生产者消费者案例
package com.isoftstone.mythread;
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor productor = new Productor(clerk);
Consumer consumer = new Consumer(clerk);
new Thread(productor,"生产者A").start();
new Thread(productor,"生产者B").start();
new Thread(consumer,"消费者a").start();
new Thread(consumer,"消费者b").start();
}
}
class Clerk {//店员
private int product = 0;
//进货
public synchronized void get(){
//这里不能使用if,避免虚假唤醒,应该总是使用在循环中
while(product >= 10) {
System.out.println("货物已满!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "--进货,货物个数变为"+ ++product);
this.notifyAll();
}
//卖货
public synchronized void sale(){
//这里不能使用if,避免虚假唤醒,应该总是使用在循环中
while(product <= 0) {
System.out.println("货物为空!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "--卖货,货物个数变为"+ --product);
this.notifyAll();
}
}
class Productor implements Runnable {//生产者
private Clerk clerk; //店员
public Productor(Clerk clerk) {
this.clerk = clerk;
}
public void run() {
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.get();
}
}
}
class Consumer implements Runnable {//消费者
private Clerk clerk; //店员
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
public void run() {
while(true) {
try {
Thread.sleep(10009);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.sale();
}
}
}
1.3 volatile 内存可见性
-
当一个线程修改了对象状态后,其他线程能够看到发生的状态变化
-
不具备互斥性,也不能保证变量状态的“原子性操作”
1.4 原子变量 CAS算法
- Compare And Swap 多线程情况下,假设原来的值是0,当最快的一个线程访问时候会查看这个值是否发生了变化,如果还是0,则值变为1;而对于第二名的线程过来时发现值已经改变,则会拿到这个值改变的值给自己,假设为1,继续下一轮的尝试;如果下一轮值没有变化,则值变为2;如果还是慢了,则继续拿到这个当前值进行下一轮的尝试
package com.isoftstone.mythread;
import java.util.concurrent.atomic.AtomicInteger;
public class TestAtomic {
public static void main(String[] args) {
AtomicDemo demo = new AtomicDemo();
for (int i = 0; i < 30; i++) {
new Thread(demo).start();
}
}
}
class AtomicDemo implements Runnable {
//private volatile int serialNum = 0;
private AtomicInteger serialNum = new AtomicInteger();
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getSerialNum());
}
public int getSerialNum() {
//获取原来的值并且增加1
return serialNum.getAndIncrement();
}
}
1.5 ConcurrentHashMap 锁分段机制
内部采用“锁分段”机制替代Hashtable 的独占锁。进而提高性能
1.6 CopyOnWriteArrayList/CopyOnWriteArraySet
-
写入并复制操作;
-
添加操作多时,效率低,因为每次添加都要进行复制,开销很大;
-
并发迭代操作多时可以选择
public static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
1.7 CountDownLatch 闭锁
-
确保某个计算在其需要的所有资源都被初始化之后才继续执行;
-
确保某个服务在其依赖的所有其他服务都已经启动之后才启动;
-
等待直到某个操作所有参与者都准备就绪再继续执行
package com.isoftstone.mythread;
import java.util.concurrent.CountDownLatch;
public class TestCountDownLatch {
public static void main(String[] args) {
int threadCount = 5;
final CountDownLatch latch = new CountDownLatch(threadCount);
LatchDemo demo = new LatchDemo(latch);
long start = System.currentTimeMillis();
for (int i = 0; i < threadCount; i++) {
new Thread(demo).start();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("耗费的时间=" + (end - start));
}
}
class LatchDemo implements Runnable {
private CountDownLatch latch;
public LatchDemo(CountDownLatch latch) {
this.latch = latch;
}
public void run() {
synchronized (this) {
try {
for (int i = 0; i < 50000; i++) {
if (i%2 == 0) {
System.out.println(i);
}
}
} catch (Exception e) {
} finally {
latch.countDown();
}
}
}
}
1.8 Callable 接口
Callable 接口类似于Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是Runnable 不会返回结果,并且无法抛出经过检查的异常。
package com.isoftstone.mythread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class TestCallable {
public static void main(String[] args) {
ThreadDemo demo = new ThreadDemo();
FutureTask<Integer> task = new FutureTask<Integer>(demo);
new Thread(task).start();
try {
//接收线程运算后的结果
//只有线程都结束后才执行get方法,也就是说FutureTask可以用于闭锁
Integer sum = task.get();
System.out.println("sum="+sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class ThreadDemo implements Callable<Integer> {
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum += i;
}
return sum;
}
}
1.9 Lock 同步锁
1.9.1 Lock版 卖票案例
package com.isoftstone.mythread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
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;
private Lock myLock = new ReentrantLock();
public void run() {
while(true) {
myLock.lock();
try {
if (tick > 0) {
System.out.println(Thread.currentThread().getName() + "*****完成售票,余票=" + --tick);
} else {
return;
}
} catch (Exception e) {
} finally {
myLock.unlock();
}
}
};
}
1.9.2 Lock版 生产者消费者案例
package com.isoftstone.mythread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestProductAndConsumerWithLock {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor productor = new Productor(clerk);
Consumer consumer = new Consumer(clerk);
new Thread(productor,"生产者A").start();
new Thread(productor,"生产者B").start();
new Thread(consumer,"消费者a").start();
new Thread(consumer,"消费者b").start();
}
}
class Clerk {//店员
private int product = 0;
private Lock myLock = new ReentrantLock();
private Condition myCondition = myLock.newCondition();//建立通信
//进货
public void get(){
myLock.lock();
try {
//这里不能使用if,避免虚假唤醒,应该总是使用在循环中
while(product >= 10) {
System.out.println("货物已满!");
try {
myCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "--进货,货物个数变为"+ ++product);
myCondition.signalAll();
} catch (Exception e) {
} finally {
myLock.unlock();
}
}
//卖货
public void sale(){
myLock.lock();
try {
//这里不能使用if,避免虚假唤醒,应该总是使用在循环中
while(product <= 0) {
System.out.println("货物为空!");
try {
myCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "--卖货,货物个数变为"+ --product);
myCondition.signalAll();
} catch (Exception e) {
} finally {
myLock.unlock();
}
}
}
class Productor implements Runnable {//生产者
private Clerk clerk; //店员
public Productor(Clerk clerk) {
this.clerk = clerk;
}
public void run() {
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.get();
}
}
}
class Consumer implements Runnable {//消费者
private Clerk clerk; //店员
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
public void run() {
while(true) {
try {
Thread.sleep(10009);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.sale();
}
}
}
1.9.3 Lock版 线程按序交替执行
package com.isoftstone.mythread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestABCAlternate {
//编写程序
//开启3个线程,这三个线程ID分别为A、B、C,
//每个线程将自己的ID在屏幕打印10遍,
//要求输出的结果必须按顺序显示,
//如 ABCABCABCABCABCABCABCABCABCABC
public static void main(String[] args) {
AlternateDemo demo = new AlternateDemo();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
demo.loop1();
}
}
},"A").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
demo.loop2();
}
}
},"B").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
demo.loop3();
}
}
},"C").start();
}
}
class AlternateDemo {
private int currThreadFlag = 1; //当前正在执行线程的标记
private Lock myLock = new ReentrantLock();
private Condition myCondition1 = myLock.newCondition();
private Condition myCondition2 = myLock.newCondition();
private Condition myCondition3 = myLock.newCondition();
public void loop1() {
myLock.lock();
try {
if (currThreadFlag != 1) {
try {
myCondition1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(Thread.currentThread().getName());
currThreadFlag = 2;
myCondition2.signal();
} catch (Exception e) {
} finally {
myLock.unlock();
}
}
public void loop2() {
myLock.lock();
try {
if (currThreadFlag != 2) {
try {
myCondition2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(Thread.currentThread().getName());
currThreadFlag = 3;
myCondition3.signal();
} catch (Exception e) {
} finally {
myLock.unlock();
}
}
public void loop3() {
myLock.lock();
try {
if (currThreadFlag != 3) {
try {
myCondition3.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(Thread.currentThread().getName());
currThreadFlag = 1;
myCondition1.signal();
} catch (Exception e) {
} finally {
myLock.unlock();
}
}
}
1.10 读写锁 ReadWriteLock
-
ReadWriteLock 维护了一对相关的锁,一个用于只读操作, 另一个用于写入操作
-
只要没有 writer,读取锁可以由 多个 reader 线程同时保持。写入锁是独占的
-
ReadWriteLock 读取操作通常不会改变共享资源,但执行 写入操作时,必须独占方式来获取锁
-
对于读取操作占多数的数据结构。 ReadWriteLock 能提供比独占锁更高 的并发性
-
而对于只读的数据结构,其中包含的不变性可以完全不需要考虑加锁操作
package com.isoftstone.mythread;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class TestReadWriteLock {
public static void main(String[] args) {
ReadWriteLockDemo demo = new ReadWriteLockDemo();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i > 10) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
demo.read();
}
}
},"Read").start();
new Thread(new Runnable() {
@Override
public void run() {
demo.write("新的");
}
},"Write").start();
}
}
class ReadWriteLockDemo {
private String word = "原来";
private ReadWriteLock myLock = new ReentrantReadWriteLock();
public void read() {
myLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "读取内容:" + word);
} catch (Exception e) {
} finally {
myLock.readLock().unlock();
}
}
public void write(String word) {
myLock.writeLock().lock();
try {
this.word = word;
System.out.println(Thread.currentThread().getName() + "写入内容:" + word);
} catch (Exception e) {
} finally {
myLock.writeLock().unlock();
}
}
}
1.11 线程八锁
-
非静态方法的锁默认为this ;静态方法的锁为Class实例
-
对于同一把锁而言,某一个时刻内,只能有一个线程持有这个锁,无论有几个方法
-
锁不同,则互不影响;有锁与无锁也互不影响;
2 线程池
线程池提供了一个线程队列。队列中保存着所有等待状态的线程,避免创建与销毁的额外开销,提高了响应的速度。
2.1 线程池的体系结构
2.2 线程池的工具类Executors
2.2.1线程池分配多个任务,进行求和
package com.isoftstone.mythread;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class TestTheadPool {
public static void main(String[] args) {
//Executors.newFixedThreadPool(nThreads); //创建固定大小的线程池
//Executors.newCachedThreadPool(); //创建缓存线程池,自动更改数量
//Executors.newSingleThreadExecutor(); //创建单个线程
ExecutorService executorService = Executors.newFixedThreadPool(5);
//两个Callable接口实现类
ThreadPoolDemo demo = new ThreadPoolDemo();
ThreadPoolDemo2 demo2 = new ThreadPoolDemo2();
//为线程池分配20个任务
List<Future<Integer>> futureList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Future<Integer> future = executorService.submit(demo);
futureList.add(future);
}
for (int i = 0; i < 10; i++) {
Future<Integer> future = executorService.submit(demo2);
futureList.add(future);
}
//归还到线程池
executorService.shutdown();
//遍历结果
for (Future<Integer> future : futureList) {
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
class ThreadPoolDemo implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum += i;
}
return sum;
}
}
class ThreadPoolDemo2 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName());
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum -= i;
}
return sum;
}
}
2.2.2 线程池的秒杀业务
package com.isoftstone.demo;
import java.util.concurrent.*;
public class MyTaskTest {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(30);
for (int i = 1; i <= 200; i++) { //这里假设有200个人参与秒杀
pool.submit(new MyTask("customer" + i));
}
pool.shutdown();
}
}
class MyTask implements Runnable {
private static int googsNum = 10; //商品初始数量
private String customerName; //客户名称
public MyTask(String customerName) {
this.customerName = customerName;
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
synchronized (MyTask.class) {
if (googsNum > 0) {
System.out.println(customerName + ",通过线程"+threadName+"抢到第"+googsNum+"个商品");
googsNum--;
} else {
System.out.println(customerName + ",通过线程"+threadName+"秒杀任务失败。。。");
}
}
}
}
2.2.3 线程池的ATM机取款业务
package com.isoftstone.demo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
public class MyATMTest {
public static void main(String[] args) {
//3台ATM机
ExecutorService pool = Executors.newFixedThreadPool(3, new ThreadFactory() {
private int id = 1;
@Override
public Thread newThread(Runnable r) {
return new Thread(r,"ATM" + id++);
}
});
for (int i = 0; i < 5; i++) { //5个取款用户取同一账号的钱
pool.submit(new CustomerDemo("c-" + i,300));
}
pool.shutdown();
}
}
class CustomerDemo implements Runnable {
private static double total = 1000; //假设存入银行1000元
private static double withdrawalAmount; //取款金额
private String customerName; //客户名称
public CustomerDemo(String customerName,double withdrawalAmount) {
this.customerName = customerName;
this.withdrawalAmount = withdrawalAmount;
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println(customerName + "开始准备取钱。。。");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (CustomerDemo.class) {
if (total >= withdrawalAmount) {
total -= withdrawalAmount;
System.out.println(customerName +"通过取款机"+threadName+"取钱成功,余额=" + total);
} else {
System.out.println(customerName +"通过取款机"+threadName+"取钱失败,余额=" + total);
}
}
}
}
2.3 线程池的调度
2.3.1 ScheduledExecutorService.schedule
package com.isoftstone.mythread;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class TestScheduledThreadPool {
public static void main(String[] args) {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
try {
for (int i = 0; i < 10; i++) {
//每隔3秒执行一次,且可以有返回值
//如果当前线程的执行时间没有超过3秒,则按照间隔3秒执行下一次
//如果当前线程的执行时间超过3秒,则按照当前线程执行时间算,执行完毕后,再开始下一次执行;
ScheduledFuture<String> future = pool.schedule(new ScheduledDemo(i), 3, TimeUnit.SECONDS);
System.out.println(future.get());
}
} catch (Exception e) {
e.printStackTrace();
}
pool.shutdown();
}
}
class ScheduledDemo implements Callable<String> {
private int id;
public ScheduledDemo(int id) {
this.id = id;
}
@Override
public String call() throws Exception {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//返回一个100以内的随机数
return "当前线程名称=" + Thread.currentThread().getName() +",执行任务的ID="+id + ",所得的随机数="+new Random().nextInt(100);
}
}
2.3.2 ScheduledExecutorService.scheduleAtFixedRate
package com.isoftstone.mythread;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class TestScheduledThreadPool2 {
public static void main(String[] args) {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
try {
//第一次运行从1/2/3秒后开始,然后后面每隔3秒执行一次,实例只能是实现Runnable接口
//如果当前线程的执行时间没有超过3秒,则按照间隔3秒执行下一次
//如果当前线程的执行时间超过3秒,则按照当前线程执行时间算,执行完毕后,再开始下一次执行;
pool.scheduleAtFixedRate(new ScheduledDemo2(1), 1,3, TimeUnit.SECONDS);
//pool.scheduleAtFixedRate(new ScheduledDemo2(2), 2,3, TimeUnit.SECONDS);
//pool.scheduleAtFixedRate(new ScheduledDemo2(3), 3,3, TimeUnit.SECONDS);
//会优先打印over,因为上面三个线程是在1/2/3秒之后才第一次运行
System.out.println("over");
//int i = 1 / 0;
} catch (Exception e) {
e.printStackTrace();
pool.shutdownNow();
}
}
}
class ScheduledDemo2 implements Runnable {
private int id;
public ScheduledDemo2(int id) {
this.id = id;
}
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程名称=" + Thread.currentThread().getName() +",执行任务的ID="+id + ",所得的随机数="+new Random().nextInt(100));
}
}
2.3.3 ScheduledExecutorService.scheduleWithFixedDelay
package com.isoftstone.mythread;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class TestScheduledThreadPool3 {
public static void main(String[] args) {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
try {
//第一次运行从1/2/3秒后开始,然后后面延迟3秒执行一次,实例只能是实现Runnable接口
//只能等当前线程完成后,且延迟3秒,才执行下一次
pool.scheduleWithFixedDelay(new ScheduledDemo2(1), 1,3, TimeUnit.SECONDS);
//pool.scheduleWithFixedDelay(new ScheduledDemo2(2), 2,3, TimeUnit.SECONDS);
//pool.scheduleWithFixedDelay(new ScheduledDemo2(3), 3,3, TimeUnit.SECONDS);
//会优先打印over,因为上面三个线程是在1/2/3秒之后才第一次运行
System.out.println("over");
//int i = 1 / 0;
} catch (Exception e) {
e.printStackTrace();
pool.shutdownNow();
}
}
}
class ScheduledDemo3 implements Runnable {
private int id;
public ScheduledDemo3(int id) {
this.id = id;
}
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程名称=" + Thread.currentThread().getName() +",执行任务的ID="+id + ",所得的随机数="+new Random().nextInt(100));
}
}
3. Java8并行流
java8并行流底层其实就是在JDK1.7时就有的Fork/Join框架,但是写起来太麻烦,Java8进行了优化
3.1 for循环遍历求和
package com.isoftstone.mythread;
import java.time.Duration;
import java.time.Instant;
import java.util.stream.LongStream;
import org.junit.jupiter.api.Test;
public class TestJDK8 {
@Test
public void testJdk8() {
//java8新特性 572毫秒 parallel()并行
Instant start = Instant.now();
long sum = LongStream.rangeClosed(0L, 5000000000L)
.parallel()
.reduce(0L,Long::sum);
System.out.println(sum);
Instant end = Instant.now();
System.out.println("jdk8耗费时间为:" + Duration.between(start, end).toMillis());
}
@Test
public void testNormal() {//普通for循环 1441毫秒
Instant start = Instant.now();
long sum = 0;
for (long i = 0; i < 5000000000L; i++) {
sum += i;
}
System.out.println(sum);
Instant end = Instant.now();
System.out.println("normal耗费时间为:" + Duration.between(start, end).toMillis());
}
}