- 什么是JUC?
java.util.current的简称。
管程的概念:
一种同步机制,保证同一时间只有一个线程访问被保护数据或者代码。
用户线程:平时用到的多是用户线程
守护线程:比如主线程Main
-
创建线程的四种方式:
(1)继承Thread
(2)实现Runnable
(3)使用callable
(4)使用线程池
-
synchronize
自动释放锁
-
Lock接口
目的:手动释放锁
可重入锁:对某线程头标记,可重复加锁或者解锁。
start()方法默认执行native方法,资源交给操作系统执行。
与synchornize区别:
(1)synchronized是Java中关键字,Lock是一个接口
(2)Lock可以等待锁的线程响应中断,而synchronized不行
(3)可知道是否成功获取锁,synchronized不行。
-
多线程编程步骤
-
package org.example; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class Ticket4{ private int number = 0; private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public void add() { lock.lock(); try { while (number != 0){ condition.await(); } number++; System.out.println(Thread.currentThread().getName()+"::"+number); condition.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } public void decr(){ //加锁 lock.lock(); try { while (number != 1){ //睡眠,释放锁 condition.await(); } number--; System.out.println(Thread.currentThread().getName()+"::"+number); //通知 condition.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); }finally { //释放锁 lock.unlock(); } } } public class LockImplInter { public static void main(String[] args) { Ticket4 ticket4 = new Ticket4(); new Thread(()->{ for (int i = 0; i < 10; i++) { ticket4.add(); } },"aa").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { ticket4.decr(); } },"bb").start(); } }
-
步骤:
(1)公共资源定义
(2)判断、干活、通知
(3)防止虚假唤醒,将判断条件放在while循环中,wait()特 性:在哪里睡在哪里唤醒,所以这里用await,可以循环判断。
-
定制化通信
-
启动三个线程,按照如下要求:
AA打印5次,BB打印10次,CC打印15次
进行10轮。
-
思路流程图
-
package org.example;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class ShareResource{
private int flag = 1; // flag=1是A,flag=2是B,flag=3是c
private Lock lock = new ReentrantLock();
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
public void print5(int loop) throws InterruptedException {
//上锁
lock.lock();
try {
while (flag!=1){
c1.await();
}
for (int i = 1; i < 6; i++) {
System.out.println(Thread.currentThread().getName()+"::"+i+"::"+"轮数"+loop);
}
//修改标志位
flag=2;
//定向唤醒
c2.signal();
}
catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放锁
lock.unlock();
}
}
public void pring11(int loop) throws InterruptedException{
lock.lock();
try {
while (flag !=2){
c2.await();
}
for (int i = 1; i < 11; i++) {
System.out.println(Thread.currentThread().getName()+"::"+i+"::"+"轮数"+loop);
}
flag = 3;
c3.signal();//唤醒c3
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void pring15(int loop) throws InterruptedException{
lock.lock();
try {
while (flag!=3){
c3.await();
}
for (int i = 1; i < 16; i++) {
System.out.println(Thread.currentThread().getName()+":"+i+"论述:"+loop);
}
flag = 1;
c1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class DingXiangHuanxin {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
new Thread(()->{
for (int i = 1; i < 11; i++) {
try {
shareResource.print5(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"aa").start();
new Thread(()->{
for (int i = 1; i < 11; i++) {
try {
shareResource.pring11(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"bb").start();
new Thread(()->{
for (int i = 1; i < 11; i++) {
try {
shareResource.pring15(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"cc").start();
}
}
-
几种list的创建方式
package org.example; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; public class ListTest { public static void main(String[] args) { //(1)ArrayList不安全 //(2)List<String> list = new ArrayList<>(); //(3)List<String> list = new Vector<>(); //(4)Collections.synchronizedList // List<String> list = Collections.synchronizedList(new ArrayList<>()); //(5)写时复制技术 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(); } } }
写时复制技术:写的时候先复制一块,写完后再读取新的集合,进行合并。
部分源码:
public CopyOnWriteArrayList(Collection<? extends E> c) { Object[] elements; if (c.getClass() == CopyOnWriteArrayList.class) elements = ((CopyOnWriteArrayList<?>)c).getArray(); else { elements = c.toArray(); if (c.getClass() != ArrayList.class) elements = Arrays.copyOf(elements, elements.length, Object[].class); } setArray(elements); }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4JzBCUA4-1670401854022)(image-20221206102932793.png)]
-
concurrentHashMap()
package org.example; import java.util.*; import java.util.concurrent.ConcurrentHashMap; public class MapTest { public static void main(String[] args) { //不安全 // Map<String,String> map = new HashMap<>(); //解决 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(); } } }
-
公平锁与非公平锁
- 公平锁:效率低
- 非公平锁:效率高,但是会出现线程饿死的情况,比如synchronized就是非公平锁,而ReentrantLock默认非公平锁,可设置入参为true为公平锁。
-
可重入锁 (递归锁)
-
synchronized(隐式),lock(显示)
-
线程中对同一个对象加了多次同一把锁
package org.example; class DiguiLock{ public synchronized void add(){ add();//容易出现栈溢出问题 } } public class ReInLock { public static void main(String[] args) { Object o = new Object(); new Thread(()->{ synchronized (o){ System.out.println(Thread.currentThread().getName()+"外层"); synchronized (o){ System.out.println(Thread.currentThread().getName()+"中层"); synchronized (o){ System.out.println(Thread.currentThread().getName()+"内层"); } } } },"aa").start(); } }
-
lock接口演示可重入锁
package org.example; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Lockyanshi { public static void main(String[] args) { Lock lock = new ReentrantLock(); new Thread(()->{ try { lock.lock(); System.out.println("aaa"); try { lock.lock(); System.out.println("bbb"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } },"t1").start(); new Thread(()->{ lock.lock(); System.out.println("ccc"); lock.unlock(); },"cc").start(); } }
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gY2WLb2g-1670401854024)(image-20221206111235695.png)]
-
死锁
-
什么是死锁:
两个或两个以上进程执行过程中,因争夺资源造成互相等待现象,无外力干涉则无法继续执行。
- 验证是否死锁:
(1)jps -l
(2)jstack 端口号
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZInsvADb-1670401854025)(image-20221206111616984.png)]
-
-
callable
-
接口,相对于runnable,有返回值,如果没有则抛异常
-
实现方法名称不同,callable是call方法,而runnable是run()方法。
-
实现方式
找一个类,既和Runnable有关系,又和Callable有关系
Runnable接口有实现类FutureTask,可传递callable接口
类似适配器模式
-
FutureTask的使用
package org.example; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; class MyThread1 implements Runnable{ @Override public void run() { } } class Mythread2 implements Callable{ @Override public Integer call() throws Exception { return 200; } } public class CallAndRunTest { public static void main(String[] args) { new Thread(new MyThread1(),"aa").start(); // 报错 // new Thread(new Mythread2(),"bb").start(); /** * futuretask 未来任务 * 例子:老师上课(主线程),口渴买水。 * 1.不影响主线程,需要某些值时,单开线程,get即可 * 2.多个子线程之间对同一个资源的汇总, * 3.某个步骤迟迟得不到结果,先挂起,先做完其他任务。 */ FutureTask<Integer> task = new FutureTask<>(new Mythread2()); //函数式接口可用lambda表达式 FutureTask<Integer> task2 = new FutureTask<>(()->{ return 20; }); } }
-
-
辅助类
(1)countdown演示,减少计数
package org.example; import java.util.concurrent.CountDownLatch; public class CountDownTest { public static void main(String[] args) throws InterruptedException { //设置初始值是6 CountDownLatch countDownLatch = new CountDownLatch(6); for (int i = 0; i < 6; i++) { new Thread(()->{ System.out.println(Thread.currentThread().getName()+" 号同学离开了教室"); //计数-1 countDownLatch.countDown(); },String.valueOf(i)).start(); } countDownLatch.await(); //主线程 System.out.println(Thread.currentThread().getName()+"班长关门"); } }
(2)CyclicBarrier(循环栅栏)
例子:集齐七颗龙珠召唤神龙
package org.example; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; public class CyclicBarrierDemo { private static final int NUMBER = 7; public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER,new Thread(()->{ System.out.println("集齐7颗龙珠才能召唤神龙"); })); for (int i = 0; i < 7; i++) { new Thread(()->{ System.out.println(Thread.currentThread().getName()+" 星龙珠被收集到了"); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } },String.valueOf(i)).start(); } } }
(3)Semaphore:信号量,6辆汽车,3个停车位,相当于三把锁
package org.example;
import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class Semophere {
public static void main(String[] args) {
//创建Semaphore,3个许可量,相当于三把锁
//场景:秒杀场景
Semaphore semaphore = new Semaphore(3);
for (int i = 0; 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();
}
}
}
-
悲观锁与乐观锁
-
悲观:每次读写都加锁,不支持并发操作
-
乐观:版本控制,对同一资源 操作时,通过版本号控制,谁先提交就用谁的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wXCOsC77-1670401854026)(image-20221207101238830.png)]
-
-
表锁与行锁,都会发生死锁,写等读或者读等写
-
表锁:对象为一个表,锁整个表
-
行锁:一行记录
表锁与行锁都会发生死锁。
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P3sNvKCw-1670401854027)(image-20221207102337530.png)]
-
虚假唤醒
(1)循环操作中使用了wait方法时,由于wait方法在哪里睡在哪里醒的特性,会出现虚假唤醒的情况。
(2)判断条件不足
(3)金融方面会出现-1的可能。
-
读写锁案例
package org.example; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; class Myache{ //volatile,并发保证资源可见性,以及不可重排序,非原子性 private volatile Map<String,Object> map = new HashMap<>(); private ReadWriteLock rwLock = new ReentrantReadWriteLock(); //放数据 public void put(String key,Object value){ rwLock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName()+"...正在写操作"+key); TimeUnit.MICROSECONDS.sleep(300); map.put(key,value); System.out.println(Thread.currentThread().getName()+"写完了"+key); } catch (InterruptedException e) { e.printStackTrace(); }finally { rwLock.writeLock().unlock(); } } //取数据 public Object getValue(String key){ rwLock.readLock().lock(); Object result = null; System.out.println(Thread.currentThread().getName()+"正在获取:"+key); try { TimeUnit.MICROSECONDS.sleep(300); result = map.get(key); System.out.println(Thread.currentThread().getName()+"已经取完了"+key); } catch (InterruptedException e) { e.printStackTrace(); }finally { rwLock.readLock().unlock(); } return result; } } public class ReadWriteLockTest { public static void main(String[] args) { Myache myache = new Myache(); for (int i = 0; i < 6; i++) { final int number = i; new Thread(()->{ myache.put(number+"",number+""); },String.valueOf(i)).start(); } for (int i = 0; i < 6; i++) { final int number = i; new Thread(()->{ myache.getValue(number+""); },String.valueOf(i)).start(); } } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e2GpRuit-1670401854029)(image-20221207132450039.png)]
-
读写锁:一个资源可以被多个读线程访问,或者可以被一个写线程访问,但是不能同时存在读写线程,读写互斥,读读共享。
-
锁降级:写锁降为读锁的过程。
写锁只有一个线程,自然不能把别的线程赶走降级为读锁;因此,就只能是写锁降级为读锁了。先写后读。
package org.example; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantReadWriteLock; //锁降级过程 加写锁(可重入)--》加读锁-->释放写锁--》释放读锁 public class WriteToReadLock { public static void main(String[] args) { ReentrantReadWriteLock reentrantLock = new ReentrantReadWriteLock(); ReentrantReadWriteLock.ReadLock readLock = reentrantLock.readLock(); ReentrantReadWriteLock.WriteLock writeLock = reentrantLock.writeLock(); Map<String,String> map = new HashMap<>(); writeLock.lock(); System.out.println("写锁线程执行"); map.put("123","看你吗"); //说明写锁进行时,读锁可以进入该线程进行读操作 readLock.lock(); System.out.println("获取读锁"); String readValue = map.get("123"); System.out.println(Thread.currentThread().getName()+"号线程读到了值:"+readValue); //释放写锁 writeLock.unlock(); //释放读锁 readLock.unlock(); } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-paPQvetL-1670401854029)(image-20221207134157290.png)]
-
队列与栈
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xf6RonQK-1670401854030)(image-20221207134629847.png)]
-
阻塞队列
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rPC1SVB5-1670401854030)(image-20221207135100033.png)]
-
当队列是空时读线程阻塞,在队列不为空的时候自动唤醒
-
当队列是空时写线程唤醒,在队列满时写线程阻塞
唤醒与阻塞由队列一手包办,无需考虑在什么时候执行唤醒或者阻塞操作。
-
分类:
(1)有界队列:ArrayBlockingQueue,维护一个固定长度的链表。
(2)链表队列:LinkedBlcokingQueue,等等。
-
阻塞队列核心方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ew85hEuh-1670401854031)(image-20221207140153079.png)]
-
阻塞队列操作演示:
package org.example; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; public class BlockingQueueTest { public static void main(String[] args) throws InterruptedException { //创建阻塞队列 BlockingQueue blockingQueue = new ArrayBlockingQueue(3); //元素操作方法: //第一种方法 //添加元素 System.out.println(blockingQueue.add("a")); System.out.println(blockingQueue.add("b")); System.out.println(blockingQueue.add("c")); //检查元素 System.out.println(blockingQueue.element()); //删除元素 System.out.println(blockingQueue.remove()); System.out.println(blockingQueue.remove()); System.out.println(blockingQueue.remove()); //删多会报异常 // System.out.println(blockingQueue.remove()); BlockingQueue blockingQueue1 = new ArrayBlockingQueue(3); //第二种方法 //添加值 blockingQueue1.offer("a"); blockingQueue1.offer("b"); blockingQueue1.offer("c"); //取值 System.out.println(blockingQueue1.poll()); System.out.println(blockingQueue1.poll()); System.out.println(blockingQueue1.poll()); //为空时会输出空值,但是不会报异常 System.out.println(blockingQueue1.poll()); //put、get特点:队列满时写入会一直阻塞,可以加个判断条件 BlockingQueue blockingQueue2 = new ArrayBlockingQueue(3); blockingQueue2.offer("a"); blockingQueue2.offer("b"); blockingQueue2.offer("c"); System.out.println(blockingQueue2.offer("d", 3L, TimeUnit.SECONDS)); System.out.println(blockingQueue2.take()); System.out.println(blockingQueue2.take()); System.out.println(blockingQueue2.take()); System.out.println(blockingQueue2.take()); } }
-
-
线程池
-
概念:
维护多个线程,等待监督管理者分配可执行任务。
优点:
(1)降低资源消耗
(2)提高响应速度
(3)提高线程的可管理型
(4)Java中的线程池是通过Executor框架实现的,该框架中用到的Executor,Exceutors,ExecutorService,ThreadPoolExecutor这几个类,涉及到双亲委派机制。
-
分类:
(1)一池多线程:Executors.newFixedThreadPool(int)
(2)一池一线程:Executors.newSingleThreadPool()
(3)可扩容线程:Executors.newCachedThreadPool()
-
线程池使用演示:
package org.example; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolTest { public static void main(String[] args) { //一对多,定长 // ExecutorService threadPool = Executors.newFixedThreadPool(5); // 一池一线程 // ExecutorService threadPool = Executors.newSingleThreadExecutor(); // 可扩展线程 ExecutorService threadPool = Executors.newCachedThreadPool(); try { for (int i = 0; i < 10; i++) { threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+"办理业务"); }); } } catch (Exception e) { e.printStackTrace(); } finally { //给线程池打上一个不可用标记 threadPool.shutdown(); } } }
-
-
ThreadPool七个参数
- int corePoolSize 常驻线程池数量(核心)
- int maximumPoolSize 最大核心线程数
- long keepAliveTime 非核心线程数空闲时存活时间
- TimeUnit unit
- BlockingQueue workQueue 阻塞队列
- ThreadFactory threadFactory 线程工厂
- RejectedExecutionHandler handler 拒绝策略
-
线程池工作流程和拒绝策略
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wKtKXRgA-1670401854031)(image-20221207150819770.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qhp4lngn-1670401854031)(image-20221207151219264.png)]
AbortPolicy(默认):直接抛出RejectedExecutionException异常阻止系统正常运行
CallerRunsPolicy:调用者运行,一种调节机制,该策略图不会抛弃任务,不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。
DiscardOldestPolicy:抛弃队列中等待时间最长的任务
DiscardPolicy:无法处理任务,不予任何处理也不跑异常。
-
自定义线程池
package org.example; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class CustomThreadPool { public static void main(String[] args) { ThreadPoolExecutor customPool = new ThreadPoolExecutor( 2, 5, 3L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy() ); try { for (int i = 0; i < 10; i++) { //只有在execute这一步才是创建线程池完成 customPool.execute(()->{ System.out.println(Thread.currentThread().getName()+"自定义线程池"); }); } } catch (Exception e) { e.printStackTrace(); } finally { //关闭线程池(不可用标记) customPool.shutdown(); } } }
-
异步回调
- 同步:某线程请求做某事,锁未释放,阻塞,等待锁释放后执行以后操作。
- 异步:比如找同学办点事,同学不在,继续忙别的,请求别人同学回来了告诉我。
- A定义回调函数,注册到B的响应函数,事件触发,B调用响应函数,响应函数调用回调函数。
演示:
package org.example;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompleableFutureTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//同步回调,无返回值
CompletableFuture<Void> voidcompletableFuture = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName() + "同步回调");
});
voidcompletableFuture.get();
//异步回调,有返回值
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "异步回调");
//模拟异常
int i = 1/0;
return 1024;
});
Integer integerValue = completableFuture.get();
System.out.println(integerValue);
}
}
关于回调,写了个简单的回调测试demo
package org.example.huidiao;
public class A {
int number = 1;
public int callbackFunction(){
System.out.println("A定义了回调函数");
return number;
}
}
package org.example.huidiao;
public class B {
public void responseFunction(){
Integer bNumber = 1;
//注册A的回调函数
A a = new A();
int number = a.number;
//响应条件:bNumber的值等于A中的全局变量number
if (bNumber.equals(number)){
System.out.println("条件满足,B的响应函数调用A的回调函数");
int callNumber = a.callbackFunction();
System.out.println(callNumber);
}else {
System.out.println("条件不满足,只输出B的响应函数");
}
}
public static void main(String[] args) {
B b = new B();
b.responseFunction();
}
}
nt i = 1/0;
return 1024;
});
Integer integerValue = completableFuture.get();
System.out.println(integerValue);
}
}
关于回调,写了个简单的回调测试demo
package org.example.huidiao;
public class A {
int number = 1;
public int callbackFunction(){
System.out.println("A定义了回调函数");
return number;
}
}
package org.example.huidiao;
public class B {
public void responseFunction(){
Integer bNumber = 1;
//注册A的回调函数
A a = new A();
int number = a.number;
//响应条件:bNumber的值等于A中的全局变量number
if (bNumber.equals(number)){
System.out.println("条件满足,B的响应函数调用A的回调函数");
int callNumber = a.callbackFunction();
System.out.println(callNumber);
}else {
System.out.println("条件不满足,只输出B的响应函数");
}
}
public static void main(String[] args) {
B b = new B();
b.responseFunction();
}
}