1、乐观锁和悲观锁

1.1、悲观锁

  1. 认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会加锁,确保数据不会别的线程修改。
  2. synchronized关键字和Lock的实现类都是悲观锁。
  3. 适合写操作多的场景,先加锁可以保证写操作时数据正确,显示的锁定之后再造作同步资源。
  4. 狼性锁

1.2、乐观锁

  1. 认为自己在使用数据时不会有别的线程修改数据或资源,所以不会添加锁。

  2. 在JAVA中是通过使用无锁编程来实现,只是在更新数据的时候去判断,之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果这个数据已经被其它线程更新,则根据不同的实现方式执行不同的操作,比如放弃修改、重试抢锁等等。

  3. 判断规则:
    (1)版本号机制Version
    (2) 最常采用的是CAS算法,JAVA原子类中的递增操作就通过CAS自旋实现的。

  4. 适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅度提升。

  5. 乐观锁直接去操作同步资源,是一种无锁算法,得之我幸不得我命,再努力就是一句话:佛系锁。
    在这里插入图片描述

2、线程八锁

  1. 标准访问有a、b两个线程,请问先打印邮件还是短信?
import java.util.concurrent.TimeUnit;/*** @Description: 线程8锁* @Author: yangyb* @Date:2022/9/28 22:25* Version: 1.0**/class Phone{public synchronized void sendEmail(){System.out.println("-------------sendEmail");}public synchronized void sendSMS(){System.out.println("-----------sendSMS");}
}public class Lock8Demo {public static void main(String[] args){Phone phone = new Phone();new Thread(phone::sendEmail,"a").start();//暂停两毫秒,保证a线程先启动try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}new Thread(phone::sendSMS,"b").start();}}

synchronized 加在普通的方法上面,是对象锁,由于a线程先启动,在调用sendEmail()方法时先拿到了这个phone对象的锁,所以肯定先执行此方法,此方法执行完后,a线程释放phone对象锁,然后b线程才能拿到phone对象锁,去执行sendSMS()方法。
在这里插入图片描述
2. sendEmail方法中加入暂停3秒钟,请问先打印邮件还是hello?

import java.util.concurrent.TimeUnit;/*** @Description: 线程8锁* @Author: yangyb* @Date:2022/9/28 22:25* Version: 1.0**/class Phone{public synchronized void sendEmail(){try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("-------------sendEmail");}public synchronized void sendSMS(){System.out.println("-----------sendSMS");}
}public class Lock8Demo {public static void main(String[] args){Phone phone = new Phone();new Thread(phone::sendEmail,"a").start();//暂停两毫秒,保证a线程先启动try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}new Thread(phone::sendSMS,"b").start();}}

synchronized 加在普通的方法上面,是对象锁,由于a线程先启动,在调用sendEmail()方法时先拿到了这个phone对象的锁,此时虽然暂停了3秒钟,但phone对象锁一直被a线程持有,所以肯定还是先执行sendEmail()方法,此方法执行完后,a线程释放phone对象锁,然后b线程才能拿到phone对象锁,去执行sendSMS()方法。
在这里插入图片描述
在这里插入图片描述

  1. 添加一个普通的hello方法,请问先打印邮件还是hello?
import java.util.concurrent.TimeUnit;/*** @Description: 线程8锁* @Author: yangyb* @Date:2022/9/28 22:25* Version: 1.0**/class Phone{public synchronized void sendEmail(){try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("-------------sendEmail");}public synchronized void sendSMS(){System.out.println("-----------sendSMS");}public void hello(){System.out.println("----------hello");}
}public class Lock8Demo {public static void main(String[] args){Phone phone = new Phone();new Thread(phone::sendEmail,"a").start();//暂停两毫秒,保证a线程先启动try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}new Thread(phone::hello,"b").start();}}

a线程拿到phone对象锁以后,去执行sendEmail()方法,但中间暂停了3秒钟,同时b线程执行的是普通的hello(),所以不需要获取phone对象锁,就可以立即执行hello()方法,而不用等到a线程释放phone对象锁,再去获取phone对象锁。
在这里插入图片描述

  1. 有两个手机对象,请问先打印邮件还是短信?
/*** @Description: 线程8锁* @Author: yangyb* @Date:2022/9/28 22:25* Version: 1.0**/class Phone{public synchronized void sendEmail(){try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("-------------sendEmail");}public synchronized void sendSMS(){System.out.println("-----------sendSMS");}public void hello(){System.out.println("----------hello");}
}public class Lock8Demo {public static void main(String[] args){Phone phone = new Phone();Phone phoneOne = new Phone();new Thread(phone::sendEmail,"a").start();//暂停两毫秒,保证a线程先启动try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}new Thread(phoneOne::sendSMS,"b").start();}}

a线程持有的是phone对象的锁,并不会影响到b线程获取phoneOne对象的锁,所以是两个线程拿到了两个不同的对象锁,互不干扰,各自执行各自的。
在这里插入图片描述
在这里插入图片描述

  1. 有两个静态同步方法,有一个手机对象,请问先打印邮件还是短信?
import java.util.concurrent.TimeUnit;/*** @Description: 线程8锁* @Author: yangyb* @Date:2022/9/28 22:25* Version: 1.0**/class Phone{public static synchronized void sendEmail(){try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("-------------sendEmail");}public static synchronized void sendSMS(){System.out.println("-----------sendSMS");}public void hello(){System.out.println("----------hello");}
}public class Lock8Demo {public static void main(String[] args){Phone phone = new Phone();new Thread(()->{phone.sendEmail();},"a").start();//暂停两毫秒,保证a线程先启动try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}new Thread(()->{phone.sendSMS();},"b").start();}}

静态方法属于类,在静态方法上加上synchronized关键字,线程a先启动获得的就是类锁,这时候锁住的是整个类的资源,所以线程b必须等待线程a释放类锁以后才能获取到类锁。
在这里插入图片描述
6. 有两个静态同步方法,有两个手机对象,请问先打印邮件还是短信?

import java.util.concurrent.TimeUnit;/*** @Description: 线程8锁* @Author: yangyb* @Date:2022/9/28 22:25* Version: 1.0**/class Phone{public static synchronized void sendEmail(){try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("-------------sendEmail");}public static synchronized void sendSMS(){System.out.println("-----------sendSMS");}public void hello(){System.out.println("----------hello");}
}public class Lock8Demo {public static void main(String[] args){Phone phone = new Phone();Phone phoneOne = new Phone();new Thread(()->{phone.sendEmail();},"a").start();//暂停两毫秒,保证a线程先启动try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}new Thread(()->{phoneOne.sendSMS();},"b").start();}}

静态方法属于类,在静态方法上加上synchronized关键字,线程a先启动获得的就是类锁,这时候锁住的是整个类的资源,所以线程b必须等待线程a释放类锁以后才能获取到类锁,即使创建了两个不同的对象,但这两个对象都属于同一个类,所以b线程还是要等待a线程释放整个类锁后,才能获得类锁,再执行响应的资源。
在这里插入图片描述
在这里插入图片描述

  1. 有一个静态同步方法,有一个普通同步方法,有一个手机对象,请问先打印邮件还是短信?
import java.util.concurrent.TimeUnit;/*** @Description: 线程8锁* @Author: yangyb* @Date:2022/9/28 22:25* Version: 1.0**/class Phone {public static synchronized void sendEmail() {try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("-------------sendEmail");}public synchronized void sendSMS() {System.out.println("-----------sendSMS");}public void hello() {System.out.println("----------hello");}
}public class Lock8Demo {public static void main(String[] args) {Phone phone = new Phone();new Thread(() -> {phone.sendEmail();}, "a").start();//暂停两毫秒,保证a线程先启动try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {phone.sendSMS();}, "b").start();}}

获取静态同步方法,需要持有类锁,普通同步方法,获取的是对象锁,所以b线程不需要等到a线程释放完类锁,就能能获取到对象锁。
在这里插入图片描述

  1. 有一个静态同步方法,有一个普通同步方法,有两个手机对象,请问先打印邮件还是短信?
import java.util.concurrent.TimeUnit;/*** @Description: 线程8锁* @Author: yangyb* @Date:2022/9/28 22:25* Version: 1.0**/class Phone {public static synchronized void sendEmail() {try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("-------------sendEmail");}public synchronized void sendSMS() {System.out.println("-----------sendSMS");}public void hello() {System.out.println("----------hello");}
}public class Lock8Demo {public static void main(String[] args) {Phone phone = new Phone();Phone phoneOne = new Phone();new Thread(() -> {phone.sendEmail();}, "a").start();//暂停两毫秒,保证a线程先启动try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {phoneOne.sendSMS();}, "b").start();}}

获取静态同步方法,需要持有类锁,普通同步方法,获取的是对象锁,所以b线程不需要等到a线程释放完类锁,就能能获取到对象锁。

在这里插入图片描述
在这里插入图片描述

3、公平锁和非公平锁

3.1、非公平锁

import java.util.concurrent.locks.ReentrantLock;/*** @Description: 非公平锁* @Author: yangyb* @Date:2022/10/5 11:38* Version: 1.0**/
class Ticket {private int number = 20;// 默认为非公平锁ReentrantLock lock = new ReentrantLock();public void sale() {lock.lock();try {if (number > 0) {System.out.println(Thread.currentThread().getName() + "售出了第" + number-- + "张票,还剩" + number + "票");}} finally {lock.unlock();}}
}public class SaleTicketDemo {public static void main(String[] args) {Ticket ticket = new Ticket();new Thread(() -> {for (int i = 0; i < 55; i++) {ticket.sale();}}, "a").start();new Thread(() -> {for (int i = 0; i < 55; i++) {ticket.sale();}}, "b").start();new Thread(() -> {for (int i = 0; i < 55; i++) {ticket.sale();}}, "c").start();}}

在这里插入图片描述

3.2、公平锁

import java.util.concurrent.locks.ReentrantLock;/*** @Description: 公平锁* @Author: yangyb* @Date:2022/10/5 11:38* Version: 1.0**/
class Ticket {private int number = 20;// 公平锁ReentrantLock lock = new ReentrantLock(true);public void sale() {lock.lock();try {if (number > 0) {System.out.println(Thread.currentThread().getName() + "售出了第" + number-- + "张票,还剩" + number + "票");}} finally {lock.unlock();}}
}public class SaleTicketDemo {public static void main(String[] args) {Ticket ticket = new Ticket();new Thread(() -> {for (int i = 0; i < 22; i++) {ticket.sale();}}, "a").start();new Thread(() -> {for (int i = 0; i < 22; i++) {ticket.sale();}}, "b").start();new Thread(() -> {for (int i = 0; i < 22; i++) {ticket.sale();}}, "c").start();}}

在这里插入图片描述
在这里插入图片描述

3.3、为什么会有公平锁和非公平锁的设计?为什么默认非公平?

  1. 恢复挂起的线程到真正锁的获取还是有时间差的,从开发人员来看这个时间微乎其微,但是从cpu的角度来看,这个时间差存在的还是很明显的。所以非公平锁能更充分的利用CPU的时间片,尽量减少cpu空闲状态时间。
  2. 使用多线程很重要的考量点是线程切换的开销,当采用非公平锁时,当1个线程请求锁获取同步状态,然后释放同步状态,所以刚释放锁的线程在此刻再次获取同步状态的概率就变得非常大,所以就减少了线程的开销。

3.4、何时用公平锁?何时用非公平锁?

如果为了更高的吞吐量,很显然非公平锁是比较合适的,因为节省很多线程切换时间,吞吐量自然就上去了;否则就用公平锁,大家公平使用。

4、可重入锁(又名递归锁)

4.1、概念

  • 指同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象得是同一个对象),不会因为之前已经获取过还没释放而阻塞。
    例如:如果是一个有symchronized修饰得递归调用方法,程序第二次进入被自己阻塞了,这不成自己阻塞自己了吗?显然就不合常规的处理逻辑了。所以Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可以一定程度避免死锁。
  • 总结:一个线程中的多个流程可以获取同一把锁,持有这把同步锁可以再次进入。即自己可以获取自己的内部锁。

4.2、隐式锁

隐式锁(即synchronized关键字使用的锁)默认是可重入锁。指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫可重入锁。简单的lai说就是,在一个synchronized修饰的方法或代码块的内部调用本类的其他synchronized修饰的方法或代码块时,是永远可以得到锁的。

/*** @Description: 隐式锁synchronized,同步代码块* @Author: yangyb* @Date:2022/10/6 19:08* Version: 1.0**/
public class ReEntryLockDemo {public static void main(String[] args) {Object object = new Object();new Thread(() -> {synchronized (object) {System.out.println(Thread.currentThread().getName() + "\t-------外层调用");synchronized (object) {System.out.println(Thread.currentThread().getName() + "\t-------中层调用");synchronized (object) {System.out.println(Thread.currentThread().getName() + "\t-------内层调用");}}}}, "t1").start();}}

在这里插入图片描述

/*** @Description: 隐式锁synchronized,同步方法* @Author: yangyb* @Date:2022/10/6 19:08* Version: 1.0**/
public class ReEntryLockDemo {public synchronized void m1(){System.out.println("---m1 "+Thread.currentThread().getName()+"\t ------come in!");m2();System.out.println("---m1 "+Thread.currentThread().getName()+"\t ------end m1!");}public synchronized void m2(){System.out.println("---m2 "+Thread.currentThread().getName()+"\t ------come in!");m3();}public synchronized void m3(){System.out.println("---m3 "+Thread.currentThread().getName()+"\t ------come in!");}public static void main(String[] args) {ReEntryLockDemo reEntryLockDemo = new ReEntryLockDemo();new Thread(reEntryLockDemo::m1,"t1").start();}}

在这里插入图片描述

4.2.1、Synchronized可重入的实现原理
在这里插入图片描述

4.3、显示锁(ReentrantLock)

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*** @Description: 显示锁ReentrantLock* @Author: yangyb* @Date:2022/10/6 19:08* Version: 1.0**/
public class ReEntryLockDemo {static Lock lock = new ReentrantLock();public static void main(String[] args) {new Thread(() -> {lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t 外层调用lock!");lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t 内层调用lock!");} finally {// 这里故意注释掉,让实现加锁次数和释放锁的次数不一样// 由于枷锁次数和释放锁次数不一样,第二个线程始终无法获取到锁,导致一直在等待//lock.unlock();}} finally {lock.unlock();}}, "t1").start();new Thread(() -> {lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t 调用lock!");} finally {lock.unlock();}}, "t2").start();}
}

在这里插入图片描述

5、死锁及排查

5.1、死锁

5.1.1、概念

在这里插入图片描述

5.1.2、代码示例

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*** @Description: 死锁* @Author: yangyb* @Date:2022/10/6 20:19* Version: 1.0**/
public class DeadlockDemo {static Lock lockA = new ReentrantLock();static Lock lockB = new ReentrantLock();public static void main(String[] args) {new Thread(() -> {lockA.lock();try {System.out.println(Thread.currentThread().getName() + "\t 线程持有lockA锁,去尝试持有lockB锁");lockB.lock();try {System.out.println(Thread.currentThread().getName() + "\t 尝试获取并持有了lockB锁");} finally {lockB.unlock();}} finally {lockA.unlock();}}, "A").start();new Thread(() -> {lockB.lock();try {System.out.println(Thread.currentThread().getName() + "\t 线程持有lockB锁,去尝试持有lockA锁");lockA.lock();try {System.out.println(Thread.currentThread().getName() + "\t 尝试获取并持有了lockA锁");} finally {lockA.unlock();}} finally {lockB.unlock();}}, "B").start();}
}

在这里插入图片描述

5.1.3、产生死锁的主要原因

  • 系统资源不足
  • 进程运行推进的顺序不合适
  • 资源分配不当

5.2、死锁排查

5.2.1、在终端使用命令行

  1. jps -l
    在这里插入图片描述

  2. jstack 17988
    在这里插入图片描述
    在这里插入图片描述

5.2.2、图形化

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

6、为什么任何一个对象都可以成为锁?

在这里插入图片描述
在这里插入图片描述

7、LockSupport与线程中断

7.1、线程中断

7.1.1、什么是中断机制?

首先,一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止,自己来决定自己的命运。所以,Thread.stop,Thread.suspend,Thread.resume都已经被废弃了。
其次,在java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。因此,Java提供了一种用于停止线程的协商机制——中断,即中断标识协商机制。
中断只是一种协作协商机制,Java中没有给中断增加任何语法,中断的过程完全需要程序员自己实现。
若要中断一个线程,你需要手动调用该线程的interrupt方法,该方法也仅仅是将线程对象的中断标识设成true,此时究竟该做什么需要你自己写代码实现。
每个线程对象中都有一个中断标识位,用于表示 线程是否被中断;该标识位为true表示中断,为false表示未中断;通过调用线程对象的interrupt方法将该线程的标识位设为true;可以在别的线程中调用,也可以在自己的线程中调用。

7.1.2、中断线程API常见的三大方法
在这里插入图片描述

  1. 如何停止中断允许中的线程?
    (1)同过一个volatile 变量实现
/*** @Description: 线程中断* @Author: yangyb* @Date:2022/10/7 1:40* Version: 1.0**/
public class InterruptedDemo {static volatile boolean isStop = false;public static void main(String[] args) {new Thread(() -> {while (true) {if (isStop) {System.out.println(Thread.currentThread().getName() + "\t isStop被修改为" + isStop + "线程终止!");break;}System.out.println(Thread.currentThread().getName() + "\t 线程正在执行!");}}, ":t1").start();try {TimeUnit.MILLISECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {isStop = true;}, "t2").start();}}

在这里插入图片描述
(2)同过AtomicBoolean 实现

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;/*** @Description: 线程中断* @Author: yangyb* @Date:2022/10/7 1:40* Version: 1.0**/
public class InterruptedDemo {static AtomicBoolean atomicBoolean=new AtomicBoolean(false);public static void main(String[] args) {new Thread(() -> {while (true) {if (atomicBoolean.get()) {System.out.println(Thread.currentThread().getName() + "\t atomicBoolean被修改为" + atomicBoolean.get() + "线程终止!");break;}System.out.println(Thread.currentThread().getName() + "\t 线程正在执行!");}}, ":t1").start();try {TimeUnit.MILLISECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {atomicBoolean.set(true);}, "t2").start();}}

在这里插入图片描述
(3)通过Thread类自带的pai来实现

import java.util.concurrent.TimeUnit;/*** @Description: 线程中断* @Author: yangyb* @Date:2022/10/7 1:40* Version: 1.0**/
public class InterruptedDemo {public static void main(String[] args) {Thread t1=new Thread(() -> {while (true) {if (Thread.currentThread().isInterrupted()) {System.out.println(Thread.currentThread().getName() + "\t isInterrupted()被修改为" + true + "线程终止!");break;}System.out.println(Thread.currentThread().getName() + "\t 线程正在执行!");}}, ":t1");t1.start();try {TimeUnit.MILLISECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(t1::interrupt, "t2").start();}}

在这里插入图片描述
源码分析:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2. 当前线程的中断标识为true,是不是线程就立刻停止?

import java.util.concurrent.TimeUnit;/*** @Description: 线程中断* @Author: yangyb* @Date:2022/10/7 1:40* Version: 1.0**/
public class InterruptedDemo {public static void main(String[] args) {Thread t1 = new Thread(() -> {for (int i = 0; i < 300; i++) {System.out.println("i=" + i);}System.out.println(Thread.currentThread().getName() + "\t 线程调用t1.interrupt()方法后的中断标识为" + Thread.currentThread().isInterrupted());}, ":t1");t1.start();System.out.println(t1.getName() + "\t 线程中默认的中断标识为" + t1.isInterrupted());//falsetry {TimeUnit.MILLISECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}t1.interrupt();//trueSystem.out.println(Thread.currentThread().getName() + "\t 线程调用t1.interrupt()方法后的第一次中断标识为" + t1.isInterrupted());try {TimeUnit.MILLISECONDS.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}// 中断标识重置为false,中断不活动的线程不会产生任何影响System.out.println(Thread.currentThread().getName() + "\t 线程调用t1.interrupt()方法,并等待t1线程执行完所有的逻辑后中断标识为" + t1.isInterrupted());}}

在这里插入图片描述
在这里插入图片描述
此时t1线程的中断标识已经置为true,然而并没有立即中断线程,而是继续执行了所有逻辑
在这里插入图片描述
注意:
在这里插入图片描述
总结:
中断只是一种协商机制,修改中断标识位仅此而已,不是立刻stop打断。

  1. 静态方法Thread.interrupted()谈谈你的理解?
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    示例:
    在这里插入图片描述

结论:
静态方法interrupted将会清除中断状态(传入的参数ClearInterrupted为true),实例方法isInterrupted则不会(传入的参数ClearInterrupted为false)。

4.三大方法对比总结
在这里插入图片描述

7.2、LockSupport

7.2.1、概念

LockSupport是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,当然阻塞之后肯定得有唤醒的方法。归根结底,LockSupport调用的Unsafe中的native代码。

LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。LockSupport 提供park()和unpark()方法实现阻塞线程和解除线程阻塞,LockSupport和每个使用它的线程都有一个许可(permit)关联。permit相当于1,0的开关,默认是0,调用一次unpark就加1变成1,调用一次park会消费permit, 也就是将1变成0,同时park立即返回。再次调用park会变成block(因为permit为0了,会阻塞在这里,直到permit变为1), 这时调用unpark会把permit置为1。每个线程都有一个相关的permit, permit最多只有一个,重复调用unpark也不会积累。

park() 和 unpark()不会有 Thread.suspend 和 Thread.resume 所可能引发的死锁问题,由于许可的存在,调用 park 的线程和另一个试图将其 unpark 的线程之间的竞争将保持活性。

如果调用线程被中断,则park方法会返回。同时park也拥有可以设置超时时间的版本。
在这里插入图片描述

7.2.2、线程等待唤醒机制

  1. 线程等待和唤醒的方法在这里插入图片描述
    (1)Object的wait和notify在这里插入图片描述
    在这里插入图片描述
    (2)Condition的await()和signal()方法

在这里插入图片描述
在这里插入图片描述
(3)LockSupport的park()和unpark()方法
park()和unpark()顺序执行

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;/*** @Description: LockSupport* @Author: yangyb* @Date:2022/10/7 7:38* Version: 1.0**/
public class LockSupportDemo {public static void main(String[] args){Thread t1=new Thread(()->{System.out.println(Thread.currentThread().getName()+"\t 线程---come in!");LockSupport.park();System.out.println(Thread.currentThread().getName()+"\t 线程---被唤醒");},"t1");t1.start();try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}new Thread(()->{LockSupport.unpark(t1);System.out.println(Thread.currentThread().getName()+"\t 线程发出通知给t1线程");},"t2").start();}
}

在这里插入图片描述
先执行unpark(),再执行unpark()

/*** @Description: LockSupport* @Author: yangyb* @Date:2022/10/7 7:38* Version: 1.0**/
public class LockSupportDemo {public static void main(String[] args) {Thread t1 = new Thread(() -> {try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "\t 线程---come in!");LockSupport.park();System.out.println(Thread.currentThread().getName() + "\t 线程---被唤醒");}, "t1");t1.start();new Thread(() -> {LockSupport.unpark(t1);System.out.println(Thread.currentThread().getName() + "\t 线程发出通知给t1线程");}, "t2").start();}
}

在这里插入图片描述
7.2.3、总结
在这里插入图片描述

在这里插入图片描述

7.2.3、面试题
在这里插入图片描述

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;/*** @Description: LockSupport* @Author: yangyb* @Date:2022/10/7 7:38* Version: 1.0**/
public class LockSupportDemo {public static void main(String[] args) {Thread t1 = new Thread(() -> {try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "\t 线程---come in!");LockSupport.park();LockSupport.park();System.out.println(Thread.currentThread().getName() + "\t 线程---被唤醒");}, "t1");t1.start();new Thread(() -> {LockSupport.unpark(t1);LockSupport.unpark(t1);LockSupport.unpark(t1);System.out.println(Thread.currentThread().getName() + "\t 线程发出通知给t1线程");}, "t2").start();}
}

在这里插入图片描述

8、Java内存模型即JMM

8.1、计算机硬件存储体系

在这里插入图片描述

在这里插入图片描述

8.2、JMM

8.2.1、JMM简介

在这里插入图片描述

8.2.2、JMM规范下,三大特性
(1)可见性
指当一个线程修改了某一个共享变量的值,其他线程能否立即知道该变更。JMM规定所有的变量都存储在主内存中。
在这里插入图片描述
在这里插入图片描述
(2)原子性
指一个操作是不可打断的,即多线程环境下,操作不能被其他线程干扰。
(3)有序性
在这里插入图片描述
在这里插入图片描述
8.2.3、JMM规范下,多线程对变量的读写过程
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

8.2.3、JMM规范下,多线程先行发生原则之happens-before
(1)简介
在这里插入图片描述
(2)happens-before总原则
如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
两个操作之间存在happens-before关系,并不意味着一定要按照happens-before原则制定的顺序来执行。如果重排序之后的执行结果与按照happens-before关系来执行最终的结果一致,那么这种重排序并不非法。
(2)happens-before的8条规则

  1. 次序规则:一个线程内,按照代码顺序,写在前面的操作先行发生于写在后面的操作。
  2. 锁定规则:一个unlock操作先行发生于后面(这里的“后面”是指时间上的先后)对同一个锁的lock操作。
  3. volatile变量规则:对一个volatile变量的写操作先行发生于后面对这个变量的读操作,前面的写对后面的读是可见的,这里的“后面”同样是指时间上的先后。
  4. 传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出来操作A先行发生于操作C。
  5. 线程启动规则(Thread Start Rule):Thread对象的start()方法先行发生于此线程的每一个动作。
  6. 线程中断规则(Thread Interruption Rule):
    在这里插入图片描述
  7. 线程终止规则(Thread Termination Rule):线程中的所有操作都先行发生于对此线程的终止检测,我们可以通过isAlive()等手段检测线程是否已经终止执行。
  8. 对象终结规则(Finalizer Rule):一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。
    (4)总结
    在这里插入图片描述

9、volatile

9.1、volatile之两大特性

被volatile修饰的变量有两大特点:可见性和有序性

  1. 可见性:保证不同线程对某个变量完成操作后的结果对其它线程及时可见,即该共享变量一旦改变所有线程立即可见。

9.1.1、volatile内存语义
在这里插入图片描述
9.1.2、vlolatile如何保证可见性和有序性?
内存屏障(Memory Barrier)

9.2、内存屏障(Memory Barrier)

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

9.3、读写屏障之插入策略

在这里插入图片描述

  1. 读屏障

在这里插入图片描述

在这里插入图片描述

  1. 写屏障

在这里插入图片描述
在这里插入图片描述

  1. volatile变量的读写过程:JAVA内存模型中定义的8种每个线程自己的工作内存与主物理内存之间的原子操作。
    在这里插入图片描述
    在这里插入图片描述

9.4、volatile无原子性

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

9.5、指令禁重排

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

9.6、volatile使用场景

  1. 单一赋值可以,但是含复合运算赋值不可以(i++)类
volatile int a=10;
volatile boolean flag=false;
  1. 状态标志,判断业务是否结束
import java.util.concurrent.TimeUnit;/*** @Description: volatile使用:作为一个布尔状态标志,用于指示发生了一个重要的一次性事件,列如完成初始化或任务结束* 理由:状态标志并不依赖于程序内任何其他状态,且通常只有一种状态装换* 例子:判断业务是否结束* @Author: yangyb* @Date:2022/10/7 17:22* Version: 1.0**/
public class UseVolatileDemo {private volatile static boolean flog=true;public static void main(String[] args){new Thread(()->{while (flog){System.out.println("do something");}},"t1").start();try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}new Thread(()->{flog=false;},"t2").start();}}
  1. 开销较低的读、写锁策略
    /** 使用:当读远多于写,结合使用内部锁和volatile变量来减少同步的开销* 理由:利用volatile保证读取操作的可见性,利用synchronized保证复合操作的原子性* */public class Counter{private volatile int value;public int getValue(){return value; // 利用volatile保证读取操作的可见性}public synchronized int increment(){return value++; // 利用synchronized保证复合操作的原子性}}
  1. DCL双检锁的发布
    在这里插入图片描述
    单线程:
    在这里插入图片描述
    多线程:
    在这里插入图片描述
    在这里插入图片描述
    解决方法:
    在这里插入图片描述

9.7、总结

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

10、CAS

10.1、CAS简介

10.1.1、说明

  • compare and swap的缩写,中文翻译成比较并交换,实现并发算法时场用到的一种技术。它包含三个操作数——内存位置、预期原值及更新值。
  • 执行CAS操作的时候,将内存位置的值与预期原值比较:如果相匹配,那么处理器会自动将该位置值更新为新值,如果不匹配,处理器不做任何操作,多个线程同时执行CAS操作只有一个会成功。

10.1.2、原理
CAS有三个操作数,位置内存值V,旧的预期值A,要修改的更新值B。当且仅当旧的预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做或重来。
在这里插入图片描述
10.1.2、硬件级别保证
在这里插入图片描述
源码分析
在这里插入图片描述

/*** Atomically sets the value to the given updated value* if the current value {@code ==} the expected value.** @param expect the expected value* @param update the new value* @return {@code true} if successful. False return indicates that* the actual value was not equal to the expected value.*/public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}

问题:Unsafe类是什么?

10.2、CAS底层原理-Unsafe类

10.2.1、源码分析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

10.2.2、总结
在这里插入图片描述

10.3、CAS之原子引用 AtomicReference

import java.util.concurrent.atomic.AtomicReference;/*** @Description: 原子引用* @Author: yangyb* @Date:2022/10/7 21:17* Version: 1.0**/class User{private int age;private String name;public User(int age, String name) {this.age = age;this.name = name;}@Overridepublic String toString() {return "User{" +"age=" + age +", name='" + name + '\'' +'}';}
}public class AtomicReferenceDemo {public static void main(String[] args){AtomicReference<User> userAtomicReference = new AtomicReference<>();User zhangSan = new User(10, "张三");User liSi = new User(12, "李四");userAtomicReference.set(zhangSan);System.out.println(userAtomicReference.compareAndSet(zhangSan,liSi)+"\t"+userAtomicReference.get().toString());System.out.println(userAtomicReference.compareAndSet(zhangSan,liSi)+"\t"+userAtomicReference.get().toString());}}

在这里插入图片描述

10.4、CAS与自旋锁

10.4.1、简介

在这里插入图片描述
在这里插入图片描述

10.4.2、手写自旋锁,借鉴CAS思想

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;/*** @Description: 自旋锁实现* 目标:实现一个自旋锁,复习CAS思想* 自旋锁的好处:循环比较获取没有类似wait的阻塞* 通过CAS操作完成自旋锁,A线程先进来调用myLock方法自己持有锁5秒钟,B线程随后进来发现当前线程持有锁,* 所以只能通过自旋等待,直到A释放锁后B随后抢到* @Author: yangyb* @Date:2022/10/8 20:50* Version: 1.0**/
public class SpinLockDemo {AtomicReference<Thread> atomicReference = new AtomicReference<>();public void lock() {// 获取当前线程Thread thread = Thread.currentThread();System.out.println(Thread.currentThread().getName() + "\t 线程come in!");while (!atomicReference.compareAndSet(null, thread)) {System.out.println(Thread.currentThread().getName() + "\t 线程 自旋中!,等待A线程释放锁");}System.out.println(Thread.currentThread().getName() + "\t 线程 自旋结束");}public void unlock() {// 获取当前线程Thread thread = Thread.currentThread();atomicReference.compareAndSet(thread, null);System.out.println(Thread.currentThread().getName() + "\t task over,unlock!");}public static void main(String[] args) {SpinLockDemo spinLockDemo = new SpinLockDemo();new Thread(() -> {spinLockDemo.lock();// 休息5秒钟try {TimeUnit.MILLISECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}spinLockDemo.unlock();}, "A").start();// 休息1秒钟,保证A线程先启动try {TimeUnit.MILLISECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {spinLockDemo.lock();// 休息5秒钟try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}spinLockDemo.unlock();}, "B").start();}}

在这里插入图片描述
在这里插入图片描述

10.5、CAS两大缺点

10.5.1、循环时间长,CPU开销大
在这里插入图片描述

10.5.2、ABA问题的产生
在这里插入图片描述

10.6、邮戳AtomicStampedReference

(1)没有用AtomicStampedReference,会发生ABA问题

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;/*** @Description: ABADemo* @Author: yangyb* @Date:2022/10/8 21:48* Version: 1.0**/
public class ABADemo {static AtomicInteger atomicInteger = new AtomicInteger(100);public static void main(String[] args) {new Thread(() -> {atomicInteger.compareAndSet(100, 101);try {TimeUnit.MILLISECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}atomicInteger.compareAndSet(101, 100);}, "A").start();new Thread(() -> {try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(atomicInteger.compareAndSet(100, 2022) + "\t" + atomicInteger.get());}, "B").start();}
}

A线程中间悄无声息的替换了数据,B没有
在这里插入图片描述
(2)加上AtomicStampedReference,解决ABA问题

/*** @Description: ABADemo* @Author: yangyb* @Date:2022/10/8 21:48* Version: 1.0**/
public class ABADemo {static AtomicInteger atomicInteger = new AtomicInteger(100);static AtomicStampedReference stampedReference = new AtomicStampedReference<>(100, 1);public static void main(String[] args) {new Thread(() -> {int stamp = stampedReference.getStamp();System.out.println(Thread.currentThread().getName() + "\t" + "首次版本号:" + stamp);//暂停一下,保证后面的B线程拿到的版本号和我一样try {TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}stampedReference.compareAndSet(100, 101, stampedReference.getStamp(), stampedReference.getStamp() + 1);System.out.println(Thread.currentThread().getName() + "\t" + "2次版本号:" + stampedReference.getStamp());stampedReference.compareAndSet(101, 100, stampedReference.getStamp(), stampedReference.getStamp() + 1);System.out.println(Thread.currentThread().getName() + "\t" + "3次版本号:" + stampedReference.getStamp());}, "A").start();new Thread(() -> {int stamp = stampedReference.getStamp();System.out.println(Thread.currentThread().getName() + "\t" + "首次版本号:" + stamp);// 暂停一秒钟,等待上面的t3线程,发生ABA问题try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}boolean b = stampedReference.compareAndSet(100, 202, stamp, stampedReference.getStamp() + 1);System.out.println(b + "\t" + stampedReference.getReference() + "\t" + stampedReference.getStamp());}, "B").start();}
}

在这里插入图片描述

11、原子类

在这里插入图片描述

11.1、基本类型原子类

  1. AtomicInterger
  2. AtomicBoolean
  3. AtomicLong
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;/*** @Description: TODO* @Author: yangyb* @Date:2022/10/9 22:00* Version: 1.0**/class MyNumber {AtomicInteger atomicInteger = new AtomicInteger();public void addPlusPlus() {atomicInteger.getAndIncrement();}
}public class AtomicIntegerDemo {public static final int SIZE = 50;public static void main(String[] args) throws InterruptedException {MyNumber myNumber = new MyNumber();CountDownLatch countDownLatch = new CountDownLatch(SIZE);for (int i = 1; i < SIZE; i++) {new Thread(() -> {try {for (int j = 1; j < 1000; j++) {myNumber.addPlusPlus();}} finally {countDownLatch.countDown();}}, String.valueOf(i)).start();}countDownLatch.await();System.out.println(Thread.currentThread().getName() + "\t" + "result: " + myNumber.atomicInteger.get());}
}

11.2、数组类型原子类

  1. AtomicIntegerArray
  2. AtomicLongArray
  3. AtomicReferenceArray
    在这里插入图片描述

11.3、引用类型原子类

  1. AtomicReference

  2. AtomicStampedReference
    在这里插入图片描述

  3. AtomicMarkableReference

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

11.4、对象的属性修改原子类

  1. AtomicIntegerFieldUpdater
  2. AtomicLongFieldUpdater
  3. AtomicReferenceFieldUpdater
    在这里插入图片描述

在这里插入图片描述

11.5、原子操作增强类

在这里插入图片描述

在这里插入图片描述

12、ThreadLocal

12.1、简介

ThreadLocal提供线程局部变量。这些变量与正常的变量不同,因为每一个线程在访问ThreadLocal实列的时候(通过其get或set方法)都有自己的、独立初始化的变量副本。ThreadLocal实例通常是类中的私有静态字段,使用它的目的是希望将状态(列入,用户ID或事物ID)与线程关联起来。
在这里插入图片描述

/*** @Description: ThreadLocal* @Author: yangyb* @Date:2022/10/10 21:58* Version: 1.0**/
// 资源类
class House{int saleCount =0;public synchronized void saleHouse(){++saleCount;}ThreadLocal<Integer> saleVolume=ThreadLocal.withInitial(()->0);public void saleVolumeByThreadLocal(){saleVolume.set(1+saleVolume.get());}
}
public class ThreadLocalDemo {public static void main(String[] args){House house = new House();for(int i=1;i<=5;i++){new Thread(()->{try {int size=new Random().nextInt(5)+1;for(int j=1;j<=size;j++){house.saleHouse();house.saleVolumeByThreadLocal();}System.out.println(Thread.currentThread().getName()+"\t"+"共计卖出多少套:"+house.saleVolume.get());}finally {house.saleVolume.remove();}},String.valueOf(i)).start();}try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"\t"+"共计卖出多少套:"+house.saleCount);}}

在这里插入图片描述

12.2、ThreadLocal规范要求

在这里插入图片描述

/*** @Description: ThreadLocal规范* @Author: yangyb* @Date:2022/10/10 22:37* Version: 1.0**/
class MyData{ThreadLocal<Integer> threadLocalField=ThreadLocal.withInitial(()->0);public void add(){threadLocalField.set(1+threadLocalField.get());}
}
public class ThreadLocalDemoTwo {public static void main(String[] args){MyData myData = new MyData();ExecutorService threadPool = Executors.newFixedThreadPool(3);try {for(int i=0;i<10;i++){threadPool.submit(()->{Integer beforeInteger = myData.threadLocalField.get();myData.add();Integer afterInteger = myData.threadLocalField.get();System.out.println(Thread.currentThread().getName()+"\t"+"beforeInteger:"+beforeInteger+"\t"+"afterInteger:"+afterInteger);});}}finally {threadPool.shutdown();}}}

在这里插入图片描述
使用remove()方法以后:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** @Description: ThreadLocal规范* @Author: yangyb* @Date:2022/10/10 22:37* Version: 1.0**/
class MyData{ThreadLocal<Integer> threadLocalField=ThreadLocal.withInitial(()->0);public void add(){threadLocalField.set(1+threadLocalField.get());}
}
public class ThreadLocalDemoTwo {public static void main(String[] args){MyData myData = new MyData();ExecutorService threadPool = Executors.newFixedThreadPool(3);try {for(int i=0;i<10;i++){threadPool.submit(()->{try {Integer beforeInteger = myData.threadLocalField.get();myData.add();Integer afterInteger = myData.threadLocalField.get();System.out.println(Thread.currentThread().getName()+"\t"+"beforeInteger:"+beforeInteger+"\t"+"afterInteger:"+afterInteger);}finally {myData.threadLocalField.remove();}});}}finally {threadPool.shutdown();}}}

在这里插入图片描述

12.3、总结

在这里插入图片描述

12.4、Thread、ThreadLocal、ThreadLocalMap三者的关系

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

12.5、弱引用的引出

在这里插入图片描述
在这里插入图片描述

12.6、强引用

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

12.7、软引用

在这里插入图片描述
在这里插入图片描述

12.8、弱引用

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

12.9、虚引用

在这里插入图片描述

12.10、四中引用总结

在这里插入图片描述

12.11、ThreadLocal为何用弱引用

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

12.12、总结

在这里插入图片描述
在这里插入图片描述

13、对象内存布局和对象头

13.1、简介

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

13.2、对象头

1、对象标记Mark Word
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
总结:
在这里插入图片描述

2、类元信息(又叫类型指针)
在这里插入图片描述
总结:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是那个类的实例。

3、对象头有多大?
在这里插入图片描述
在这里插入图片描述

13.3、实例数据和对齐填充

1、实例数据

在这里插入图片描述

2、对齐填充

在这里插入图片描述

13.4、对象分代年龄

在这里插入图片描述

14、synchronized与锁升级

14.1、简介

在这里插入图片描述
synchronized锁:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

14.2、synchronized锁升级流程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

14.3、无锁

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

14.4、偏向锁

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
理论实现:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
案例说明:
在这里插入图片描述
在这里插入图片描述
重要参数说明:
在这里插入图片描述
在这里插入图片描述
代码示例:
在这里插入图片描述

在这里插入图片描述

14.5、偏向锁撤销

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
偏向锁废弃:
在这里插入图片描述

14.6、轻量级锁

在这里插入图片描述
在这里插入图片描述
轻量级锁的获取:
在这里插入图片描述

在这里插入图片描述
补充:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

14.7、重锁

在这里插入图片描述

14.8、锁升级后和hashCode的关系

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

14.9、各种锁优缺点总结

在这里插入图片描述
在这里插入图片描述

14.10、锁消除

在这里插入图片描述

14.11、锁粗化

在这里插入图片描述

14.12、总结

在这里插入图片描述

15、AQS

15.1、简介

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

15.2、AQS作用

15.2.1、源码查看

在这里插入图片描述
在这里插入图片描述

15.3、AQS之state和LCH队列

在这里插入图片描述

15.4、AQS自身属性和Node节点

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

15.5、AQS源码分析

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

15.5.1、非公平锁
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

15.6、AQS源码总结

。。。。。。。。。。。。。。。。。。。。。。。。。。。

16、读写锁

16.1、简介

在这里插入图片描述

16.2、锁演化

在这里插入图片描述

在这里插入图片描述

16.2.1、读写锁的意义和特点
在这里插入图片描述

16.3、锁降级

在这里插入图片描述

16.4、不可锁升级

在这里插入图片描述
在这里插入图片描述
结论:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

17、stampedLock

17.1、简介

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

17.2、stampedLock锁特点

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
缺点:
在这里插入图片描述

JAVA并发编程之锁

相关文章

  1. 电子拣货标签13代系统简介

    CK_Label_v13一、产品参数 1. 电池供电版 产品型号 CK_Label_v13 尺寸 70x34.7x13.6mm 屏幕尺寸 2.1 inch 分辨率 250*122 像素密度 130dpi 显示技术 电子墨水屏显示 显示颜色 黑/白 外观颜色 黑色 按键 1 指示灯 1 RGB灯 灯光颜色 7种(红/绿/蓝/黄/紫/白…...

    2023/3/28 15:49:24
  2. Ubuntu下安装和编译onnxruntime

    onnxruntime是一种用于onnx格式的机器学习模型的高性能推理引擎&#xff0c;适用于Linux&#xff0c;Windows、Mac及嵌入式设备。这篇博客记录了编译onnxruntime的步骤及在此过程种遇到的问题及解决方法。1 下载git clone --depth 1 --branch v1.12.1 --recursive https://gith…...

    2023/3/28 15:47:54
  3. Asp.net core 依赖注入 (带案例以及注释理解)

    1.很多朋友不知道什么是依赖注入&#xff0c;接下来我用比较通俗易懂的话语 来帮助大家理解 依赖注入&#xff08;Dependency Injection&#xff0c;简称DI&#xff09;是一种设计模式&#xff0c;用于减少组件之间的耦合度。它的核心思想是&#xff0c;将组件之间的依赖关系从…...

    2023/3/28 15:47:43
  4. Java分布式锁面试题

    1.为什么需要分布式锁? public synchronized void test() {System.out.println("获取到锁"); } public void test2() {synchronized (Test.class) {System.out.println("获取到锁");} }假设我们把上述代码部署到多台服务器上&#xff0c;这个互斥锁还能生…...

    2023/3/28 15:46:29
  5. 微信小程序-微信小程序登录流程(一)

    微信小程序&#xff0c;小程序的一种&#xff0c;英文名Wechat Mini Program&#xff0c;是一种不需要下载安装即可使用的应用&#xff0c;它实现了应用“触手可及”的梦想&#xff0c;用户扫一扫或搜一下即可打开应用 冷启动&#xff1a; 小程序首次打开或销毁后再次被打开&…...

    2023/3/28 15:43:29
  6. C++初级教程(三)

    一、C的一维数组 C 支持数组数据结构&#xff0c;它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据&#xff0c;但它往往被认为是一系列相同类型的变量。 数组的声明并不是声明一个个单独的变量&#xff0c;而是声明一个数组变量&#xff0c;比如 …...

    2023/3/28 15:42:13
  7. Github上得分最高的20个项目

    freeCodeCamp&#xff1a;一个流行的编程教育平台&#xff0c;目标是让任何人都能够免费学习编程。 Vscode&#xff1a;一个流行的跨平台文本编辑器&#xff0c;支持各种语言和框架&#xff0c;可通过扩展进行自定义。 React&#xff1a;一个流行的 JavaScript 库&#xff0c…...

    2023/3/28 15:41:28
  8. 【java缓存、redis缓存、guava缓存】java中实现缓存的几种方式

    一、通过HashMap实现缓存 这种方式可以简单实现本地缓存&#xff0c;但是实际开发中不推荐使用&#xff0c;下面我们来实现一下这种方式。 首先创建一个管理缓存的类 public class LocalCache {public static HashMap<String,String> cache new HashMap<>();sta…...

    2023/3/28 15:37:22
  9. 从应用火到股市 AIGC概念股连涨仨月

    AI有变革生产力之势&#xff0c;证券市场闻风而动。同花顺数据显示&#xff0c;AIGC概念指数年内上涨近60%&#xff0c;涨幅是上证指数的10倍多。 具体到个股&#xff0c;软通动力、远大智能等搭上AI风口的企业服务商“老树发新芽”&#xff0c;直接涨停。游戏板块霸榜产品回报…...

    2023/3/28 15:37:03
  10. linux突破git clone下载限制

    前言 本人要在linux下载一个2.5G的文件&#xff0c;但只有20kb/s&#xff0c;实在忍受不了&#xff0c;最终利用工具实现了800kb/s的速度&#xff0c;除了利用工具的方法&#xff0c;本人都尝试了&#xff0c;但是几乎没有效果&#xff0c;我也会罗列出来&#xff0c;可自行尝试…...

    2023/3/28 15:36:50
  11. x86架构利用docker去编译arm64的应用程序

    文章目录1. 交叉编译&#xff1a;toolchain2. 隔离挂载的方式&#xff1a;3. QEMU 或其他模拟器来实际运行dockerx86架构实现多平台系统代码的编译&#xff0c;实现方式有多种&#xff1a;交叉编译&#xff1a;toolchain 【新的第三方库不好处理】隔离挂载的方式 【速度慢&…...

    2023/3/28 15:33:40
  12. Apache Camel

    目录儿一、简介二、核心总结一、简介 Camel is an Open Source integration framework that empowers you to quickly and easily integrate various systems consuming or producing data. Camel 是一个开源的集成框架&#xff0c;能够让开发者快速、轻松地整合/集成不同的应…...

    2023/3/28 15:33:26
  13. Java学习记录

    阅读前请看一下&#xff1a;我是一个热衷于记录的人&#xff0c;每次写博客会反复研读&#xff0c;尽量不断提升博客质量。文章设置为仅粉丝可见&#xff0c;是因为写博客确实花了不少精力。希望互相进步谢谢&#xff01;&#xff01; 文章目录阅读前请看一下&#xff1a;我是一…...

    2023/3/28 15:32:34
  14. 「数据架构」TOGAF建模之数据架构:数据迁移图

    数据迁移图的目的是显示从源应用程序到目标应用程序的数据流。该图将提供源/目标分布的可视化表示&#xff0c;并作为数据审核和建立可追溯性的工具。该图可以根据需要进行细化或增强。例如&#xff0c;该图可以只包含迁移环境的总体布局&#xff0c;也可以包含单个应用程序元数…...

    2023/3/28 15:29:54
  15. MySQL数据库之——高级[进阶]SQL语句(一)SQL高级语句、函数等

    文章目录一、SQL高级语句1、 SELECT2、DISTINCT3、WHER条件查询4 、AND OR5、IN6、BETWEEN7、通配符8、LIKE9、ORDER BY二、SQL函数1、数学函数&#xff1a;2、聚合函数&#xff1a;3、字符串函数&#xff1a;4、GROUP BY5、HAVING6、别名7、连接查询8、子查询9、EXISTS一、SQL…...

    2023/3/28 15:24:30
  16. Java实现一个简单的东南西北中的面板

    目录 一、前言 二、代码部分 1.代码 三、程序运行结果&#xff08;面板弹出&#xff09; 四、涉及到的知识点代码 一、前言 1.本代码是我在上学时写的&#xff0c;有一些地方没能完美实现&#xff0c;请包涵也请多赐教&#xff01; 2.本弹窗界面可以根据简单的要求进行…...

    2023/3/28 15:24:15
  17. 阿里十年资深程序员吐血总结之Java代理模式

    阿里十年资深程序员吐血总结之Java代理模式 文章目录阿里十年资深程序员吐血总结之Java代理模式1.接口代理2.类代理3.动态代理都是通过反射实现的吗4.jdk动态代理和cglib动态代理的区别Java代理模式是怎么实现的​ Java 代理模式是一种常见的设计模式&#xff0c;它可以在不改变…...

    2023/3/28 15:23:34
  18. ServletAPI详解(一)

    目录 1.Tomcat简介 2.Servlet 是什么? 3.编写Servlet 程序步骤 1.创建一个Maven项目 2.引入依赖 3.创建目录 4.编写代码 5.打包程序 6.部署程序 7.验证程序 4.使用SmartTomcat插件部署 1.Tomcat简介 Tomcat是一个开源的,基于java实现的被广泛使用的web服务器.web服…...

    2023/3/28 15:22:50
  19. 全球第三机械制造商徐工集团表示与 Conflux、Zen Spark Technology 建立合作伙伴关系公司

    【ConfluxNews】2023.3.28 ----------------------------- 1.【网络状态】当前版本V2.2.2&#xff0c;全网算力≈10T&#xff0c;昨日交易次数58K&#xff0c;昨日新增账户0.69K&#xff0c;昨日新增合约13个&#xff1b; 2.【POS参数】总锁仓244&#xff08;-3&#xff09;&a…...

    2023/3/28 15:20:26
  20. 为什么所谓的“自律”一定要跟坚持挂钩呢?懂一点“行为设计学”,升级对“意义”的认知

    为什么所谓的“自律”一定要跟坚持挂钩呢&#xff1f;懂一点“行为设计学”&#xff0c;升级对“意义”的认知00 导读01 懂一点“行为设计学”02 升级你对“意义”的认知03 小结00 导读 世界上最成功的人之一&#xff0c;风险投资家雷达利欧&#xff08;Ray Dalio&#xff09;…...

    2023/3/28 15:19:52
  21. 中国各省份研究随笔

    一.陕西 1.陕北榆林 黄土荒原&#xff0c;萧瑟荒凉&#xff0c;生活艰难&#xff0c;亩产60斤小麦。2002年开始榆林富裕&#xff0c;因为煤炭资源&#xff0c;易开采、质量好 2002年之前煤炭没有人开采&#xff0c;因为价格只有14元一吨&#xff0c;2001年加入WTO&#xff0…...

    2023/3/28 15:19:31
  22. 配置JRebel热部署

    1&#xff1a;此对应设置的是IntelliJ IDEA 2022.1.3 (Ultimate Edition)的热部署设置 2&#xff1a;激活插件失败&#xff0c;提示【LS client not configued】原因是版本过高&#xff0c;解决办法&#xff1a;手动下载jRebel,官网去找到过去版本的jRebel&#xff0c;地址&am…...

    2023/3/28 15:16:22
  23. OpenGL | 渲染带透明通道的2D精灵

    一、Alpha测试 Alpha 测试的基本原理为&#xff1a;当绘制一个片元时&#xff0c;首先检测其 Alpha 值&#xff0c;若 Alpha 值满足要求&#xff0c;就通过测试&#xff0c;绘制此片元&#xff1b;否则丢弃此片元&#xff0c;不进行绘制。 glEnable(GL_ALPHA_TEST)&#xff1b…...

    2023/3/28 15:15:20
  24. PCIE中断发送与接收

    1. EndPoint模式下中断的发送 在EP模式下&#xff0c;PCIE会根据配置信息只产生MSI或者INTA中断中的一种&#xff0c;然后传送到RC端。 1.1 INTA中断的发送 INTA中断以带内消息(Assert_INTA/Deassert_INTA)的形式传递中断&#xff0c;其作用相当于传统PCI总线中使用边带中断…...

    2023/3/28 15:15:04
  25. SQL Server底层架构技术对比

    背景 数据库是信息化的基石&#xff0c;支撑着整个业务系统&#xff0c;发挥着非常重要的作用&#xff0c;被喻为“IT的心脏”。因此&#xff0c;让数据库安全、稳定、高效地运行已经成为IT管理者必须要面对的问题。数据库在底层架构层面需要满足以下几点建设要求&#xff1a; …...

    2023/3/28 15:14:36
  26. 你真的了解变量吗?

    变量是编程语言中最基本的术语&#xff0c;用来代指计算机中存放的可变数据——如整数、小数、字符或被调用的一片内存空间。 变量可以保存不同的变量类型&#xff0c;比如整型、布尔型、对象、数组等等&#xff0c;每一个变量在内存中对应着一个内存地址&#xff0c;但是如果…...

    2023/3/28 15:13:20
  27. 一文彻底搞懂为什么OpenCV用GPU/cuda跑得比用CPU慢?

    一、原因总结 最近项目需要&#xff0c;发现了这个问题。网上找原因&#xff0c;汇总起来&#xff0c;有以下几点原因&#xff1a; 1、首先对于任何一个CUDA程序&#xff0c;在调用它的第一个CUDA API时后都要花费秒级的时间去初始化运行环境&#xff0c;后续还要分配显存&am…...

    2023/3/28 15:11:34
  28. 数仓必备概念

    目录 数据仓库 三范式建模 维度建模 数据仓库 是一个面向主题的&#xff08;Subject&#xff09;、集成的&#xff08;Integrated&#xff09;、非易失&#xff08;Non-Volatile&#xff09;、时变性&#xff08;Time Variant&#xff09;的数据集合&#xff0c;用于支持管理…...

    2023/3/28 15:09:12
  29. Spring Security 6.0系列【5】源码篇之认证组件

    有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot 版本 3.0.4 本系列Spring Security 版本 6.0.2 源码地址:https://gitee.com/pearl-organization/study-spring-security-demo 文章目录 前言认证机制认证组件SecurityContextHolderSecurityContextAuthentication…...

    2023/3/28 15:08:48
  30. webpack 配置介绍

    一、webpack webpack 仅是一个打包工具&#xff0c;不会处理打包过程的东西例如 es6等新特性&#xff0c;如果需要处理这些新特性需要配置单独的加载bebblle-loader 等。 "devDependencies": {"webpack": "^5.76.3", // 打包工具"…...

    2023/3/28 15:06:40

最新文章

  1. 电子拣货标签13代系统简介

    CK_Label_v13一、产品参数 1. 电池供电版 产品型号 CK_Label_v13 尺寸 70x34.7x13.6mm 屏幕尺寸 2.1 inch 分辨率 250*122 像素密度 130dpi 显示技术 电子墨水屏显示 显示颜色 黑/白 外观颜色 黑色 按键 1 指示灯 1 RGB灯 灯光颜色 7种(红/绿/蓝/黄/紫/白…...

    2023/3/28 15:49:24
  2. Ubuntu下安装和编译onnxruntime

    onnxruntime是一种用于onnx格式的机器学习模型的高性能推理引擎&#xff0c;适用于Linux&#xff0c;Windows、Mac及嵌入式设备。这篇博客记录了编译onnxruntime的步骤及在此过程种遇到的问题及解决方法。1 下载git clone --depth 1 --branch v1.12.1 --recursive https://gith…...

    2023/3/28 15:47:54
  3. Asp.net core 依赖注入 (带案例以及注释理解)

    1.很多朋友不知道什么是依赖注入&#xff0c;接下来我用比较通俗易懂的话语 来帮助大家理解 依赖注入&#xff08;Dependency Injection&#xff0c;简称DI&#xff09;是一种设计模式&#xff0c;用于减少组件之间的耦合度。它的核心思想是&#xff0c;将组件之间的依赖关系从…...

    2023/3/28 15:47:43
  4. Java分布式锁面试题

    1.为什么需要分布式锁? public synchronized void test() {System.out.println("获取到锁"); } public void test2() {synchronized (Test.class) {System.out.println("获取到锁");} }假设我们把上述代码部署到多台服务器上&#xff0c;这个互斥锁还能生…...

    2023/3/28 15:46:29
  5. 微信小程序-微信小程序登录流程(一)

    微信小程序&#xff0c;小程序的一种&#xff0c;英文名Wechat Mini Program&#xff0c;是一种不需要下载安装即可使用的应用&#xff0c;它实现了应用“触手可及”的梦想&#xff0c;用户扫一扫或搜一下即可打开应用 冷启动&#xff1a; 小程序首次打开或销毁后再次被打开&…...

    2023/3/28 15:43:29
  6. C++初级教程(三)

    一、C的一维数组 C 支持数组数据结构&#xff0c;它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据&#xff0c;但它往往被认为是一系列相同类型的变量。 数组的声明并不是声明一个个单独的变量&#xff0c;而是声明一个数组变量&#xff0c;比如 …...

    2023/3/28 15:42:13
  7. Github上得分最高的20个项目

    freeCodeCamp&#xff1a;一个流行的编程教育平台&#xff0c;目标是让任何人都能够免费学习编程。 Vscode&#xff1a;一个流行的跨平台文本编辑器&#xff0c;支持各种语言和框架&#xff0c;可通过扩展进行自定义。 React&#xff1a;一个流行的 JavaScript 库&#xff0c…...

    2023/3/28 15:41:28
  8. 【java缓存、redis缓存、guava缓存】java中实现缓存的几种方式

    一、通过HashMap实现缓存 这种方式可以简单实现本地缓存&#xff0c;但是实际开发中不推荐使用&#xff0c;下面我们来实现一下这种方式。 首先创建一个管理缓存的类 public class LocalCache {public static HashMap<String,String> cache new HashMap<>();sta…...

    2023/3/28 15:37:22
  9. 从应用火到股市 AIGC概念股连涨仨月

    AI有变革生产力之势&#xff0c;证券市场闻风而动。同花顺数据显示&#xff0c;AIGC概念指数年内上涨近60%&#xff0c;涨幅是上证指数的10倍多。 具体到个股&#xff0c;软通动力、远大智能等搭上AI风口的企业服务商“老树发新芽”&#xff0c;直接涨停。游戏板块霸榜产品回报…...

    2023/3/28 15:37:03
  10. linux突破git clone下载限制

    前言 本人要在linux下载一个2.5G的文件&#xff0c;但只有20kb/s&#xff0c;实在忍受不了&#xff0c;最终利用工具实现了800kb/s的速度&#xff0c;除了利用工具的方法&#xff0c;本人都尝试了&#xff0c;但是几乎没有效果&#xff0c;我也会罗列出来&#xff0c;可自行尝试…...

    2023/3/28 15:36:50
  11. x86架构利用docker去编译arm64的应用程序

    文章目录1. 交叉编译&#xff1a;toolchain2. 隔离挂载的方式&#xff1a;3. QEMU 或其他模拟器来实际运行dockerx86架构实现多平台系统代码的编译&#xff0c;实现方式有多种&#xff1a;交叉编译&#xff1a;toolchain 【新的第三方库不好处理】隔离挂载的方式 【速度慢&…...

    2023/3/28 15:33:40
  12. Apache Camel

    目录儿一、简介二、核心总结一、简介 Camel is an Open Source integration framework that empowers you to quickly and easily integrate various systems consuming or producing data. Camel 是一个开源的集成框架&#xff0c;能够让开发者快速、轻松地整合/集成不同的应…...

    2023/3/28 15:33:26
  13. Java学习记录

    阅读前请看一下&#xff1a;我是一个热衷于记录的人&#xff0c;每次写博客会反复研读&#xff0c;尽量不断提升博客质量。文章设置为仅粉丝可见&#xff0c;是因为写博客确实花了不少精力。希望互相进步谢谢&#xff01;&#xff01; 文章目录阅读前请看一下&#xff1a;我是一…...

    2023/3/28 15:32:34
  14. 「数据架构」TOGAF建模之数据架构:数据迁移图

    数据迁移图的目的是显示从源应用程序到目标应用程序的数据流。该图将提供源/目标分布的可视化表示&#xff0c;并作为数据审核和建立可追溯性的工具。该图可以根据需要进行细化或增强。例如&#xff0c;该图可以只包含迁移环境的总体布局&#xff0c;也可以包含单个应用程序元数…...

    2023/3/28 15:29:54
  15. MySQL数据库之——高级[进阶]SQL语句(一)SQL高级语句、函数等

    文章目录一、SQL高级语句1、 SELECT2、DISTINCT3、WHER条件查询4 、AND OR5、IN6、BETWEEN7、通配符8、LIKE9、ORDER BY二、SQL函数1、数学函数&#xff1a;2、聚合函数&#xff1a;3、字符串函数&#xff1a;4、GROUP BY5、HAVING6、别名7、连接查询8、子查询9、EXISTS一、SQL…...

    2023/3/28 15:24:30
  16. Java实现一个简单的东南西北中的面板

    目录 一、前言 二、代码部分 1.代码 三、程序运行结果&#xff08;面板弹出&#xff09; 四、涉及到的知识点代码 一、前言 1.本代码是我在上学时写的&#xff0c;有一些地方没能完美实现&#xff0c;请包涵也请多赐教&#xff01; 2.本弹窗界面可以根据简单的要求进行…...

    2023/3/28 15:24:15
  17. 阿里十年资深程序员吐血总结之Java代理模式

    阿里十年资深程序员吐血总结之Java代理模式 文章目录阿里十年资深程序员吐血总结之Java代理模式1.接口代理2.类代理3.动态代理都是通过反射实现的吗4.jdk动态代理和cglib动态代理的区别Java代理模式是怎么实现的​ Java 代理模式是一种常见的设计模式&#xff0c;它可以在不改变…...

    2023/3/28 15:23:34
  18. ServletAPI详解(一)

    目录 1.Tomcat简介 2.Servlet 是什么? 3.编写Servlet 程序步骤 1.创建一个Maven项目 2.引入依赖 3.创建目录 4.编写代码 5.打包程序 6.部署程序 7.验证程序 4.使用SmartTomcat插件部署 1.Tomcat简介 Tomcat是一个开源的,基于java实现的被广泛使用的web服务器.web服…...

    2023/3/28 15:22:50
  19. 全球第三机械制造商徐工集团表示与 Conflux、Zen Spark Technology 建立合作伙伴关系公司

    【ConfluxNews】2023.3.28 ----------------------------- 1.【网络状态】当前版本V2.2.2&#xff0c;全网算力≈10T&#xff0c;昨日交易次数58K&#xff0c;昨日新增账户0.69K&#xff0c;昨日新增合约13个&#xff1b; 2.【POS参数】总锁仓244&#xff08;-3&#xff09;&a…...

    2023/3/28 15:20:26
  20. 为什么所谓的“自律”一定要跟坚持挂钩呢?懂一点“行为设计学”,升级对“意义”的认知

    为什么所谓的“自律”一定要跟坚持挂钩呢&#xff1f;懂一点“行为设计学”&#xff0c;升级对“意义”的认知00 导读01 懂一点“行为设计学”02 升级你对“意义”的认知03 小结00 导读 世界上最成功的人之一&#xff0c;风险投资家雷达利欧&#xff08;Ray Dalio&#xff09;…...

    2023/3/28 15:19:52
  21. 中国各省份研究随笔

    一.陕西 1.陕北榆林 黄土荒原&#xff0c;萧瑟荒凉&#xff0c;生活艰难&#xff0c;亩产60斤小麦。2002年开始榆林富裕&#xff0c;因为煤炭资源&#xff0c;易开采、质量好 2002年之前煤炭没有人开采&#xff0c;因为价格只有14元一吨&#xff0c;2001年加入WTO&#xff0…...

    2023/3/28 15:19:31
  22. 配置JRebel热部署

    1&#xff1a;此对应设置的是IntelliJ IDEA 2022.1.3 (Ultimate Edition)的热部署设置 2&#xff1a;激活插件失败&#xff0c;提示【LS client not configued】原因是版本过高&#xff0c;解决办法&#xff1a;手动下载jRebel,官网去找到过去版本的jRebel&#xff0c;地址&am…...

    2023/3/28 15:16:22
  23. OpenGL | 渲染带透明通道的2D精灵

    一、Alpha测试 Alpha 测试的基本原理为&#xff1a;当绘制一个片元时&#xff0c;首先检测其 Alpha 值&#xff0c;若 Alpha 值满足要求&#xff0c;就通过测试&#xff0c;绘制此片元&#xff1b;否则丢弃此片元&#xff0c;不进行绘制。 glEnable(GL_ALPHA_TEST)&#xff1b…...

    2023/3/28 15:15:20
  24. PCIE中断发送与接收

    1. EndPoint模式下中断的发送 在EP模式下&#xff0c;PCIE会根据配置信息只产生MSI或者INTA中断中的一种&#xff0c;然后传送到RC端。 1.1 INTA中断的发送 INTA中断以带内消息(Assert_INTA/Deassert_INTA)的形式传递中断&#xff0c;其作用相当于传统PCI总线中使用边带中断…...

    2023/3/28 15:15:04
  25. SQL Server底层架构技术对比

    背景 数据库是信息化的基石&#xff0c;支撑着整个业务系统&#xff0c;发挥着非常重要的作用&#xff0c;被喻为“IT的心脏”。因此&#xff0c;让数据库安全、稳定、高效地运行已经成为IT管理者必须要面对的问题。数据库在底层架构层面需要满足以下几点建设要求&#xff1a; …...

    2023/3/28 15:14:36
  26. 你真的了解变量吗?

    变量是编程语言中最基本的术语&#xff0c;用来代指计算机中存放的可变数据——如整数、小数、字符或被调用的一片内存空间。 变量可以保存不同的变量类型&#xff0c;比如整型、布尔型、对象、数组等等&#xff0c;每一个变量在内存中对应着一个内存地址&#xff0c;但是如果…...

    2023/3/28 15:13:20
  27. 一文彻底搞懂为什么OpenCV用GPU/cuda跑得比用CPU慢?

    一、原因总结 最近项目需要&#xff0c;发现了这个问题。网上找原因&#xff0c;汇总起来&#xff0c;有以下几点原因&#xff1a; 1、首先对于任何一个CUDA程序&#xff0c;在调用它的第一个CUDA API时后都要花费秒级的时间去初始化运行环境&#xff0c;后续还要分配显存&am…...

    2023/3/28 15:11:34
  28. 数仓必备概念

    目录 数据仓库 三范式建模 维度建模 数据仓库 是一个面向主题的&#xff08;Subject&#xff09;、集成的&#xff08;Integrated&#xff09;、非易失&#xff08;Non-Volatile&#xff09;、时变性&#xff08;Time Variant&#xff09;的数据集合&#xff0c;用于支持管理…...

    2023/3/28 15:09:12
  29. Spring Security 6.0系列【5】源码篇之认证组件

    有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot 版本 3.0.4 本系列Spring Security 版本 6.0.2 源码地址:https://gitee.com/pearl-organization/study-spring-security-demo 文章目录 前言认证机制认证组件SecurityContextHolderSecurityContextAuthentication…...

    2023/3/28 15:08:48
  30. webpack 配置介绍

    一、webpack webpack 仅是一个打包工具&#xff0c;不会处理打包过程的东西例如 es6等新特性&#xff0c;如果需要处理这些新特性需要配置单独的加载bebblle-loader 等。 "devDependencies": {"webpack": "^5.76.3", // 打包工具"…...

    2023/3/28 15:06:40