Java-juc

article/2025/9/11 20:57:07

1. 进程和线程

  • 进程:

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个基本单位。例如:打开一个 .exe文件就是一个进程、打开360安全软件就是一个进程

  • 线程
  1. 线程是进程的一个实体,是进程中执行运算的最小单位。
  2. java默认有两个线程:main 和 GC(垃圾回收)

2. 并行和并发

  • 并发:(多个线程操作同一个资源)
    CPU一核,模拟出来多条线程。

  • 并行:(多个人一起行走)
    CPU多核,多个线程可以同时执行。
    并发编程的本质:充分利用CPU的资源

3. Synchronized和Lock

以卖票为例,多个线程买票,需要排队获取锁,保证线程安全。代码如下:

1. Synchronized

public class SaleTestDemo {public static void main(String[] args) {//并发,多个线程操作同一个资源类Ticket ticket = new Ticket();//Runnable 函数式接口  jdk1.8 Lambda 表达式new Thread(()->{for (int i = 0; i < 60; i++) {ticket.sale();}},"A").start();new Thread(()->{for (int i = 0; i < 60; i++) {ticket.sale();}},"B").start();new Thread(()->{for (int i = 0; i < 60; i++) {ticket.sale();}},"C").start();}
}
//资源类 OOP
class Ticket {//属性  方法private int number = 30;//卖票的方式//synchronized 本质: 队列 锁public synchronized void sale(){if (number > 0) {System.out.println(Thread.currentThread().getName() + "卖出了第" + (number--) + "票,剩余:" + number);}}
}

2. Lock

class Ticket2 {//属性  方法private int number = 30;Lock lock = new ReentrantLock(true);    //默认:false 非公平锁//卖票的方式public void sale(){lock.lock();    //加锁lock.tryLock(); //尝试获取锁try {//业务代码}} catch (Exception e) {e.printStackTrace();}finally {lock.unlock();  //解锁}}
}
new ReentrantLock(true);    //默认:false 非公平锁// 可重入锁(普通锁)
new ReentrantLock
// 读锁
ReentrantReadWriteLock.ReadLock
// 写锁
ReentrantReadWriteLock.WriteLock

3. Sychronized和Lock的区别

Sychronizedlock
内置Java关键字一个Java类
无法获取锁的状态可以判断锁的状态
会自动释放锁必须手动释放锁,如果不放,会死锁
线程1获得锁,线程2必须等待(傻瓜等待)lock锁不一定一直等待下去(可以手动设置)
可重入锁,非公平锁,不可中断可重入锁,非公平锁(可以设置,默认非公),可以中断
适合少量代码同步适合大量代码同步

4. 锁

1. 公平锁:

是指多个线程按照申请锁的顺序来获取锁,类似排队打饭,先来后到。

例如:

Lock lock = new ReentrantLock(true);

2. 非公平锁

是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁在高并发的情况下,有可能会造成优先级反转或者饥饿现(先抢,抢不到再排队)

//Sychronized
//Lock lock = new ReentrantLock();

3. 可重入锁(递归锁)

线程可以进入任何一个它已经拥有的锁所同步着的代码块。

指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁

在一个同步方法里面在访问另外一个同步方法,自动获取锁

  1. synchronized
package lock;
//Synchorized
public 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");call(); //这里也有锁(sms锁 里面的call锁)}public synchronized void call(){System.out.println(Thread.currentThread().getName() + "call");}
}
  1. Lock
    lock锁必须配对,否则就会死在里面
package 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锁必须配对否则就会死在里面try {System.out.println(Thread.currentThread().getName() + "sms");call(); //这里也有锁(sms锁 里面的call锁)} catch (Exception e) {e.printStackTrace();}finally {lock.unlock();}}public synchronized void call(){lock.lock();//第二把锁try {System.out.println(Thread.currentThread().getName() + "call");} catch (Exception e) {e.printStackTrace();}finally {lock.unlock();}}
}

4. 自旋锁

是指尝试获取锁的线程不会立即阳寒,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU

package com.demo.designmode.test;import ch.qos.logback.core.util.TimeUtil;import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;/*** @Author: jxm* @Description: 自学自旋锁* @Date: 2022/4/2 10:11* @Version: 1.0*/
public class JUCTest1 {/*** compare的使用:CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。*///原子引用AtomicReference<Thread> reference = new AtomicReference<>();public void myLock(){Thread thread = Thread.currentThread();System.out.println(Thread.currentThread().getName()+"\t come in .........");//第一次进来内存值就是nullwhile (!reference.compareAndSet(null,thread)){}}public void  myUnLock(){Thread thread = Thread.currentThread();reference.compareAndSet(thread,null);System.out.println(Thread.currentThread().getName()+"\t invoked myUnLock");}public static void main(String[] args){JUCTest jucTest = new JUCTest();new Thread(()->{jucTest.myLock();try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}finally {jucTest.myUnLock();}},"A").start();new Thread(()->{jucTest.myLock();try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}finally {jucTest.myUnLock();}},"B").start();new Thread(()->{jucTest.myLock();try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}finally {jucTest.myUnLock();}},"C").start();}}

5. 独占锁(写锁)

独占锁:指该锁一次只能被一个线程所持有。对 ReentrantLock 和 Synchronized 而言都是独占锁

6. 共享锁(读锁)

多个线程同时读一个资源类没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行

7. 读写锁

package com.demo.designmode.test;import ch.qos.logback.core.util.TimeUtil;import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;/*** @Author: jxm* @Description:* 读-读能共存* 读-写不能共存* 写-写不能共存* 写操作:原子+独占,整个过程必须是一个完整的统一体,中间不许被分割,被打断* @Date: 2022/4/2 10:11* @Version: 1.0*/
public class JUCTest2 {public static void main(String[] args){MyCache cache = new MyCache();for (int i = 1; i < 6; i++) {final int tempInt = i;new Thread(()->{cache.put(tempInt + "",tempInt + "");},String.valueOf(i)).start();}for (int i = 1; i < 6; i++) {final int tempInt = i;new Thread(()->{cache.get(tempInt + "");},String.valueOf(i)).start();}}}/*** 资源类*/
class MyCache{private volatile Map<String,Object> map = new HashMap();private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();public void put(String key,Object value){try {readWriteLock.writeLock().lock();System.out.println(Thread.currentThread().getName()+"\t 正在写入" + key);try {TimeUnit.MICROSECONDS.sleep(300);map.put(key,value);System.out.println(Thread.currentThread().getName()+"\t 写入完成" + key);} catch (InterruptedException e) {e.printStackTrace();}} catch (Exception e) {e.printStackTrace();}finally {readWriteLock.writeLock().unlock();}}public void get(String key){try {readWriteLock.readLock().lock();System.out.println(Thread.currentThread().getName()+"\t 正在读取");try {TimeUnit.MICROSECONDS.sleep(300);Object value = map.get(key);System.out.println(Thread.currentThread().getName()+"\t 读取完成" + value);} catch (InterruptedException e) {e.printStackTrace();}} catch (Exception e) {e.printStackTrace();}finally {readWriteLock.readLock().unlock();}}
}

5. 常用辅助类

1. CountDownLatch

倒计时(做减法)

  • 让一些线程阻塞直到另一些线程完成一系列操作后才被唤醒
  • CountDownlatch主要有两个方法,当一个或多个线程调用await方法时,调用线程会被阻塞。其它线程调用countDown方法会将计数器减1(调用countDown 方法的线程不会阻塞),当计数器的值变为零时,因调用await方法被阻塞的线程会被唤醒,继续执行。
public static void main(String[] args) throws Exception{CountDownLatch countDownLatch = new CountDownLatch(6);for (int i = 1; i <=6; i++){new Thread(() -> {System.out.println(Thread.currentThread().getName()+"\t国,被灭");countDownLatch.countDown();},CountryEnum.forEach_CountryEnum(i).getRetMessage()).start();}countDownLatch.await();System.out.println(Thread.currentThread().getName()+"\t"+"秦帝国,一统华夏");}//枚举 枚举类似数据库(k,v)
enum CountryEnum{ONE(1,"齐"),TWO(2,"楚"),THREE(3,"燕"),FOUR(4,"赵"),FIVE(5,"魏"),SIX(6,"韩");@Getter private Integer retCode;@Getter private String retMessage;CountryEnum(Integer retCode, String retMessage){this.retCode = retCode;this.retMessage = retMessage;}public static CountryEnum forEach_CountryEnum(int index){CountryEnum[] myArray = CountryEnum.values();for (CountryEnum element : myArray){if(index == element.getRetCode()){return element;}}return null;}}

2. CyclicBarrier

介绍:集齐七颗龙珠,召唤神龙(做加法)

CyclicBarrier的字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活,线程进入屏障通过cyclicBarrier的await()方法。

public static void main(String[] args) {CyclicBarrier cyclicBarrier = new CyclicBarrier(7,() -> {System.out.println("召唤神龙");});for (int i = 1;i <=7; i++) {final int tempInt = i;new Thread(() -> {System.out.println(Thread.currentThread().getName() + "\t收集到第:" + tempInt + "龙珠");try{cyclicBarrier.await();} catch(InterruptedException e){e.printStackTrace();} catch(BrokenBarrierException e){e.printStackTrace();}},String.valueOf(i)).start();}}

3. Semaphore

信号量 例子:抢停车位

信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。

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()+"\t抢到车位了");Thread.sleep(3);System.out.println(Thread.currentThread().getName()+"离开车位");} catch (InterruptedException e) {e.printStackTrace();}finally {semaphore.release();}},String.valueOf(i)).start();}}

6. 阻塞队列(blockingQueue)

  1. 介绍
  • 队列:先进先出 栈:先进后出

  • 当阻塞队列是空时,从队列中获取元素的操作将会被阻塞。

  • 当阻塞队列是满时,往队列里添加元素的操作将会被阻塞。

  • 试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。同样试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程从列中移除一个或者多个元素或者完全清空队列后使队列重新变得空闲起来并后续新增

  1. 有什么好处?
  • 在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤醒

  • 为什么需要BlockingQueue?
    好处是我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都给你一手包办了

  • 在concurrent包发布以前,在多线程环境下,我们每个程序员都必须去自己控制这些细节,尤其还要兼顾效率和线程安全,而
    这会给我们的程序带来不小的复杂度。

  1. 架构

    BlockingQueue 父接口 Queue 父接口 collection

已知实现类描述
ArrayBlockingQueue由数组结构组成的有界阻塞队列。
LinkedBlockingQueue由链表结构组成的有界(但大小默认值为Integer.MAX_VALUE)阻塞队列。
PriorityBlockingQueue支持优先级排序的无界阻塞队列。
DelayQueue使用优先级队列实现的延迟无界阻塞队列。
SynchronousQueue不存储元素的阻塞队列,也即单个元素的队列。
LinkedTransferQueue由链表结构组成的无界阻塞队列。
LinkedBlockingDeque由链表结构组成的双向阻塞队列
  1. 核心方法
方法类型抛出异常有返回值,不抛出异常(特殊值)阻塞等待超时等待
添加add(e)offer(e)put(e)offer(e,time,unit)
移除remove()poll()take()poll(time,unit)
检查队首元素element()pee()--
抛出异常当阻塞队列满时,再往队列里add插入元素会抛IIlegalStateException:Queue full
当阻塞队列空时,再往队列里remove移除元素会抛NoSuchElementException
特殊值插入方法,成功ture失败false
移除方法,成功返回出队列的元素,队列里面没有就返回null
一直阻塞当阻塞队列满时,生产者线程继续往队列里put元素,队列会一直阻塞生产线程直到put数据or响应中断退出。
当阻塞队列空时,消费者线程试图从队列里take元素,队列会一直阻塞消费者线程直到队列可用。
超时退出当阻塞队列满时,队列会阻塞生产者线程一定时间,超过后限时后生产者线程会退出

ArrayBlockingQueue

package queue;import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;public class Test {//Collection//list//Setpublic static void main(String[] args) {try {test4();} catch (InterruptedException e) {e.printStackTrace();}}/*** 抛出异常*/public static void test1(){//队列的大小ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);System.out.println(arrayBlockingQueue.add("A"));System.out.println(arrayBlockingQueue.add("B"));System.out.println(arrayBlockingQueue.add("C"));//IllegalStateException 抛出异常//System.out.println(arrayBlockingQueue.add("D"));System.out.println("---------------");System.out.println(arrayBlockingQueue.element());//查看队首的元素System.out.println("---------------");System.out.println(arrayBlockingQueue.remove());//谁先进谁先出System.out.println(arrayBlockingQueue.remove());System.out.println(arrayBlockingQueue.remove());//NoSuchElementException//System.out.println(arrayBlockingQueue.remove());}/*** 不抛出异常*/public static void test2(){ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);System.out.println(arrayBlockingQueue.offer("A"));System.out.println(arrayBlockingQueue.offer("B"));System.out.println(arrayBlockingQueue.offer("C"));//trueSystem.out.println("-----------------");System.out.println(arrayBlockingQueue.peek());//System.out.println(arrayBlockingQueue.offer("D"));//false 不抛出异常 返回一个boolean值System.out.println("-----------------");System.out.println(arrayBlockingQueue.poll());System.out.println(arrayBlockingQueue.poll());System.out.println(arrayBlockingQueue.poll());//依旧是先进先出System.out.println(arrayBlockingQueue.poll());//None 也不抛出异常}/*** 等待,阻塞(一直阻塞)*/public static  void test3() throws InterruptedException {ArrayBlockingQueue arrayBlockingQueue =new ArrayBlockingQueue(3);//一直阻塞arrayBlockingQueue.put("A");arrayBlockingQueue.put("B");arrayBlockingQueue.put("C");//arrayBlockingQueue.put("D");//队列没有位置了,他会一直等待System.out.println("----------------");System.out.println(arrayBlockingQueue.take());System.out.println(arrayBlockingQueue.take());System.out.println(arrayBlockingQueue.take());//System.out.println(arrayBlockingQueue.take());}/*** 等待,(超时)*/public static void test4() throws InterruptedException {ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);arrayBlockingQueue.offer("a");arrayBlockingQueue.offer("b");arrayBlockingQueue.offer("c");//arrayBlockingQueue.offer("d", 2,TimeUnit.SECONDS);//超时等待两秒arrayBlockingQueue.poll();arrayBlockingQueue.poll();arrayBlockingQueue.poll();arrayBlockingQueue.poll(2,TimeUnit.SECONDS);}
}

SynchronousQueue

package queue;import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;/*** 同步队列* 和其他的BlockingQueue不一样,SynchronousQueue 不存储元素* put了一个元素,必须先take取出来,否则不能取出值*/
public class SynchronousQueueDemo {public static void main(String[] args) {BlockingQueue<String> blockingDeque = new SynchronousQueue<>();//同步队列new Thread(()->{try {System.out.println(Thread.currentThread().getName() + "PUT 1");blockingDeque.put("1");System.out.println(Thread.currentThread().getName() + "PUT 2");blockingDeque.put("2");System.out.println(Thread.currentThread().getName() + "PUT 3");blockingDeque.put("3");} catch (InterruptedException e) {e.printStackTrace();}},"T1").start();new Thread(()->{try {TimeUnit.SECONDS.sleep(3);System.out.println(Thread.currentThread().getName() + "=>" + blockingDeque.take());TimeUnit.SECONDS.sleep(3);System.out.println(Thread.currentThread().getName() + "=>" + blockingDeque.take());TimeUnit.SECONDS.sleep(3);System.out.println(Thread.currentThread().getName() + "=>" +blockingDeque.take());} catch (InterruptedException e) {e.printStackTrace();}},"T2").start();}
}

7. 生产者消费者问题

1. Sychronized 版本

package demo02;import sun.awt.windows.ThemeReader;/**
线程之间的通信问题:生产者和消费者问题
线程交替执行:A B操作同一个变量 num = 0
A num+1
B num-1*/
public class SYProducers {public static void main(String[] args) {Date date = new Date();//执行+1new Thread(()->{for (int i =0; i<10;i++){try {date.increment();} catch (InterruptedException e) {e.printStackTrace();}}},"A").start();//执行-1new Thread(()->{for (int i =0;i<10;i++){try {date.decrement();} catch (InterruptedException e) {e.printStackTrace();}}},"B").start();}
}//判断等待,业务,通知
class Date{//数字 资源类private int number = 0;//+1public synchronized void increment() throws InterruptedException {while (number !=0){//等待this.wait();}number++;//通知其他线程,我+1完毕System.out.println(Thread.currentThread().getName() +"=>" +  number);this.notifyAll();}//-1public synchronized void decrement() throws InterruptedException {while (number == 0){//等待this.wait();}number --;//通知其他线程,我-1完毕System.out.println(Thread.currentThread().getName() +"=>" + number);this.notifyAll();}}

2. Lock 版本

package com.demo.springcache.utils;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*** @Author: jxm* @Description:* @Date: 2022/4/4 14:56* @Version: 1.0*/
public class ProductAndConsumerTest {public static void main(String[] args) {ShareData shareData = new ShareData();new Thread(()->{for (int i = 0; i < 5; i++) {try {shareData.increment();} catch (Exception e) {e.printStackTrace();}}},"A").start();new Thread(()->{for (int i = 0; i < 5; i++) {try {shareData.decrement();} catch (Exception e) {e.printStackTrace();}}},"B").start();new Thread(()->{for (int i = 0; i < 5; i++) {try {shareData.increment();} catch (Exception e) {e.printStackTrace();}}},"C").start();new Thread(()->{for (int i = 0; i < 5; i++) {try {shareData.decrement();} catch (Exception e) {e.printStackTrace();}}},"D").start();}
}//资源类
class ShareData{private int number = 0;private Lock lock = new ReentrantLock();private Condition condition = lock.newCondition();//生产public void increment() throws Exception{lock.lock();try {while (number != 0){//等待,不能生产condition.await();}number ++;System.out.println(Thread.currentThread().getName()+"\t"+number);//通知其他线程condition.signalAll();}catch (Exception e){e.printStackTrace();}finally {lock.unlock();}}//消费public void decrement() throws Exception{lock.lock();try {while (number == 0){//等待,不能生产condition.await();}number --;System.out.println(Thread.currentThread().getName()+"\t"+number);//通知其他线程condition.signalAll();}catch (Exception e){e.printStackTrace();}finally {lock.unlock();}}
}

3. 虚假唤醒问题

package com.demo.springcache.utils;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*** @Author: jxm* @Description:* @Date: 2022/4/4 14:56* @Version: 1.0*/
public class ProductAndConsumerTest {public static void main(String[] args) {ShareData shareData = new ShareData();new Thread(()->{for (int i = 0; i < 5; i++) {try {shareData.increment();} catch (Exception e) {e.printStackTrace();}}},"A").start();new Thread(()->{for (int i = 0; i < 5; i++) {try {shareData.decrement();} catch (Exception e) {e.printStackTrace();}}},"B").start();new Thread(()->{for (int i = 0; i < 5; i++) {try {shareData.increment();} catch (Exception e) {e.printStackTrace();}}},"C").start();new Thread(()->{for (int i = 0; i < 5; i++) {try {shareData.decrement();} catch (Exception e) {e.printStackTrace();}}},"D").start();}
}/*假设 number此时等于1,即已经被生产了产品如果这里用的是if判断,如果此时A,C两个生产者线程争夺increment()方法执行权假设A拿到执行权,经过判断number!=0成立,则A.wait()开始等待(wait()会释放锁),然后C试图去执行生产方法,但依然判断number!=0成立,则C.wait()开始等待(wait()会释放锁)碰巧这时候消费者线程线程B/D去消费了一个产品,使number=0然后,B/D消费完后调用this.notifyAll();这时候2个等待中的生产者线程继续生产产品,而此时number++ 执行了2次同理,重复上述过程,生产者线程继续wait()等待,消费者调用this.notifyAll();然后生产者继续超前生产,最终导致‘产能过剩’,即number大于1if(number != 0){// 等待condition.await();}
*/
//资源类
class ShareData{private int number = 0;private Lock lock = new ReentrantLock();private Condition condition = lock.newCondition();//生产public void increment() throws Exception{lock.lock();try {if (number != 0){  //导致虚假唤醒//等待,不能生产condition.await();}number ++;System.out.println(Thread.currentThread().getName()+"\t"+number);//通知其他线程condition.signalAll();}catch (Exception e){e.printStackTrace();}finally {lock.unlock();}}//消费public void decrement() throws Exception{lock.lock();try {if (number == 0){ //导致虚假唤醒//等待,不能生产condition.await();}number --;System.out.println(Thread.currentThread().getName()+"\t"+number);//通知其他线程condition.signalAll();}catch (Exception e){e.printStackTrace();}finally {lock.unlock();}}
}

4. 阻塞队列 版本

有点问题:

package com.demo.springcache.test;import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;/*** @Author: jxm* @Description:* @Date: 2022/4/4 21:41* @Version: 1.0*/
public class BlockingQueueTest {public static void main(String[] args) {MyResource myResource = new MyResource(new ArrayBlockingQueue<String>(10));new Thread(()->{System.out.println(Thread.currentThread().getName()+"\t生产线程启动");try {myResource.myProd();} catch (Exception e) {e.printStackTrace();}},"Prod").start();new Thread(()->{System.out.println(Thread.currentThread().getName()+"\t消费线程启动");try {myResource.myConsumer();System.out.println("-------------");} catch (Exception e) {e.printStackTrace();}},"Consumer").start();try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("5停止线程--------,活动结束");try {myResource.stop();} catch (Exception e) {e.printStackTrace();}}}class MyResource{//默认开启,进行生产+消费private volatile boolean FLAG = true;//原子引用private AtomicInteger atomicInteger = new AtomicInteger();BlockingQueue<String> blockingQueue = null;public MyResource(BlockingQueue<String> blockingQueue){this.blockingQueue = blockingQueue;System.out.println(Thread.currentThread().getName()+"\t");}public void myProd() throws Exception{String data = null;boolean retValue;while(FLAG){data = atomicInteger.incrementAndGet() + "";retValue  = blockingQueue.offer(data,2L,TimeUnit.SECONDS);if(retValue){System.out.println(Thread.currentThread().getName()+"\t"+"插入队列"+data+"成功");}else{System.out.println(Thread.currentThread().getName()+"\t"+"插入队列"+data+"失败");}TimeUnit.SECONDS.sleep(1);}System.out.println(Thread.currentThread().getName()+"\t"+"生产动作结束,表示FLAG = false");}public void myConsumer() throws Exception{String result = null;while (FLAG){result = blockingQueue.poll(2L,TimeUnit.SECONDS);if(null == result || result.equalsIgnoreCase("")){FLAG = false;System.out.println(Thread.currentThread().getName()+"\t"+"消费超时,退出");System.out.println();return;}System.out.println(Thread.currentThread().getName()+"\t"+"消费"+result+"成功");}}//停止线程public void stop() throws Exception{this.FLAG = false;}
}

5. 唤醒区别(wait()/notifyAll() 和 Condition的 await()/signalAll())

  • 锁绑定多个条件Condition
    synchronized没有
    ReentrantLock用来实现分组唤醒需要唤醒的线程们,可以精确唤醒,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。

  • Condition 精准的通知和唤醒线程

    package demo02;import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;/*** A执行完调用B,B执行完调用C,C执行完调用A*/
    public class C {public static void main(String[] args) {Data3 data3 = new Data3();new Thread(()->{for (int i =0; i< 10; i++){data3.printA();}},"A").start();new Thread(()->{for (int i =0; i< 10; i++){data3.printB();}},"B").start();new Thread(()->{for (int i =0; i< 10; i++){data3.printC();}},"C").start();}
    }
    class Data3{//资源lockprivate Lock lock  = new ReentrantLock();private Condition condition1 = lock.newCondition();private Condition condition2 = lock.newCondition();private Condition condition3 = lock.newCondition();private int number = 1;public void printA(){lock.lock();try{while(number != 1){//等待condition1.await();}System.out.println(Thread.currentThread().getName() + " => A");//唤醒指定的Bcondition2.signal();number = 2;}catch (Exception e){e.printStackTrace();}finally {lock.unlock();}}public void printB(){lock.lock();try{//业务,判断,执行,通知while(number != 2){condition2.await();}System.out.println(Thread.currentThread().getName() + " => B");condition3.signal();number = 3;}catch (Exception e){e.printStackTrace();}finally {lock.unlock();}}public void printC(){lock.lock();try{while(number != 3){condition3.await();}System.out.println(Thread.currentThread().getName() + " => C");condition1.signal();number = 1;}catch (Exception e){e.printStackTrace();}finally {lock.unlock();}}//生产线:下单->支付操作->交易->物流
    }

8. Callable 和Runnable

1、都是接口

2、Callable 有返回结果,Runnable无返回结果

3、Callable 可以抛出异常,Runnable无法抛出异常

4、方法不同

  • Callable 是 java.util 包下 concurrent 下的接口,有返回值,可以抛出被检查的异常
  • Runable 是 java.lang 包下的接口,没有返回值,不可以抛出被检查的异常
  • 二者调用的方法不同,run()/ call()

如何使用Callable

package com.demo.springcache.test;import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;class MyThread implements Callable<Integer> {@Overridepublic Integer call() throws Exception {System.out.println(Thread.currentThread().getName()+"-----------");return 1024;}
}/*** @Author: jxm* @Description:* @Date: 2022/4/4 22:41* @Version: 1.0*/
public class CallableTest {public static void main(String[] args) throws Exception{FutureTask<Integer> futureTask = new FutureTask<>(new MyThread());//启动两个线程Thread thread = new Thread(futureTask,"a");Thread thread = new Thread(futureTask,"a");//获取Callable的返回结果 // get方法可能会产生阻塞,把它放到最后,后者通过异步通信System.out.println(futureTask.get());}
}

1、有缓存

2、结果可能需要等待,会阻塞!

补充创建线程的其他两种方式:

1、直接继承Thread类,然后重写run方法

public class ThreadDemo {public static void main(String[] args) {MyThread a = new MyThread();a.start();MyThread b = new MyThread();b.start();}
}class MyThread extends Thread{public void run(){}
}

2、实现Runnable接口

public class RunnableDemo {public static void main(String[] args){MyThread2 thread2 = new MyThread2();new Thread(thread2,"a").start();new Thread(thread2,"b").start();}}class MyThread2 implements Runnable {//实现Runnable接口@Overridepublic void run() {}}

参考:https://blog.csdn.net/qq_36908872/article/details/115180401

9. 线程池

1. 概念

线程池:3大方法,7大参数,4种策略

池化技术:

  • 程序的运行,本质:占用系统的资源!优化资源的使用! ==> 引进了一种技术池化池
  • 线程池、连接池、内存池、对象池…
  • 池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我。

池化技术的好处:

  1. 降低资源的消耗
  2. 提高响应的速度
  3. 方便管理

线程可以复用、可以控制最大并发数、管理线程

2. 三大方法

  • newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
    它的构造源码如下:

    public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
    }
  • newFixedThreadPool :创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    它的构造源码如下:

    public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
    }
  • newSingleThreadExecutor :创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

    它的构造源码如下:

    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),threadFactory));
    }

分析:

【强制】线程池不允许使用Executors去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

Executors返回的线程池对象的弊端如下:

  1. FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为Integer.MAX_VALU 可能会堆积大量的请求,从而导致OOM。
  2. CachedThreadPool : 允许的创建线程数量为Integer.MAX VALUE,可能会创建大量的线程,从而导致OOM。

3. 七大参数

public ThreadPoolExecutor(int corePoolSize,//核心线程池大小int maximumPoolSize,//最大线程池大小long keepAliveTime,//存活的时间,超时没有人调用就会释放TimeUnit unit,//超时的单位BlockingQueue<Runnable> workQueue,//阻塞队列ThreadFactory threadFactory,//创建线程的工厂,一般不用动RejectedExecutionHandler handler) {//拒绝策略if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.acc = System.getSecurityManager() == null ?null :AccessController.getContext();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;}

手动创建线程池:

package com.demo.springcache.test;import java.util.concurrent.*;/*** @Author: jxm* @Description:* @Date: 2022/4/5 1:13* @Version: 1.0*/
public class ThreadExecutorTest {public static void main(String[] args) {ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5,3,TimeUnit.SECONDS,new LinkedBlockingQueue<>(3),Executors.defaultThreadFactory(), //默认工厂new ThreadPoolExecutor.AbortPolicy()); //银行满了,但是还有人进来,不处理这个人,并抛出异常//总线程数量= 最大线程数量 + 阻塞队列数量,超过抛出异常//9 > 5 + 3try {for (int i = 1; i <= 9; i++) {executor.execute(()->{System.out.println(Thread.currentThread().getName()+"-OK");});}}catch (Exception e){e.printStackTrace();}finally {executor.shutdown();}}
}

异常信息:

pool-1-thread-1-OK
java.util.concurrent.RejectedExecutionException: Task com.demo.springcache.test.ThreadExecutorTest$$Lambda$1/1717159510@7591083d rejected from java.util.concurrent.ThreadPoolExecutor@77a567e1[Running, pool size = 5, active threads = 4, queued tasks = 0, completed tasks = 4]
pool-1-thread-2-OK
pool-1-thread-3-OK
pool-1-thread-5-OK
pool-1-thread-4-OK
pool-1-thread-2-OK
pool-1-thread-3-OK
pool-1-thread-1-OKat java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)at com.demo.springcache.test.ThreadExecutorTest.main(ThreadExecutorTest.java:23)

4. 四种策略

  • new ThreadPoolExecutor.AbortPolicy():银行满了,还有人进来,不处理这个人的,抛出异常
  • new ThreadPoolExecutor.CallerRunsPolicy():哪来的去哪里!比如你爸爸 让你去通知妈妈洗衣服,妈妈拒绝,让你回去通知爸爸洗
  • new ThreadPoolExecutor.DiscardPolicy():队列满了,丢掉任务,不会抛出异常!
  • new ThreadPoolExecutor.DiscardOldestPolicy():队列满了,尝试去和最早的竞争,如果竞争失败的话,也不会抛出异常!

10. ForkJoin

11. CompletableFuture

12. Volatile

1、volatile是Java虚拟机提供的轻量级的同步机制

2、三大特性:保证可见性、不保证原子性、禁止指令重排

13、JMM

JVM(Java虚拟机)

JMM(java内存模型)

JMM(Java内存模型Java Memory Model,简称JMM)本身是一种抽象的概念并 不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。JMM关于同步的规定:
1、线程解锁前,必须把共享变量的值刷新回主内存
2、线程加锁前,必须读取主内存的最新值到自己的工作内存
3、加锁解锁是同一把锁由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),工作内存是每个线程的私有数据区域,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,各个线程中的工作内存中存储着主内存中的变量副本拷贝,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成,其简要访问过程如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2whx7MWf-1677725484026)(https://jxm-picture.oss-cn-chengdu.aliyuncs.com/study/20221012/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE22%E5%B9%B410%E6%9C%8812%E6%97%A51415_1.png)]

特性:

1、可见性

2、原子性

3、有序性

1.可见性

package volatileT;import java.util.concurrent.TimeUnit;public class JMMDemo {//不加 volatile程序就会死循环!//加了 volatile可以保证可见性private volatile static int num = 0;public static void main(String[] args) {new Thread(()->{//线程1 对主内存的变化不知道的while(num == 0){}}).start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}num = 1;System.out.println(num);}
}

2.不保证原子性

原子性:不可分割

线程A在执行任务的时候,不能被打扰的,也不能被分割。要么同时成功,要么同时失败

package com.demo.juc.volatildemo;import java.util.concurrent.atomic.AtomicInteger;/*** @Author: jxm* @Description:* @Date: 2022/10/12 14:55* @Version: 1.0*/
public class Test02 {private volatile static int num = 0;public static void add(){num++;}//理论结果因为:20000public static void main(String[] args) {for (int i = 0; i < 20; i++) {new Thread(()->{for (int j = 0; j < 1000; j++) {add();}}).start();}//java中有两个线程是默认执行的一个是main一个GCwhile (Thread.activeCount()>2){Thread.yield();}System.out.println(Thread.currentThread().getName()+"num:"+num);}
}

加同步锁保证原子性synchronized

package com.demo.juc.volatildemo;
public class Test02 {private volatile static int num = 0;//加同步锁保证原子性public synchronized static void add(){num++;}//理论结果因为:20000public static void main(String[] args) {for (int i = 0; i < 20; i++) {new Thread(()->{for (int j = 0; j < 1000; j++) {add();}}).start();}//java中有两个线程是默认执行的一个是main一个GCwhile (Thread.activeCount()>2){Thread.yield();}System.out.println(Thread.currentThread().getName()+"num:"+num);}
}

使用原子类来解决原子性问题

package com.demo.juc.volatildemo;import java.util.concurrent.atomic.AtomicInteger;public class Test02 {private volatile static AtomicInteger num = new AtomicInteger();public static void add(){num.getAndIncrement(); //+1方法 用底层的CAS}//理论结果因为:20000public static void main(String[] args) {for (int i = 0; i < 20; i++) {new Thread(()->{for (int j = 0; j < 1000; j++) {add();}}).start();}//java中有两个线程是默认执行的一个是main一个GCwhile (Thread.activeCount()>2){Thread.yield();}System.out.println(Thread.currentThread().getName()+"num:"+num);}
}

这些类的底层都直接和操作系统挂钩!在内存中修改值!Unsafe类是一个很特殊的存在!

3.禁止指令重排

什么是指令重排?:我们写的程序,计算机并不是按照你写的那样去执行的。

源代码 —> 编译器优化的重排 —> 指令并行也可能会重排 —> 内存系统也会重排 ——> 执行

处理器在执行指令重排的时候,会考虑:数据之间的依赖性

int x = 1; // 1
int y = 2; // 2
x = x + 5; // 3
y = x * x; // 4

我们所期望的:1234 ,但是可能执行的时候会变成 2134 或者 1324,但是不可能是 4123!

示例:

前提:a b x y 这四个值默认都是 0:

可能造成影响得到不同的结果:

线程A线程B
x = ay = b
b =1a = 2

正常的结果:x = 0,y = 0;

但是由于指定重排可能执行顺序发生变化,出现以下结果:

线程A线程B
b = 1a = 2
x = ay = b

指令重排导致的异常结果:x = 2,y= 1;

volatile 可以避免指令重排:

内存屏障是一个CPU指令。作用:

1.保持特定操作的执行顺序!

2.可以保证某些变量的内存可见性(利用volatile实现了可见性

Volatile是可以保持可见性,不能保证原子性,由于内存屏障,可以保证避免指令重排的现象产生!

volatile 内存屏障在单例模式中使用的最多!

14、单例模式

单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。

单例模式有以下特点:

1、单例类只能有一个实例。
  2、单例类必须自己创建自己的唯一实例。
  3、单例类必须给所有其他对象提供这一实例。

//懒汉式单例模式
private LazySingleton(){}private volatile static LazySingleton singleton;//lazyMan必须加上volatile防止指令重排private LazySingleton(){}public static LazySingleton getInstance(){if(singleton == null){synchronized (LazySingleton.class){if(singleton == null){singleton = new LazySingleton(); //不是一个原子性操作/*** 1.分配内存空间* 2.执行构造方法,初始化对象* 3.把这个对象指向这个空间** 期望顺序是:123* 特殊情况下实际执行:132  ===>  此时 A 线程没有问题*                               若额外加一个 B 线程*                               此时lazyMan还没有完成构造*/}}}return singleton;}

15、CAS

public class CasDemo01 {public static void main(String[] args) {AtomicInteger atomicInteger = new AtomicInteger(2021);//原子类//如果和期望的值相同就更新,CAS是CPU的并发原语System.out.println(atomicInteger.compareAndSet(2022,2023));System.out.println(atomicInteger.get());System.out.println(atomicInteger.compareAndSet(2021,2022));System.out.println(atomicInteger.get());//CAS : 比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环!}
}//结果:
false
2021
true
2022

AtomicInteger原子引用类,底层使用CAS(乐观锁)

源码:

public final int getAndAddInt(Object var1, long var2, int var4) {int var5;do {//获取内存地址中的值var5 = this.getIntVolatile(var1, var2);} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); //内存操作,效率很高return var5;
}

CAS : 比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环!

缺点:

1.循环会耗时

2.一次性只能保证一个共享变量的原子性

3.存在ABA问题

CAS : ABA 问题(狸猫换太子)

public static void main(String[] args) {AtomicInteger atomicInteger = new AtomicInteger(2020);//原子类//对我们写平时写的sql:乐观锁//如果和期望的值相同就更新,CAS是CPU的并发原语//*******捣乱的线程**********System.out.println(atomicInteger.compareAndSet(2020, 2021));System.out.println(atomicInteger.get()); // 2021System.out.println(atomicInteger.compareAndSet(2021, 2020));System.out.println(atomicInteger.get()); // 2021//********期望的线程*********System.out.println(atomicInteger.compareAndSet(2020, 2021));System.out.println(atomicInteger.get());//2021}

如何解决这个问题?原子引用

解决ABA问题:原子引用,带版本号的原子操作

**注意:Integer 使用了对象缓存机制,默认范围是 -128 ~ 127 ,推荐使用静态工厂方法 valueOf 获取对象实例,而不是 new,因为 valueOf 使用缓存,而 new 一定会创建新的对象分配新的内存空间;**每一次的2020都是一个新的值

package cas;import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;public class CASDemo02 {//比较并交换public static void main(String[] args) {//AtomicInteger atomicInteger = new AtomicInteger(2020);//原子类//Integer 使用了对象缓存机制,默认范围是 -128 ~ 127 ,推荐使用静态工厂方法 valueOf 获取对象实例,而不是 new,因为 valueOf 使用缓存,而 new 一定会创建新的对象分配新的内存空间;AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(11,1);//初始值,版本号时间戳new Thread(()->{int stamp = atomicInteger.getStamp();//获得版本号System.out.println("A = >" + stamp);try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}//最后两个参数,拿到最新的版本号,把版本号+1System.out.println(atomicInteger.compareAndSet(11, 22, atomicInteger.getStamp(), atomicInteger.getStamp() + 1));System.out.println("A2 = >"+atomicInteger.getStamp());//把这个值该回去System.out.println(atomicInteger.compareAndSet(22, 11, atomicInteger.getStamp(), atomicInteger.getStamp() + 1));System.out.println("A3 = >"+atomicInteger.getStamp());},"a").start();new Thread(()->{int stamp = atomicInteger.getStamp();//获得版本号System.out.println("B = >" + stamp);try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(atomicInteger.compareAndSet(11, 66, stamp, stamp + 1));System.out.println("b2 = >"  +atomicInteger.getStamp());},"b").start();}
}

16、死锁

17、死锁排查分析

1、概念

死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推进下去,如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-woyLeIz9-1677725484028)(https://jxm-picture.oss-cn-chengdu.aliyuncs.com/study/20221013/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE22%E5%B9%B410%E6%9C%8813%E6%97%A50934_1.png)]

2、产生死锁的主要原因

​ 2.1 系统资源不足

​ 2.2 进程推进的顺序不合适

​ 2.3 资源分配不当

示例:

package com.demo.juc.thread;import java.util.concurrent.TimeUnit;public class DeadlockDemo {public static void main(String[] args) {String lockA = "lockA";String lockB = "lockB";new Thread(new MyThread3(lockA,lockB),"T1").start();new Thread(new MyThread3(lockB,lockA),"T2").start();}
}class MyThread3 implements Runnable{private String lockA;private String lockB;public MyThread3(String lockA, String lockB) {this.lockA = lockA;this.lockB = lockB;}@Overridepublic void run() {synchronized (lockA){//lockA想拿BSystem.out.println(Thread.currentThread().getName() + "lock"+ lockA + "=> get" + lockB);try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lockB){//B想拿ASystem.out.println(Thread.currentThread().getName() + "lock" + lockB + "=> get" + lockA);}}}
}

运行结果:

T2locklockB=> getlockA
T1locklockA=> getlockB

排查问题:死锁

解决问题:

1.使用jps定位进程号 jps-l

E:\workSpace\idea\testWork\springboot-demo\demo-juc\src\main\java\com\demo\juc\thread>jps -l
2960 sun.tools.jps.Jps
17748
3556 org.jetbrains.idea.maven.server.RemoteMavenServer36
9364 com.demo.juc.thread.DeadlockDemo
4652 org.jetbrains.jps.cmdline.Launcher

2.使用jstack查看进程信息,进程号来找到死锁信息

E:\workSpace\idea\testWork\springboot-demo\demo-juc\src\main\java\com\demo\juc\thread>jstack 9364
Found one Java-level deadlock:
=============================
"T2":waiting to lock monitor 0x0000025920ec5d08 (object 0x000000076c38bee0, a java.lang.String),which is held by "T1"
"T1":waiting to lock monitor 0x0000025920ec84e8 (object 0x000000076c38bf18, a java.lang.String),which is held by "T2"Java stack information for the threads listed above:
===================================================
"T2":at com.demo.juc.thread.MyThread3.run(DeadlockDemo.java:37)- waiting to lock <0x000000076c38bee0> (a java.lang.String) //等待- locked <0x000000076c38bf18> (a java.lang.String) //锁at java.lang.Thread.run(Thread.java:748)
"T1":at com.demo.juc.thread.MyThread3.run(DeadlockDemo.java:37)- waiting to lock <0x000000076c38bf18> (a java.lang.String) //等待- locked <0x000000076c38bee0> (a java.lang.String) //锁at java.lang.Thread.run(Thread.java:748)Found 1 deadlock.E:\workSpace\idea\testWork\springboot-demo\demo-juc\src\main\java\com\demo\juc\thread>

总结:

面试、工作中!排查问题:

1.日志

2.堆栈信息

知识点来源狂神说、尚硅谷juc教程视频

尚硅谷Java大厂面试题第2季学习笔记——jvm部分
尚硅谷Java大厂面试题第2季学习笔记——Java引用(强软弱虚)
尚硅谷Java大厂面试题第2季学习笔记——垃圾收集器部分


http://chatgpt.dhexx.cn/article/5DI1pmHH.shtml

相关文章

Junit

Junit单元测试 简介&#xff1a;本文主要讲解&#xff0c;如何使用Eclipse,进行单元测试。 1.准备工作&#xff1a;搭建实验环境&#xff08;EclipseJunitAnt&#xff09; Eclipse&#xff1a;http://www.eclipse.org/ JUnit&#xff1a;http://www.junit.org/ Ant&#x…

juicer使用案例

代码结构&#xff1a; 编写main.html&#xff1a;引入方式可从bootcdn直接copy script标签 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge&…

juicer

UPDATE: juicer-0.3.1-dev published github.com. 让我们从一段代码说起&#xff0c;假设有一段这样的JSON数据&#xff1a; var json{name:"流火",blog:"ued.taobao.org" };我们需要根据这段JSON生成这样的HTML代码&#xff1a; 流火 (blog: ued.taoba…

Juicer软件的安装详解

欢迎关注”生信修炼手册”! 软件安装是生物信息实战中最基础的技能之一&#xff0c;只有确保软件安装无误&#xff0c;后续使用起来才会得心应手&#xff0c;不会有很多的bug。juicer软件提供了Hi-C数据一键化分析的pipeline, 这样高度的封装使得用户操作起来更加简便&#xff…

Juicer实战详解

欢迎关注”生信修炼手册”! Juicer软件的运行是非常简单的&#xff0c;只需要设置几个参数就可以了&#xff0c;本文利用官网的小的测试测试数据集来展示该软件的基本用法。 1. 下载测试数据 从以下链接下载测试数据集 https://github.com/aidenlab/juicer/wiki/Running-Juicer…

Juicer: 辅助基因组组装

Juicer: 辅助基因组组装 Juicer 导读 本文主要对处理HiC数据的Juicer程序进行一个简短的介绍&#xff0c;并展示如何利用Juicer进行基因组组装中染色体挂载的第一步。 1. 介绍 算法介绍 Juicer[1] 是一款能够提供一键式分析Loop-Resolution的程序。 特点 只需一次单击&#xff…

如何同步数据库数据

第一步 打开mysql的客户端 这里使用navicat&#xff0c;连接数据库&#xff0c;等到navicat主页面&#xff0c;双击需要操作的数据库连接 第二步 登录到数据库主页面后&#xff0c;点击左侧的数据库链接&#xff0c;打开数据库&#xff0c;可以看到可以操作的所有数据库 第三…

Logstash数据同步

Logstash 是 Elastic 技术栈中的一个技术&#xff0c;它是一个数据采集引擎&#xff0c;可以从数据库采集数据到 ES 中。可以通过设置 自增 ID 主键 或 更新时间 来控制数据的自动同步&#xff1a; 自增 ID 主键&#xff1a;Logstatsh 会有定时任务&#xff0c;如果发现有主键…

数据同步-数据库间的双向同步

当业务侧需要MongoDB降配、活动数据迁移时都需要应用切换数据库实例进行发版&#xff0c;发版过程中需最大程度保证新旧数据库数据一致&#xff0c;这就涉及到了一种同步技术-数据双向同步。在同步过程中遇到了一些可能会产生问题或引发思考的点&#xff0c;希望利用这篇文档进…

什么是数据实时同步,为什么数据实时同步很重要

随着云成为前所未有的数据供应渠道&#xff0c;数据准确性、一致性和隐私性的重要性与日俱增。看似轻微的数据错误或故障可能会产生重大负面影响。但是&#xff0c;​对数据进行排序并将其与现有​&#xff0c;然后定期解析数据实时同步&#xff0c;同时保持数据完整性&#xf…

数据同步工具的研究(实时)

数据同步工具的研究&#xff08;实时同步&#xff09;&#xff1a; FlinkCDC、Canal、Maxwell、Debezium ——2023年01月17日 ——Yahui Di 1. 常用CDC方案比较 2. FlinkCDC FlinkCDC的简介&#xff1a; Flink CDC 连接器是 Apache Flink 的一组源连接器&#xff0c;使用变…

聊聊数据同步

一、简述 数据同步&#xff0c;这是一个很宽泛的概念&#xff0c;在互联网或者传统软件公司&#xff0c;一定会遇到数据同步的场景。数据同步一般会遇到的问题诸如同步时延、数据一致性、性能低、强依赖于中间件、失败后无法补偿等。本文笔者试图简要总结下常见的数据同步场景&…

大数据的数据同步方式

一、全量覆盖 不需要分区&#xff0c;同步时直接覆盖插入。适用于数据不会有任何新增和变化的情况。比如地区、时间、性别等维度数据&#xff0c;不会变更或变更不影响业务&#xff0c;可以只保留最新值 二、仅新增同步 每天新增一个日期分区&#xff0c;同步并存储当天的新…

DataLink 数据同步平台

文章目录 一、数据同步平台概述核心能力工作原理详细流程 二、快速接入部署中间件程序配置创建数据库表启动应用注意事项 三、扩展&#xff1a;四种 CDC 方案比较优劣 一、数据同步平台 在项目开发中&#xff0c;经常需要将数据库数据同步到 ES、Redis 等其他平台&#xff0c;通…

数据同步之全量同步与增量同步

一、什么是数据同步 业务数据是数据仓库的重要数据来源&#xff0c;我们需要每日定时从业务数据库中抽取数据&#xff0c;传输到数据仓库中&#xff0c;之后再对数据进行分析统计。 为保证统计结果的正确性&#xff0c;需要保证数据仓库中的数据与业务数据库是同步的&#xff0…

你了解数据同步吗?

1.写在前面 本篇博客参考《操作系统实战 45 讲》 上篇博客主要介绍的是程序放在什么地方&#xff0c;开发操作系统要了解的最核心的硬件——CPU、MMU、Cache、内存&#xff0c;知道了它们的工作原理。在程序运行中&#xff0c;它们起到了至关重要的作用。 在开发我们自己的操…

数据库同步有哪些方式?【怎么保障目标和源数据一致性】

文章目录 摘要一、几种主流的数据库同步方式二、架构及工作原理三、全量同步和实时增量同步机制四、源和目标五、举例&#xff1a;Oracle 数据实时同步到 Elasticsearch六、目标和源数据一致性七、异构数据类型转换八、总结 摘要 数据库同步有3大难题&#xff1a; 1是如何保障…

数据技术篇之数据同步

第3章 数据同步 1.数据同步基础 直连同步 &#xff08;1&#xff09;什么是直连同步&#xff1f;直连同步是指通过定义好的规范接口 API 和基于动态链接库的方式直接连接业务库&#xff0c;如 ODBC/JDBC 等规定了统 一规范的标准接口&#xff0c;不同的数据库基于这套标准接口…

聊聊数据同步方案

文章目录 常用的数据同步方案数据库迁移场景数据同步场景应用代码中同步定时任务同步通过MQ实现同步通过CDC实现实时同步 CDC&#xff08;change data capture&#xff0c;数据变更抓取&#xff09;Canal基于日志增量订阅&消费支持的业务工作原理Mysql主备复制实现Canal架构…

大数据之路——数据同步

三、数据技术篇—— 数据同步 3.1 数据同步基础 3.1.1 直连同步3.1.2 数据文件同步3.1.3 数据库日志解析同步 3.2 数据仓库同步方式3.2.1 批量数据同步3.2.2 实时数据同步 3.3 同步遇到的问题3.3.1 分库分表3.3.2 增量全量同步的合并3.3.3 数据漂移的处理 有多种不同应用场景&…