在java中,线程部分是一个重点,本篇说的JUC也是关于线程的,JUC就是java.util.concurrent工具包的简称。它是一个处理线程的工具包,JDK1.5开始出现的。
进程( Process )是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。
线程( thread )是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
总结来说:
进程:指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程一—资源分配的最小单位。
线程:系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元执行流。线程—程序执行的最小单位。 I
Thread.State枚举文件里有如下状态:
(1) sleep是Thread的静态方法,Iwait是Object的方法,任何对象实例都能调用。
(2) sleep不会释放锁,它也不需要占用锁。wait会释放锁,但调用它的前提是当前线程占有锁(即代码要在synchronized中),
(3)它们都可以被interrupted方法中断。
串行表示所有任务都一按先后顺序进行。串行意味着必须先装完一车柴才能运送这车柴,只有运送到了,才能卸下这车柴,并且只有完成了这整个三个步骤,才能进行下一个步骤。
串行是一次只能取得一个任务,并执行这个任务。
并行意味着可以同时取得多个任务,并同时去执行所取得的这些任务。并行模式相当于将长长的一条队列,划分成了多条短队列,所以并行缩短了任务队列的长度。并行的效率从代码层次上强依赖于多进程/线程代码,从硬件角度上
则依赖于多核CPU。
并发(€(concurren)€)指的是多个程序可以同时运行的现象,更细化的是多进程可
以同时运行或者多指令可以同时运行。但这不是重点,在描述并发的时候也不
会去扣这种字眼是否精确,并发的重点在于它是一种现象,并发描述
的是多进程同时运行的现象。但实际上,对于单核心CPU来说,同一时刻
只能运行一个线程。所以,这里的"同时运行表示的不是真的同一时刻有多个
线程运行的现象,这是并行的概念,而是提供一种功能让用户看来多个程序同
时运行起来了,但实际上这些程序中的进程不是一直霸占CP的,而是执行
会停一会。
**要解决大并发问题,通常是将大任务分解成多个小任务,**由于操作系统对进程的调度是随机的,所以切分成多个小任务后,可能会从任一小任务处执行。这可
能会出现一些现象:工
并发:同一时刻多个线程访问同一个资源,多个线程对一个点,举例:春运抢票,电商秒杀
并行:多项工作一起执行,之后再汇总,举例:泡方便面,电水壶烧水,一边撕调料到桶里。
package com.lyj.demo;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @program: java-test-demo
* @Date: 2021/8/11 7:34
* @Author: 凌兮
* @Description:
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class JUCTest {
/**
* List非线程安全的,底层add方法没有synchronized
* JUC:就是java.util.concurrent包
*/
public static void main(String [] args) {
// 方式1:list arrayList是非线程安全集合
// 报错:Exception in thread "1" Exception in thread "2"
// Exception in thread "0" java.util.ConcurrentModificationException
// List list = new ArrayList<>();
//方式2:使用vector线程安全的集合,底层add方法有synchronized,该方式比较古老,jdk1.0时就有了。
// List list = new Vector<>();
// 方式3:使用Collections.synchornizedList方式创建,入参传入一个集合,
// 线程安全的,底层也是用的synchronized,该方式比较古老,jdk1.2就有了
// List list = Collections.synchronizedList(new ArrayList<>());
// 方式4:使用JUC 里的CopyOnWriteArrayList,采用的是写时复制技术,底层采用lock方式
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(list);
}, String.valueOf(i)).start();
}
/** ============hashSet线程不安全测试======== */
// hashset非线程安全,报错:Exception in thread "15" Exception
// in thread "19" java.util.ConcurrentModificationException
// Set set = new HashSet<>();
// 解决方式1:CopyOnWriteArraySet,线程安全的,底层用的是lock
Set<String> set = new CopyOnWriteArraySet<>();
// for (int i = 0; i < 30; i++) {
// new Thread(() -> {
// set.add(UUID.randomUUID().toString().substring(0, 8));
// System.out.println(set);
// }, String.valueOf(i)).start();
// }
/** ============hashMap线程不安全测试======== */
// hashmap线程不安全的,报错:Exception in thread "3" java.util.ConcurrentModificationException
// Map map = new HashMap<>();
// 解决方式1:ConcurrentHashMap 线程安全的
Map<String, String> map = new ConcurrentHashMap<>();
for (int i = 0; i < 30; i++) {
String key = String.valueOf(i);
new Thread(() -> {
map.put(key, UUID.randomUUID().toString().substring(0, 8));
System.out.println(map);
}, String.valueOf(i)).start();
}
}
}
公平锁:
优点:效率高
缺点:会持续抢占获得锁,造成其他线程饿死。
非公平锁:
优点:每个线程都能公平的获得锁
缺点:效率低
# 查看当前运行的进程
jps -l
# 查看当前端口号进程的堆栈信息
jstack 端口号
可重入锁:可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,
并且不发生死锁,这样的锁就叫做可重入锁,在一个synchronized 修饰的方法
或者代码块的内部,调用本类的其他synchronized修饰的方法或代码块时,是永远
可以得到锁的。
/**
* countDown测试 :
* 5个学生都必须先离开后,才能锁门。相当于多个线程都执行完后,
* countdown值减为0才执行await之后的代码。
* @throws InterruptedException
*/
@Test
public void countDownLatchTest() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(5);
for (int i = 1; i <= 5; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "我离开教室了");
countDownLatch.countDown();
}, String.valueOf(i)).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + "班长锁门了,所有人都离开教室了");
}
/**
* 循环栅栏cyclicBarrier测试
* 案例:集齐7个龙珠召唤神龙
*/
@Test
public void cyclicBarrierTest() {
// 创建循环栅栏
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
// 只有parties达到7,才会执行该线程,召唤神龙
System.out.println("集齐了7个龙珠召唤神龙");
});
// 集齐龙珠过程,如果i的最大值改为6,则cyclicBarrier会一直等待,不会召唤神龙
for (int i = 1; i <= 7; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "龙珠");
try {
// 等待,parties加1,未达到7个线程时,cyclicBarrier一直会等待。
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}, String.valueOf(i)).start();
}
}
/**
* Semaphore信号量测试:
* 6辆汽车,3个停车位
*/
@Test
public void semaphoreTest() {
// 创建信号量,具有3个许可证
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
try {
// 抢占许可证
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "获得了许可证并停车了");
// 设置随机停车时间
TimeUnit.SECONDS.sleep(new Random().nextInt(5));
System.out.println(Thread.currentThread().getName() + "归还了许可证并离开车库");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放许可证,并离开
semaphore.release();
}
}, String.valueOf(i)).start();
}
}
表锁:不会发生死锁,因为它是锁的整张表
行锁:会发生死锁,多个线程互相等待,因为它锁的是行记录
读锁:又叫共享锁,会发生死锁
写锁:又叫独占锁,会发生死锁
读锁发生死锁原因:
写锁发生死锁原因:
线程1在写的时候可以操作方框2的数据,但线程2在写的时候可以操作方框1的数据,这个时候互相等待,出现死锁。
未使用读写锁,会出现读写并发问题
static class MapCache {
// 创建缓存器 由于是经常进行读写操作,所以设置为volatile
public static volatile Map<String, Object> cache = new HashMap<>();
/**
* 写操作
* @param key
* @param value
*/
public static void put(String key, Object value) {
System.out.println(Thread.currentThread().getName() + "正在进行写操作" + key);
// 暂停一会
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
cache.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入完毕" + key);
}
/**
* 读操作
* @param key
* @return
* @throws InterruptedException
*/
public static Object get(String key) {
System.out.println(Thread.currentThread().getName() + "正在进行读操作" + key);
// 暂停一会
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
cache.get(key);
System.out.println(Thread.currentThread().getName() + "读取完毕" + key);
return cache.get(key);
}
}
@Test
public void readWriteLockTest() {
// 多线程写
for (int i = 1; i <= 5; i++) {
final int key = i;
new Thread(() -> {
MapCache.put(key + "", key + "");
}, String.valueOf(i)).start();
}
// 多线程读
for (int i = 1; i <= 5; i++) {
final int key = i;
new Thread(() -> {
MapCache.get(key + "");
}, String.valueOf(i)).start();
}
}
static class MapCache {
// 创建缓存器 由于是经常进行读写操作,所以设置为volatile
public static volatile Map<String, Object> cache = new HashMap<>();
// 创建读写锁
public static ReadWriteLock lock = new ReentrantReadWriteLock();
// 读锁
public static Lock readLock = lock.readLock();
// 写锁
public static Lock writeLock = lock.writeLock();
/**
* 写操作
* @param key
* @param value
*/
public static void put(String key, Object value) {
writeLock.lock();
// 暂停一会
try {
System.out.println(Thread.currentThread().getName() + "正在进行写操作" + key);
TimeUnit.MILLISECONDS.sleep(300);
cache.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入完毕" + key);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
writeLock.unlock();
}
}
/**
* 读操作
* @param key
* @return
* @throws InterruptedException
*/
public static Object get(String key) {
readLock.lock();
Object result = null;
// 暂停一会
try {
System.out.println(Thread.currentThread().getName() + "正在进行读操作" + key);
TimeUnit.MILLISECONDS.sleep(300);
result = cache.get(key);
System.out.println(Thread.currentThread().getName() + "读取完毕" + key);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
readLock.unlock();
}
return result;
}
}
// 测试
public static void main(String[] args) {
// 多线程写
for (int i = 1; i <= 5; i++) {
final int key = i;
new Thread(() -> {
JUCTest.MapCache.put(key + "", key + "");
}, String.valueOf(i)).start();
}
// 多线程读
for (int i = 1; i <= 5; i++) {
final int key = i;
new Thread(() -> {
JUCTest.MapCache.get(key + "");
}, String.valueOf(i)).start();
}
}
/**
* 写锁降级测试:
* 读锁:只有读完之后,才能进行写操作
* 写锁:在写的过程中也可以进行读操作,写锁可以降级为读锁,提高数据的可见性,提高效率
* 读锁不能升级为写锁
*/
@Test
public void writeLockFallBackTest() {
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
Lock writeLock = readWriteLock.writeLock();
Lock readLock = readWriteLock.readLock();
/** 写锁降级测试成功---注释下面的测试模块*/
// 写操作
writeLock.lock();
System.out.println("写操作已上写锁");
// 释放写锁
writeLock.unlock();
// 释放读锁
readLock.unlock();
/** 读锁升级为写锁测试 ===不能升级--注释上面的测试模块*/
// // 读操作
// readLock.lock();
// System.out.println("读操作已上读锁");
// // 写操作
// writeLock.lock();
// System.out.println("写操作已上写锁");
// // 释放读锁
// readLock.unlock();
// // 释放写锁
// writeLock.unlock();
}
/**
* 阻塞队列测试1--抛出异常
*/
@Test
public void blockingQueueTest() {
// 创建数组类型的阻塞队列
BlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
System.out.println(queue.add("a"));
System.out.println(queue.add("b"));
System.out.println(queue.add("c"));
// 数组类型的阻塞队列,检查元素时,默认是第一个元素
System.out.println(queue.element());
// 再次添加会报错,queue full
// System.out.println(queue.add("d"));
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
// 再次移除会报错,Nosuchemenet
// System.out.println(queue.remove());
}
/**
* 阻塞队列测试2--特殊值
*/
@Test
public void blockingQueueTest2() {
// 创建数组类型的阻塞队列
BlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
System.out.println(queue.offer("a"));
System.out.println(queue.offer("b"));
System.out.println(queue.offer("c"));
// 数组类型的阻塞队列,检查元素时,默认是第一个元素
System.out.println(queue.peek());
// 再次添加返回false, 添加成功返回true
System.out.println(queue.offer("d"));
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
// 再次移除会返回null
System.out.println(queue.poll());
}
/**
* 阻塞队列测试3--阻塞
*/
@Test
public void blockingQueueTest3() throws InterruptedException {
// 创建数组类型的阻塞队列
BlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
queue.put("a");
queue.put("b");
queue.put("c");
// 再放程序会阻塞,直到队列空出一个位置。
// queue.put("d");
System.out.println("检测程序是否执行完");
System.out.println(queue.take());
System.out.println(queue.take());
System.out.println(queue.take());
// 再取程序会阻塞,直到队列里有值,获得值
System.out.println(queue.take());
System.out.println("检测程序是否执行完");
}
/**
* 阻塞队列测试4--超时
*/
@Test
public void blockingQueueTest4() throws InterruptedException {
// 创建数组类型的阻塞队列
BlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
System.out.println(queue.offer("a", 10, TimeUnit.MILLISECONDS));
System.out.println(queue.offer("b", 10, TimeUnit.MILLISECONDS));
System.out.println(queue.offer("c", 10, TimeUnit.MILLISECONDS));
// 再次添加,如果添加不进去,并且超时时间到了,会放弃添加
System.out.println(queue.offer("d", 10, TimeUnit.MILLISECONDS));
System.out.println("检测程序是否执行完");
System.out.println(queue.poll(10, TimeUnit.MILLISECONDS));
System.out.println(queue.poll(10, TimeUnit.MILLISECONDS));
System.out.println(queue.poll(10, TimeUnit.MILLISECONDS));
// 再次移除,如果没有移除的值,并且超时时间到了,会放弃移除,并返回null
System.out.println(queue.poll(10, TimeUnit.MILLISECONDS));
System.out.println("检测程序是否执行完");
}
/**
* 线程池测试
*/
@Test
public void threadPoolTest() {
// 创建指定数量的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(5, new ThreadFactory() {
// 原子计数器
AtomicInteger num = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
return new Thread("fixedThread_" + num.getAndIncrement());
}
});
// 创建一池仅有一个线程的线程池
ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
// 创建一池自动可扩容的线程池
ExecutorService threadPool3 = Executors.newCachedThreadPool();
try {
for (int i = 0; i <= 10; i++) {
// 线程池切换名字即可测试
threadPool3.execute(() -> {
System.out.println(Thread.currentThread().getName());
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 将线程归还
threadPool3.shutdown();
}
}
/**
* 采用ForkJoin池,计算1+2+3+...+100和
*/
static class MyTask extends RecursiveTask<Integer>{
// 拆分的差值不能大于10
private static final Integer VALUE = 10;
// 拆分开始值
private int begin;
// 拆分结束值
private int end;
// 拆分结束值
private int result;
public MyTask(int begin, int end) {
this.begin = begin;
this.end = end;
}
@Override
protected Integer compute() {
// 差值小于10分支
if ((end - begin) <= 10) {
for (int i = begin; i <= end; i++) {
result += i;
}
} else {
// 差值大于10, 进行任务拆分
int middle = (begin + end) / 2;
// 左任务
MyTask myTask01 = new MyTask(begin, middle);
// 右任务
MyTask myTask02 = new MyTask(middle + 1, end);
// 调用方法拆分
myTask01.fork();
// 调用方法拆分
myTask02.fork();
// 合并结果
result = myTask01.join() + myTask02.join();
}
return result;
}
}
/**
* forkJoin测试
*/
@Test
public void forkJoinTest() throws ExecutionException, InterruptedException {
// 创建任务对象
MyTask myTask = new MyTask(0, 100);
// 创建合并分支池
ForkJoinPool forkJoinPool = new ForkJoinPool();
// 将任务扔进池里
ForkJoinTask<Integer> joinTask = forkJoinPool.submit(myTask);
// 获取最终合并值
System.out.println(joinTask.get());
// 关闭合并池
forkJoinPool.shutdown();
}