详解JUC高并发编程
JUC并发编程
并发编程的本质:充分利用CPU的资源
问题:JAVA可以开启线程吗?
不能:最底层是本地方法,底层是C++,Java无法直接操作硬件
1.线程里有几个状态
NEW, 新生
RUNNABLE, 运行
BLOCKED, 阻塞
WAITING, 等待,死等
TIMED_WAITING, 超时等待
TERMINATED; 终止
2.wait和sleep的区别
1.来之不同的类
wait=>Object
sleep=>Thread
2.关于锁的释放
wait:会释放锁
sleep:不会释放
3.使用范围不同
wait:必须在同步代码块中
sleep:任何地方
4.是否需要异常捕获
wait:不需要异常捕获
sleep:需要
3.LOCK锁
synchronized锁(传统锁):队列,锁
package com.wdzl;/*** 线程是一个资源类,没有附属的操作*/
public class SaleTicketDemo01 {public static void main(String[] args) {//并发,多个线程操作同一资援,把资源类丢入线程Ticket ticket = new Ticket();new Thread(() -> {for (int i=0;i<40;i++){ticket.sale();}}, "A").start();new Thread(() -> {for (int i=0;i<40;i++){ticket.sale();}}, "B").start();new Thread(() -> {for (int i=0;i<40;i++){ticket.sale();}}, "C").start();}}//资源类 OOP面向对象编程
class Ticket {private int number = 30;//卖票方式// synchronized同步代码块public void sale() {synchronized (this){if (number > 0) {System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票" + ",还剩" + number + "张票");}}}// synchronized关键字public synchronized void sale2() {if (number > 0) {System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票" + ",还剩" + number + "张票");}}}
lock锁(接口)
底层:
公平锁:先来后到
非公平锁:可以插队(默认)提高效率
package com.wdzl;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;/*** 线程是一个资源类,没有附属的操作*/
public class SaleTicketDemo02 {public static void main(String[] args) {//并发,多个线程操作同一资援,把资源类丢入线程Ticket2 ticket = new Ticket2();new Thread(() -> { for (int i=0;i<40;i++) ticket.sale(); }, "A").start();new Thread(() -> { for (int i=0;i<40;i++) ticket.sale(); }, "B").start();new Thread(() -> { for (int i=0;i<40;i++) ticket.sale(); }, "C").start();}}//资源类 OOP面向对象编程/*** LOCK三部曲* 1.new ReentrantLock();* 2.加锁* 3.解锁*/
class Ticket2 {private int number = 30;Lock lock=new ReentrantLock();//卖票方式public void sale() {//加锁lock.lock();try {if (number > 0) {System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票" + ",还剩" + number + "张票");}} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}}
4.Synchronized与Lock的区别
1、Synchronizcd内置的Java关键字,Lock 是一个Java类
2、Synchronized无法判断获取锁的状态,Lock 可以判断是否获取到了锁(tryLock)
3、Synchronized 会自动释放锁,lock必须要手动释放锁!如果不释放锁,死锁
4、Synchronized线程1〔获得锁,阻塞)、线程2(等待,傻傻的等) ;Lock锁就不一定会等待下去;(tryLock)
5、Synchronized可重入锁,不可以中断的,非公平; Lock,可重入锁,可以判断锁,非公平(可以自己设置)
6、Synchronized适合锁少量的代码同步问题,Lock 适合锁大量的同步代码!
5.生产者消费者模型
1.Synchronzied版
注意:判断不用if,会出现虚假唤醒,if只判断一次
package com.wdzl;
/**\* synchronized等待唤醒模式*/public class pc {public static void main(String[] args) {//创建资源对象Data data = new Data();new Thread(()-> {for (int i=0;i<10;i++){try {data.Increment();} catch (InterruptedException e) {e.printStackTrace();}}},"A").start();new Thread(()-> {for (int i=0;i<10;i++){try {data.Decrement();} catch (InterruptedException e) {e.printStackTrace();}}},"B").start();}}//资源类/*** 判断等待,业务,通知*/
class Data{private Integer number=0;//加一public synchronized void Increment() throws InterruptedException {while (number!=0){this.wait();}number++;System.out.println(Thread.currentThread().getName()+"---->"+number);//通知其他线程this.notify();}//减一public synchronized void Decrement() throws InterruptedException {while (number==0){this.wait();}number--;System.out.println(Thread.currentThread().getName()+"---->"+number);//通知其他线程this.notify();}}
2.Lock版
1.Lock相当于Synchronzied
2.Synchronzied中的(wait,notify)相当于Condition中的(await,singalAll)
package com.wdzl;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*** Lock生产者消费者模型*/
public class pc2 {public static void main(String[] args) {//创建资源对象Data2 data = new Data2();new Thread(()-> {for (int i=0;i<10;i++){try {data.Increment();} catch (InterruptedException e) {e.printStackTrace();}}},"A").start();new Thread(()-> {for (int i=0;i<10;i++){try {data.Decrement();} catch (InterruptedException e) {e.printStackTrace();}}},"B").start();new Thread(()-> {for (int i=0;i<10;i++){try {data.Increment();} catch (InterruptedException e) {e.printStackTrace();}}},"C").start();new Thread(()-> {for (int i=0;i<10;i++){try {data.Decrement();} catch (InterruptedException e) {e.printStackTrace();}}},"D").start();}}//资源类/*** 判断等待,业务,通知*/
class Data2 {private Integer number = 0;Lock lock = new ReentrantLock();Condition condition = lock.newCondition();//加一public void Increment() throws InterruptedException {//加锁lock.lock();try {while (number != 0) {condition.await();}number++;System.out.println(Thread.currentThread().getName() + "---->" + number);//通知其他线程condition.signalAll();} catch (Exception e) {e.printStackTrace();} finally {//解锁lock.unlock();}}//减一public void Decrement() throws InterruptedException {//加锁lock.lock();try {while (number == 0) {condition.await();}number--;System.out.println(Thread.currentThread().getName() + "---->" + number);//通知其他线程condition.signalAll();} catch (Exception e) {e.printStackTrace();} finally {//解锁lock.unlock();}}
}
3.Condition(监视器,可以设置多个监视器来实现精准通知唤醒)
package com.wdzl;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*** Condition(监视器,可以设置多个监视器来实现精准通知唤醒)*/
public class pc3 {public static void main(String[] args) {Data3 data3=new Data3();new Thread(()-> {for (int i=0;i<10;i++){data3.Test1();}},"A").start();new Thread(()-> {for (int i=0;i<10;i++){data3.Test2();}},"B").start();new Thread(()-> {for (int i=0;i<10;i++){data3.Test3();}},"C").start();}
}//资源类
class Data3{private Lock lock=new ReentrantLock();private Condition condition=lock.newCondition();private Condition condition2=lock.newCondition();private Condition condition3=lock.newCondition();private int number=1;public void Test1(){lock.lock();try {//业务代码,判断等待,业务,通知while (number!=1){condition.await();}System.out.println(Thread.currentThread().getName()+"--------->"+number);number++;condition2.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void Test2(){lock.lock();try {//业务代码,判断等待,业务,通知while (number!=2){condition2.await();}System.out.println(Thread.currentThread().getName()+"--------->"+number);number++;condition3.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void Test3(){lock.lock();try {//业务代码,判断等待,业务,通知while (number!=3){condition3.await();}System.out.println(Thread.currentThread().getName()+"--------->"+number);number=1;condition.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}
}
6.八锁现象
1,标准情况下,两个线程先发短信 还是打电话 ? 答案:先发短信,后打电话
2,sendMsg 延迟4秒,两个线程是先打印 发短信,还是打电话? 答案:先发短信,后打电话
3,增加了一个普通方法后,是执行发短信,还是hello? 答案:先hello,后发短信
4,两个独享,两个同步方法,是先执行发短信,还是打电话? 答案:先打电话,后发短信
5,增加两个静态的同步方法,只有一个对象,是先发短信,还是先打电话? 答案:先发短信,后打电话
6,两个静态的同步方法,两个对象,是先发短信,还是先打电话? 答案:先发短信,后打电话
7,一个静态同步方法,一个普通同步方法,只有一个对象,先发短信,还是打电话? 答案:先打电话,后发短信
8,一个静态同步方法,一个普通同步方法,两个对象,先发短信,还是打电话? 答案:先打电话,后发短信
总结:
- new 出来的对象,每一个对象都是一把锁,锁的是对象自己,this;
- static 静态方法,锁的是Class,全局唯一;
- 同一把锁谁先获取锁,谁先执行;不用锁,根据CPU具体执行;
ps:如果有不对的,请评论指正,互相学习。
标签:锁对象
7.集合不安全
List不安全
ArrayList不安全
解决方法
- List list = new Vector<>();(底层是Synchronzied)
- List list = Collections.synchronizedList(new ArrayList<>());
- List list= new CopyOnWriteArrayList();(底层是Lock)
package com.wdzl.unsafe;import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;//java.util.ConcurrentModificationException 并发修改异常
public class ListTest {public static void main(String[] args) {// List<String> list = new ArrayList<>(); //线程不安全/*** 解决方法1* 1.List<String> list = new Vector<>();*///List<String> list = new Vector<>();/*** 解决方法2* 2. List<String> list = Collections.synchronizedList(new ArrayList<>());*/
// List<String> list = Collections.synchronizedList(new ArrayList<>());/*** 解决方法3* 3. List<String> list= new CopyOnWriteArrayList();*/List<String> list= new CopyOnWriteArrayList();for (int i=0;i<10;i++){new Thread(()->{list.add(UUID.randomUUID().toString().substring(0,5));System.out.println(list);},String.valueOf(i)).start();}}
}
Set不安全
解决方式
* 1.Set set = Collections.synchronizedSet(new HashSet<>());
* 2. Set set =new CopyOnWriteArraySet<>();
package com.wdzl.unsafe;import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;public class SetList {public static void main(String[] args) {// Set<String> set = new HashSet<>(); //不安全/*** 解决方式* 1.Set<String> set = Collections.synchronizedSet(new HashSet<>());* 2. Set<String> set =new CopyOnWriteArraySet<>();*/// Set<String> set = Collections.synchronizedSet(new HashSet<>());Set<String> set =new CopyOnWriteArraySet<>();for (int i=0;i<10;i++){new Thread(()->{set.add(UUID.randomUUID().toString().substring(0,5));System.out.println(set);},String.valueOf(i)).start();}}
}
HashSet底层
Hash底层就是HashMap
public HashSet() { map = new HashMap<>(); }set add的值就是map的key值,key值无法重复 public boolean add(E e) { return map.put(e, PRESENT)==null; }
Map不安全
重要
new HashMap<>(16,0.75) initialCapacity=16(初始容量16,), loadFactor=0.75(加载因子0.75)
解决方式
* 1.Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
* 2. Map<String, String> map =new ConcurrentHashMap<>(); 并发的HashMap
package com.wdzl.unsafe;import java.util.Collections;import java.util.HashMap;import java.util.Map;import java.util.UUID;import java.util.concurrent.ConcurrentHashMap;//new HashMap<>(16,0.75) initialCapacity=16(初始容量16,),loadFactor=0.75(加载因子0.75)public class MapList { public static void main(String[] args) { Map<String, String> map = new HashMap<>();//线程不安全 /** * 解决方式 * 1.Map<String, String> map = Collections.synchronizedMap(new HashMap<>()); * 2. Map<String, String> map =new ConcurrentHashMap<>();并发的HashMap */ //Map<String, String> map = Collections.synchronizedMap(new HashMap<>());// Map<String, String> map =new ConcurrentHashMap<>(); for (int i=0;i<10;i++){ new Thread(()->{ map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0, 5)); System.out.println(map); },String.valueOf(i)).start(); } }}
8.Callable
1.有返回值
2.可以抛出异常
3.方法为call()方法
如何使用Callable,使用Runable的实现类FutureTask
package com.wdzl.callable;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;public class CallableTest { public static void main(String[] args) throws ExecutionException, InterruptedException { //如何使用Callable,使用Runable的实现类FutureTask MyThread myThread=new MyThread(); FutureTask futureTask = new FutureTask(myThread); new Thread(futureTask,"A").start(); Object o = futureTask.get(); System.out.println(o); }}class MyThread implements Callable<Integer>{ @Override public Integer call() throws Exception { System.out.println("赵大帅币。。。。。。。。。"); return 1024; }}
细节:
1.有缓存(多个线程去调同一个方法,结果只打印一次)
2.get()的结果可能需要等待,会阻塞
9.常用的辅助类
9.1CountDownLatch(减法计数器)
package com.wdzl.add;import java.util.concurrent.CountDownLatch;public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch=new CountDownLatch(6); for (int i = 1; i <=6; i++) { new Thread(()-> { System.out.println(Thread.currentThread().getName()+"出门------》"); countDownLatch.countDown();//数量减一 } ,String.valueOf(i)).start(); } countDownLatch.await();//等待计数器归零,然后先下执行 System.out.println("关上大门。。。。。。。。"); }}
9.2CyclicBarrier(加法计数器)
package com.wdzl.add;import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CyclicBarrier;public class CyclicBarrierDemo { public static void main(String[] args) { //计数的数量为7 CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->{ System.out.println("召唤神龙。。。。。。。。"); }); for (int i = 1; i <=7 ; i++) { final int temp=i; new Thread(()->{ System.out.println(Thread.currentThread().getName()+"得到第几颗龙珠。。"+temp); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }).start(); } }}
9.3Semaphore(通行证)
作用:多个共享资源互斥使用。并发限流,控制最大线程数
package com.wdzl.add;import java.util.concurrent.Semaphore;import java.util.concurrent.TimeUnit;public class SemaphoreDemo { public static void main(String[] args) { 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(2); System.out.println(Thread.currentThread().getName()+"离开车位---"); } catch (InterruptedException e) { e.printStackTrace(); }finally { semaphore.release();//释放线程 } },String.valueOf(i)).start(); } }}
10.读写锁ReadWriteLock
独占锁(写锁):一次只能被一个线程占有
共享锁(读锁):多个线程可以共同占有
package com.wdzl.readWriteLock;import java.util.HashMap;import java.util.Map;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteLockDemo { public static void main(String[] args) { MyCache myCache = new MyCache(); //写锁 for (int i = 1; i <6 ; i++) { final int temp=i; new Thread(()->{ myCache.put(temp+"",temp+""); },String.valueOf(i)).start(); } //读锁 for (int i = 1; i <6 ; i++) { final int temp=i; new Thread(()->{ myCache.get(temp+""); },String.valueOf(i)).start(); } }}//资源类,自定义缓存class MyCache{ private volatile Map<String,String> map=new HashMap<>(); ReadWriteLock readWriteLock=new ReentrantReadWriteLock(); //存,写 public void put(String key,String value){ readWriteLock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName()+"写入"+key); map.put(key, value); System.out.println(Thread.currentThread().getName()+"写入成功"); } catch (Exception e) { e.printStackTrace(); } finally { readWriteLock.writeLock().unlock(); } } //取,读 public void get(String key){ readWriteLock.readLock().lock(); try { System.out.println(Thread.currentThread().getName()+"读取"+key); map.get(key); System.out.println(Thread.currentThread().getName()+"写读取成功"); } catch (Exception e) { e.printStackTrace(); } finally { readWriteLock.readLock().unlock(); } }}
11.阻塞队列
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-buL1FKcD-1628393314067)(
)]
什么时候用到阻塞队列:多线程并发处理,线程池
阻塞队列与集合的关系:与set,List同级
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZqZEsQe4-1628393314070)(
)]
学会使用队列
添加,移除
四组API
方式 | 抛出异常 | 有返回值,不抛出异常 | 等待阻塞(死等) | 超时等待(超过时间就结束) |
---|---|---|---|---|
添加 | add() | offer() | put() | offer(,) |
移除 | remove() | poll() | take() | poll(,) |
检测队首元素 | element() | peek() | - | - |
package com.wdzl.bq;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.TimeUnit;public class BlockingQueueDemo { public static void main(String[] args) throws InterruptedException { BlockingQueueDemo blockingQueueDemo = new BlockingQueueDemo();// blockingQueueDemo.test1(); // blockingQueueDemo.test2(); //blockingQueueDemo.test3(); blockingQueueDemo.test4(); } /** * 抛出异常 */ public void test1(){ //队列大小 ArrayBlockingQueue 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.add("d"));// Queue full抛出异常 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());//抛出异常 java.util.NoSuchElementException } /** * 不抛出异常 */ public void test2(){ //队列大小 ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3); //添加 System.out.println(blockingQueue.offer("a")); System.out.println(blockingQueue.offer("b")); System.out.println(blockingQueue.offer("c")); System.out.println(blockingQueue.offer("d")); //不抛出异常 false //查看首元素 System.out.println(blockingQueue.peek()); //移除 System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); //不抛出异常 null } /** * 等待阻塞 死等 * */ public void test3() throws InterruptedException { //队列大小 ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3); //添加 blockingQueue.put("a"); blockingQueue.put("b"); blockingQueue.put("c");// blockingQueue.put("d"); //移除 System.out.println(blockingQueue.take()); System.out.println(blockingQueue.take()); System.out.println(blockingQueue.take()); } /** * 超时等待 */ public void test4() throws InterruptedException { //队列大小 ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3); //添加 System.out.println(blockingQueue.offer("a")); System.out.println(blockingQueue.offer("b")); System.out.println(blockingQueue.offer("c")); System.out.println(blockingQueue.offer("d",2, TimeUnit.SECONDS)); //等待两秒,没有空间就退出 //移除 System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));//超时时间两秒,超过就退出 }}
12.同步队列
- 同步队列 SynchronousQueue
- 和其他的BlockingQueue不一样, SynchronousQueue不存储元素
- put了一个元素,必须冲里面take取出来,否则不能put值
package com.wdzl.bq;import java.util.concurrent.BlockingQueue;import java.util.concurrent.SynchronousQueue;import java.util.concurrent.TimeUnit;/** * 同步队列 SynchronousQueue * 和其他的BlockingQueue不一样, SynchronousQueue不存储元素 * put了一个元素,必须冲里面take取出来,否则不能put值 */public class SynchronousQueueDemo { public static void main(String[] args) { BlockingQueue blockingQueue=new SynchronousQueue(); new Thread(()->{ try { System.out.println(Thread.currentThread().getName()+"--------"+" put1"); blockingQueue.put(1); System.out.println(Thread.currentThread().getName()+"--------"+"put2"); blockingQueue.put(2); System.out.println(Thread.currentThread().getName()+"--------"+"put3"); blockingQueue.put(3); } catch (InterruptedException e) { e.printStackTrace(); } },"A").start(); new Thread(()->{ try { TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"--------"+blockingQueue.take()); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"--------"+blockingQueue.take()); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"--------"+blockingQueue.take()); } catch (InterruptedException e) { e.printStackTrace(); } },"B").start(); }}
13.线程池
线程池:三大方法,七大参数,四种拒接策略
线程池的好处:
1.降低资源消耗
2.提高响应速度
3.方便管理
三大方法:
package com.wdzl.pool;import java.util.concurrent.Executor;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;// Executors,工具类,三大方法public class Demo1 { public static void main(String[] args) { ExecutorService executorService = Executors.newSingleThreadExecutor(); //单线程 ExecutorService executorService1 = Executors.newFixedThreadPool(5);//创建一个固定的线程池大小 ExecutorService executorService2 = Executors.newCachedThreadPool();//可伸缩,根据任务的多少来生成对应最大的线程数 try { for (int i = 0; i < 10; i++) { final int temp=i; executorService2.execute(()->{ System.out.println(Thread.currentThread().getName()+"............>"+temp); }); } } catch (Exception e) { e.printStackTrace(); } finally { executorService.shutdown(); } }}
七大参数:
public ThreadPoolExecutor( int corePoolSize,//核心线程池大小 intmaximumPoolSize,//最大核心线程池大小 long keepAliveTime,//超时时间 TimeUnit unit,//超时单位 BlockingQueue<Runnable> workQueue,//阻塞队列 ThreadFactory threadFactory,//线程工厂,阻塞线程的,一般不用动 RejectedExecutionHandler handler//拒绝策略 ) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler); }
四种拒绝策略:
new ThreadPoolExecutor.AbortPolicy() //银行满了,还有人来,不处理
new ThreadPoolExecutor.CallerRunsPolicy() //哪来的回哪去
new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,尝试跟最早的竞争,不会抛出异常
new ThreadPoolExecutor.DiscardPolicy()//队列满了,丢掉任务,不会抛出异常
package com.wdzl.pool;import java.util.concurrent.*;// Executors,工具类,三大方法public class Demo1 { public static void main(String[] args) { /** * 三大方法 */ ExecutorService executorService = Executors.newSingleThreadExecutor(); //单线程 ExecutorService executorService1 = Executors.newFixedThreadPool(5);//创建一个固定的线程池大小 ExecutorService executorService2 = Executors.newCachedThreadPool();//可伸缩,根据任务的多少来生成对应最大的线程数 /** * 自定义线程池 */ ExecutorService threadPoolExecutor = new ThreadPoolExecutor( 2, 5, 3, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy() //银行满了,还有人来,不处理// new ThreadPoolExecutor.CallerRunsPolicy() //哪来的回哪去// new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,尝试跟最早的竞争,不会抛出异常// new ThreadPoolExecutor.DiscardPolicy()//队列满了,不会抛出异常 ); try { for (int i = 0; i < 10; i++) { final int temp=i; threadPoolExecutor.execute(()->{ System.out.println(Thread.currentThread().getName()+"............>"+temp); }); } } catch (Exception e) { e.printStackTrace(); } finally { executorService.shutdown(); } }}
小结拓展
池的最大的大小如何去设置!
了解:IO密集型,CPU密集型:(调优)
package com.kuang.pool; import java.util.concurrent.*;public class Demo01 { public static void main(String[] args) {// 自定义线程池!工作 ThreadPoolExecutor// 最大线程到底该如何定义// 1、CPU 密集型,几核,就是几,可以保持CPu的效率最高!// 2、IO 密集型 > 两倍判断你程序中十分耗IO的线程,// 程序 15个大型任务 io十分占用资源! // 获取CPU的核数 System.out.println(Runtime.getRuntime().availableProcessors()); ExecutorService threadPool = new ThreadPoolExecutor( 2, Runtime.getRuntime().availableProcessors(), 3, TimeUnit.SECONDS, new LinkedBlockingDeque<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了,尝试去和 最早的竞争,也不会抛出异常! try {// 最大承载:Deque + max// 超过 RejectedExecutionException for (int i = 1; i <= 9; i++) {// 使用了线程池之后,使用线程池来创建线程 threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+" ok"); }); } } catch (Exception e) { e.printStackTrace(); } finally {// 线程池用完,程序结束,关闭线程池 threadPool.shutdown(); } }}
14.四大函数式接口
1.函数式接口:只有一个方法的接口
Function函数式接口:有一个输入参数,有返回值
package com.wdzl.function;import java.util.function.Function;/** * Function函数型接口,有一个输入参数,有一个输出 */public class Demo1 { public static void main(String[] args) { Function<String,String> function= new Function<String, String>() { @Override public String apply(String s) { return s; } }; Function<String,String> function1=(String str)->{return str;}; System.out.println(function1.apply("wwwwwwwwww")); }}
断定型接口:有一个输入参数,返回值只能是 布尔值!
package com.wdzl.function;import java.util.function.Predicate;/** * 断定型接口:有一个输入参数,返回值只能是 布尔值! */public class Demo2 { public static void main(String[] args) {// 判断字符串是否为空// Predicate<String> predicate = new Predicate<String>(){ @Override public boolean test(String str) { return str.isEmpty(); } }; Predicate<String> predicate = (str)->{return str.isEmpty(); }; System.out.println(predicate.test("")); }}
Consumer 消费型接口:只有输入,没有返回值
package com.wdzl.function;import java.util.function.Consumer;public class Demo3 { public static void main(String[] args) { Consumer<String> consumer= new Consumer<String>() { @Override public void accept(String o) { System.out.println(o); } }; Consumer<String> consumer2=(str)->{ System.out.println(str); }; consumer2.accept("22222222"); }}
Supplier 供给型接口:没有参数,只有返回值
package com.wdzl.function;import java.util.function.Supplier;public class Demo4 { public static void main(String[] args) { Supplier<Integer> supplier= new Supplier<Integer>() { @Override public Integer get() { return 1024; } }; System.out.println(supplier.get()); }}
15.Stream流式计算
大数据:存储+计算
集合、MySQL 本质就是存储东西的;
计算都应该交给流来操作!
package com.wdzl.steam;import java.util.Arrays;import java.util.List;/** * 题目要求:一分钟内完成此题,只能用一行代码实现! * 现在有5个用户!筛选: * 1、ID 必须是偶数 * 2、年龄必须大于23岁 * 3、用户名转为大写字母 * 4、用户名字母倒着排序 * 5、只输出一个用户! */public class Test { public static void main(String[] args) { User u1 = new User(1,"a",21); User u2 = new User(2,"b",22); User u3 = new User(3,"c",23); User u4 = new User(4,"d",24); User u5 = new User(6,"e",25);// 集合就是存储 List<User> list = Arrays.asList(u1, u2, u3, u4, u5);// 计算交给Stream流// lambda表达式、链式编程、函数式接口、Stream流式计算 list.stream() .filter(u->{return u.getId()%2==0;})// .filter(u->{return u.getAge()>23;})// .map(u->{return u.getName().toUpperCase();})// .sorted((uu1,uu2)->{return uu2.compareTo(uu1);})// .limit(1) .forEach(u->{ System.out.println(u); }); }}
16.ForkJoin(拆分合并)
ForkJoin 在 JDK 1.7 , 并行执行任务!提高效率。大数据量!
大数据:Map Reduce (把大任务拆分为小任务)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jx45WP1E-1628393314072)(
)]
ForkJoin 特点:工作窃取
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VYxUfCgQ-1628393314074)(
)]
应用类
package com.wdzl.forkjoin;import java.util.concurrent.RecursiveTask;/** * 求和计算的任务! * 3000 6000(ForkJoin) 9000(Stream并行流) * // 如何使用 forkjoin * // 1、forkjoinPool 通过它来执行 * // 2、计算任务 forkjoinPool.execute(ForkJoinTask task) * // 3. 计算类要继承 ForkJoinTask */public class ForkJoinDemo extends RecursiveTask<Long> { private Long start; // 1 private Long end; // 1990900000 // 临界值 private Long temp = 10000L; public ForkJoinDemo(Long start, Long end) { this.start = start; this.end = end; } // 计算方法 @Override protected Long compute() { if ((end-start)<temp){ Long sum = 0L; for (Long i = start; i <= end; i++) { sum += i; } return sum; }else { // forkjoin 递归 long middle = (start + end) / 2; // 中间值 ForkJoinDemo task1 = new ForkJoinDemo(start, middle); task1.fork(); // 拆分任务,把任务压入线程队列 ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end); task2.fork(); // 拆分任务,把任务压入线程队列 return task1.join() + task2.join(); } }}
测试类
package com.wdzl.forkjoin;import java.util.concurrent.ExecutionException;import java.util.concurrent.ForkJoinPool;import java.util.concurrent.ForkJoinTask;import java.util.stream.LongStream;/** * 同一个任务,别人效率高你几十倍! */public class Test { public static void main(String[] args) throws ExecutionException {// test1(); // 12224// test2(); // 10038 test3(); // 153 } // 普通程序员 public static void test1(){ Long sum = 0L; long start = System.currentTimeMillis(); for (Long i = 1L; i <= 10_0000_0000; i++) { sum += i; } long end = System.currentTimeMillis(); System.out.println("sum="+sum+" 时间:"+(end-start)); } // 会使用ForkJoin public static void test2() throws ExecutionException, InterruptedException { long start = System.currentTimeMillis(); ForkJoinPool forkJoinPool = new ForkJoinPool(); ForkJoinTask<Long> task = new ForkJoinDemo(0L, 10_0000_0000L); ForkJoinTask<Long> submit = forkJoinPool.submit(task);// 提交任务 Long sum = submit.get(); long end = System.currentTimeMillis(); System.out.println("sum="+sum+" 时间:"+(end-start)); } public static void test3(){ long start = System.currentTimeMillis();// Stream并行流 () (] long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum); long end = System.currentTimeMillis(); System.out.println("sum="+"时间:"+(end-start)); }}
17.异步回调
Future 设计的初衷: 对将来的某个事件的结果进行建模
package com.wdzl.future;import java.util.concurrent.CompletableFuture;import java.util.concurrent.ExecutionException;/** * 异步调用: CompletableFuture * // 异步执行 * // 成功回调 * // 失败回调 */public class Demo01 { public static void main(String[] args) throws ExecutionException, InterruptedException {// 没有返回值的 runAsync 异步回调// CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {// try {// TimeUnit.SECONDS.sleep(2);// } catch (InterruptedException e) {// e.printStackTrace();// }// System.out.println(Thread.currentThread().getName() + "runAsync=>Void");// }); System.out.println("1111"); completableFuture.get(); // 获取阻塞执行结果// 有返回值的 supplyAsync 异步回调// ajax,成功和失败的回调 // 返回的是错误信息;// 返回的是错误信息; CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> { System.out.println(Thread.currentThread().getName() + "supplyAsync=>Integer"); int i = 10 / 0; return 1024; }); System.out.println(completableFuture.whenComplete((t, u) -> { System.out.println("t=>" + t); // 正常的返回结果 System.out.println("u=>" + u); // 错误信息:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero }).exceptionally((e) -> { System.out.println(e.getMessage()); return 233; // 可以获取到错误的返回结果 })); }); }}
18.JMM
JMM : Java内存模型,不存在的东西,概念!约定!
Volatile 是 Java 虚拟机提供轻量级的同步机制
1、保证可见性
2、不保证原子性
3、禁止指令重排
关于JMM的一些同步的约定:
1、线程解锁前,必须把共享变量立刻刷回主存。
2、线程加锁前,必须读取主存中的最新值到工作内存中!
3、加锁和解锁是同一把锁
线程 工作内存 、主内存
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lWQsJNyW-1628393314076)(
)]
内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类 型的变量来说,load、store、read和write操作在某些平台上允许例外)
lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量 才可以被其他线程锁定
read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便 随后的load动作使用
load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机 遇到一个需要使用到变量的值,就会使用到这个指令
assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变 量副本中
store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中, 以便后续的write使用
write (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内 存的变量中
JMM对这八种指令的使用,制定了如下规则:
不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须 write
不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存
不允许一个线程将没有assign的数据从工作内存同步回主内存
一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量 实施use、store操作之前,必须经过assign和load操作
一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解 锁
如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前, 必须重新load或assign操作初始化变量的值
如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
对一个变量进行unlock操作之前,必须把此变量同步回主内存
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0I9KCIaN-1628393314077)(
)]
19.Volatile
1.保证可见性
package com.wdzl.jmm;import java.util.concurrent.TimeUnit;public class JMMDemo { //不加volatile程序就会死循环 //加volatile可以保证可见性 private volatile static int num=0; public static void main(String[] args) {//main线程 new Thread(()->{ while (num==0){ } }).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } num=1; System.out.println(num); }}
2.不保证原子性
原子性 : 不可分割
线程A在执行任务的时候,不能被打扰的,也不能被分割。要么同时成功,要么同时失败。
package com.wdzl.jmm;// volatile 不保证原子性public class VDemo02 { // volatile 不保证原子性 private volatile static int num = 0; public static void add() { num++; } public static void main(String[] args) {//理论上num结果应该为 2 万 for (int i = 1; i <= 20; i++) { new Thread(() -> { for (int j = 0; j < 1000; j++) { add(); } }).start(); } while (Thread.activeCount() > 2) { // main gc Thread.yield(); } System.out.println(Thread.currentThread().getName() + " " + num); }}
如果不加lock和synchronzied怎样保证原子性
可以使用原子类,解决原子性
package com.wdzl.jmm;import java.util.concurrent.atomic.AtomicInteger;// volatile 不保证原子性public class VDemo02 { // volatile 不保证原子性 // 原子类的 Integer private static AtomicInteger num = new AtomicInteger(); public static void add() { // num++; // 不是一个原子性操作 num.getAndIncrement(); // AtomicInteger + 1 方法, CAS } public static void main(String[] args) {//理论上num结果应该为 2 万 for (int i = 1; i <= 20; i++) { new Thread(() -> { for (int j = 0; j < 1000; j++) { add(); } }).start(); } while (Thread.activeCount() > 2) { // main gc Thread.yield(); } System.out.println(Thread.currentThread().getName() + " " + num); }}
3.指令重排
什么是 指令重排:你写的程序,计算机并不是按照你写的那样去执行的。
源代码–>编译器优化的重排–> 指令并行也可能会重排–> 内存系统也会重排—> 执行
处理器在进行指令重排的时候,考虑:数据之间的依赖性!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2Bf6S6N7-1628393314078)(D:\jvm图片\2.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ekRMM4IU-1628393314079)(
)]
volatile可以避免指令重排:
内存屏障。CPU指令。
作用:
1、保证特定的操作的执行顺序!
2、可以保证某些变量的内存可见性 (利用这些特性volatile实现了可见性)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WyNW3ptz-1628393314079)(
)]
Volatile 是可以保持 可见性。不能保证原子性,由于内存屏障,可以保证避免指令重排的现象产生!
20.彻底玩转单例模式
单例模式:一个类只有一个实例,并且她自己负责创建自己的对象,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
核心代码:构造方法私有化,private。
1.饿汉式
从名字上也很好理解,就是“比较勤”,实例在初始化的时候就已经建好了,不管你有没有用到,都先建好了再说。好处是没有线程安全的问题,坏处是浪费内存空间。
package com.wdzl.singal;/** * 饿汉式 */public class EHan { private EHan(){}//私有化构造方法 private static EHan instance=new EHan(); public static EHan getInstance(){ return instance; }}
2.懒汉式
顾名思义就是实例在用到的时候才去创建,“比较懒”,用的时候才去检查有没有实例,如果有则返回,没有则新建。有线程安全和线程不安全两种写法,区别就是synchronized关键字。
package com.wdzl.singal;/** * 懒汉式 */public class LHan { private LHan(){}//私有化构造方法 private static LHan instance; public static LHan getInstance(){ if (instance==null){ instance=new LHan(); } return instance; }}
3.双检锁
双检锁,又叫双重校验锁,综合了懒汉式和饿汉式两者的优缺点整合而成。看上面代码实现中,特点是在synchronized关键字内外都加了一层 if 条件判断,这样既保证了线程安全,又比直接上锁提高了执行效率,还节省了内存空间。
package com.wdzl.singal;public class DoubleCheck { private DoubleCheck(){}//私有化构造方法 private static DoubleCheck instance; public static DoubleCheck getInstance(){ if (instance==null){ synchronized (DoubleCheck.class){ if (instance==null){ instance=new DoubleCheck(); } } } return instance; }}
4.静态内部类
静态内部类的方式效果类似双检锁,但实现更简单。但这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
package com.wdzl.singal;public class Singleton { private static class SingletonHolder{ private static final Singleton InSTANCE=new Singleton(); } private Singleton(){} public static final Singleton getInstance(){ return SingletonHolder.InSTANCE; }}
5.枚举
枚举的方式是比较少见的一种实现方式,但是看上面的代码实现,却更简洁清晰。并且她还自动支持序列化机制,绝对防止多次实例化。
package com.wdzl.singal;public enum Signletion { INSTANCE; public void anyMethod(){ }}
21.深入理解CAS
CAS目的
利用CPU的CAS指令,同时借助JNI来完成Java的非阻塞算法。其它原子操作都是利用类似的特性完成的。而整个J.U.C都是建立在CAS之上的,因此对于synchronized阻塞算法,J.U.C在性能上有了很大的提升。
CAS 用于同步的方式是从地址 V 读取值 A,执行多步计算来获得新 值 B,然后使用 CAS 将 V 的值从 A 改为 B。如果 V 处的值尚未同时更改,则 CAS 操作成功。
package com.wdzl.cas;import java.util.concurrent.atomic.AtomicInteger;public class CASDemo { // CAS compareAndSet : 比较并交换! public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(2020);// 期望、更新// public final boolean compareAndSet(int expect, int update)// 如果我期望的值达到了,那么就更新,否则,就不更新, CAS 是CPU的并发原语! System.out.println(atomicInteger.compareAndSet(2020, 2021)); System.out.println(atomicInteger.get());// atomicInteger.getAndIncrement(); System.out.println(atomicInteger.compareAndSet(2020, 2021)); System.out.println(atomicInteger.get()); }}
CAS存在的问题
1.ABA问题。因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。
2. 循环时间长开销大。自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。如果JVM能支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用,第一它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation)而引起CPU流水线被清空(CPU pipeline flush),从而提高CPU的执行效率。
3. 只能保证一个共享变量的原子操作。当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作。
21.原子引用
package com.wdzl.cas;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicStampedReference;public class CASDemo2 { //AtomicStampedReference 注意,如果泛型是一个包装类,注意对象的引用问题// 正常在业务操作,这里面比较的都是一个个对象 static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1); // CAS compareAndSet : 比较并交换! public static void main(String[] args) { new Thread(() -> { int stamp = atomicStampedReference.getStamp(); // 获得版本号 System.out.println("a1=>" + stamp); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } atomicStampedReference.compareAndSet(1, 2, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1); System.out.println("a2=>" + atomicStampedReference.getStamp()); System.out.println(atomicStampedReference.compareAndSet(2, 1, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1)); System.out.println("a3=>" + atomicStampedReference.getStamp()); }, "a").start();// 乐观锁的原理相同! new Thread(() -> { int stamp = atomicStampedReference.getStamp(); // 获得版本号 System.out.println("b1=>" + stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(atomicStampedReference.compareAndSet(1, 6, stamp, stamp + 1)); System.out.println("b2=>" + atomicStampedReference.getStamp()); }, "b").start(); }}
注意:
Integer 使用了对象缓存机制,默认范围是 -128 ~ 127 ,推荐使用静态工厂方法 valueOf 获取对象实 例,而不是 new,因为 valueOf 使用缓存,而 new 一定会创建新的对象分配新的内存空间;
22.各种锁的理解
1、公平锁、非公平锁
公平锁: 非常公平, 不能够插队,必须先来后到!
非公平锁:非常不公平,可以插队 (默认都是非公平)
2、可重入锁
可重入锁(递归锁)
Synchronized
package com.wdzl.lock;import java.util.concurrent.TimeUnit;// Synchronizedpublic class Demo01 { public static void main(String[] args) { Phone phone = new Phone(); new Thread(()->{ phone.sms(); },"A").start(); new Thread(()->{ phone.sms(); },"B").start(); }}class Phone{ public synchronized void sms(){ System.out.println(Thread.currentThread().getName() + "sms"); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } call(); // 这里也有锁 } public synchronized void call(){ System.out.println(Thread.currentThread().getName() + "call"); }}
lock
package com.wdzl.lock;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class Demo02 { public static void main(String[] args) { Phone2 phone = new Phone2(); new Thread(()->{ phone.sms(); },"A").start(); new Thread(()->{ phone.sms(); },"B").start(); }}class Phone2{ Lock lock = new ReentrantLock(); public void sms(){ lock.lock(); // 细节问题:lock.lock(); lock.unlock(); // lock 锁必须配对,否 则就会死在里面 lock.lock(); try { System.out.println(Thread.currentThread().getName() + "sms"); call(); // 这里也有锁 } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); lock.unlock(); } } public void call(){ lock.lock(); try { System.out.println(Thread.currentThread().getName() + "call"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }}
3、自旋锁
package com.wdzl.lock;import java.util.concurrent.TimeUnit;public class TestSpinlock { public static void main(String[] args) throws InterruptedException {// ReentrantLock reentrantLock = new ReentrantLock();// reentrantLock.lock();// reentrantLock.unlock();// 底层使用的自旋锁CAS SpinlockDemo lock = new SpinlockDemo(); new Thread(()-> { lock.myLock(); try { TimeUnit.SECONDS.sleep(5); } catch (Exception e) { e.printStackTrace(); } finally { lock.myUnLock(); } },"T1").start(); TimeUnit.SECONDS.sleep(1); new Thread(()-> { lock.myLock(); try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) { e.printStackTrace(); } finally { lock.myUnLock(); } },"T2").start(); }}
4、死锁
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qkMOL58h-1628393314080)(
)]
package com.wdzl.lock;import java.util.concurrent.TimeUnit;public class DeadLock { public static void main(String[] args) { String lockA = "lockA"; String lockB = "lockB"; new Thread(new MyThread(lockA, lockB), "T1").start(); new Thread(new MyThread(lockB, lockA), "T2").start(); }}class MyThread implements Runnable { private String lockA; private String lockB; public MyThread(String lockA, String lockB) { this.lockA = lockA; this.lockB = lockB; } @Override public void run() { synchronized (lockA) { System.out.println(Thread.currentThread().getName() + "lock:" + lockA + "=>get" + lockB); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockB) { System.out.println(Thread.currentThread().getName() + "lock:" + lockB + "=>get" + lockA); } } }}
解决问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lOcnD4pc-1628393314081)(
)]
面试,工作中! 排查问题:
1、日志 9
2、堆栈 1
详解JUC高并发编程相关推荐
- Java JUC高并发编程(三)-CallableJUC辅助类
目录 一.Callable接口 二.JUC辅助类 1.减少计数CountDownLatch 2.循环栅栏CyclicBarrier 3.信号灯Semaphore 一.Callable接口 Callab ...
- Java JUC高并发编程(一)
目录 一.概述 二.Lock接口 三.线程间的通信 解决虚假唤醒问题 Lock通信示例: 四.线程间定制化通信 一.概述 JUC就是java.util.concurrent工具包的简称,这是一个处理线 ...
- 【JUC高并发编程】—— 初见JUC
一.JUC 概述 什么是JUC JUC 是 Java并发编程的缩写,指的是 Java.util.concurrent 即Java工具集下的并发编程库 [说白了就是处理线程的工具包] JUC提供了一套并 ...
- 史上最强大型分布式架构详解:高并发+数据库+缓存+分布式+微服务+秒杀
分布式架构设计是成长为架构师的必备技能,涵盖的内容很广,今天一次打包分享,文末有:最全分布式架构设计资料获取方式~ 负载均衡 负载均衡的原理和分类 负载均衡架构和应用场景 分布式缓存 常见分布式缓存比 ...
- JUC高并发编程从入门到精通(全)
目录 前言 1. Lock接口 1.1 复习synchronized 1.2 Lock接口 1.3 Lock方法 1.4 两者差异 2. 线程间通信 2.1 synchronized案例 2.2 Lo ...
- 高级技术之 JUC 高并发编程
1,什么是 JUC 1.1 JUC简介 JUC 就是java.util.concurrent 工具包的简称,这一个专门用来处理线程的工具包,JDK1.5开始出现 1.2 进程与线程 **进程(Proc ...
- 【JUC高并发编程】—— 再见JUC
一.读写锁 读写锁概述 1️⃣ 什么是读写锁? 读写锁是一种多线程同步机制,用于在多线程环境中保护共享资源的访问 与互斥锁不同的是,读写锁允许多个线程同时读取共享资源,但在有线程请求写操作时,必须将其 ...
- 【牛客网】-【并发详解】-【并发编程基础】-【原子类】
目录 并发编程基础 原子类 参考书目: 并发编程基础 在操作系统中,并发是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处 ...
- RocketMQ使用详解以及高并发系统实践问题
RocketMQ 一.MQ介绍 1.什么是MQ?为什么要用MQ? 2.MQ的优缺点 3.几大MQ产品特点比较 二.RocketMQ快速实战 三.RocketMQ集群架构 1.RocketMQ集群中的各 ...
最新文章
- 【C++】C/C++ 中default/delete特性
- c++ python opencv_ubuntu下C++与Python混编,opencv中mat类转换
- [Java基础]哈希值
- matlab 实验数据 传递函数,《传递函数MATLAB实验》.ppt
- 架构师需要了解的知识
- python类型转换astype-python中numpy数据类型转换的方法
- ICCV 2021 best paper-Swin Transformer:对各类SOTA的降维打击!
- 用mysql搭建蚂蚁笔记_利用蚂蚁笔记搭建个人云笔记/博客
- 乐优商城遇到的坑(四)之前台门户系统之Search.html
- Ink脚本语言学习笔记(三)
- 计算机主机拆机事项,一体机联想s700拆机流程与注意事项
- rgb转换 css 字体
- CRISPR-Cas9实验常见问题及解决方案
- 【庄碰辉】万般滋味,皆是生活常态
- vue手机端回退_从外链回退到vue应用不触发生命周期、beforeRouterEnter等钩子函数的问题...
- 《领导沟通艺术与真实影响力》感想一
- Linux下海康威视工业相机的SDK二次开发
- 尾波冲浪流行起来了,水上运动“圣地”三亚火出圈
- 5G DTU 数据上传 无线通信
- 如何把无线路由器变成无线交换机使用?
热门文章
- 项目中Swagger2、lombok(小辣椒)、以及短信API的调用 简单介绍
- 机器人抓取(二)—— ROS 控制 onrobot RG2 / RG6 机械手(第二代)
- 课程体系包括哪些要素_课程体系包括哪些要素
- Firebird 数据库使用心得
- 在Android上用PHP编写应用- PFA初探
- 转行?小白?来看看这门和Java分庭抗礼的萌新语言!会的同学笑出了声!
- 今天我们说个一直以来的有趣话题:男程序员都是秃头,女程序员满脸痘痘
- 音乐ToB赛道的隐形巨头
- access 数据计算,常用函数
- Ubuntu连接上海大学校园网(ShuWlan-1x Shu(For All))