JUC进阶-NO.3 说说Java锁

文章目录

    • ⭐NO.3 说说Java锁
      • 一. 乐观锁 & 悲观锁
        • 1.悲观锁
        • 2.伪代码
        • 3.乐观锁
      • 二. 通过8种情况演示锁运行案例,看看我们到底锁的是什么
        • 1.8种锁案例
          • (1). 标准访问有ab两个线程,请问先打印邮件还是短信
          • (2). sendEmail方法暂停3秒钟,请问先打印邮件还是短信
          • (3).新增一个普通的hello方法,请问先打印邮件还是hello
          • (4).有两部手机,请问先打印邮件还是短信
          • (5).两个静态同步方法,同1部手机,请问先打印邮件还是短信
          • (6).两个静态同步方法, 2部手机,请问先打印邮件还是短信
          • (7).1个静态同步方法,1个普通同步方法,同1部手机,请问先打印邮件还是短信
          • (8).1个静态同步方法,1个普通同步方法,2部手机,请问先打印邮件还是短信
        • 2.synchronizde有三种应用方式
        • 3.从字节码角度分析synchronized实现
        • 4.对于synchronized关键字,我们在<synchronized与锁升级>章节还会在深度讲解
      • 三. 公平锁 & 非公平锁
        • 1.从ReentrantLock卖票编码演示公平和非公平
        • 2.何为公平锁 | 非公平锁?
        • 3.预埋伏AQS
      • 四. 可重入锁(又名递归锁)
        • 1.说明
        • 2."可重入锁"这四个字分开来解释:
        • 3.可重入锁种类
          • (1).隐式锁(即synchronized关键字使用的锁)默认是可重入锁
          • (2).synchronize的重入的实现机理
          • (3).显示锁(Lock)也有ReentrantLock这样的可重入锁
      • 五. 死锁及排查
        • 1.死锁是什么?(验证死锁是死锁方法)
          • 2.产生死锁的主要原因
      • 六. 写锁(独占锁) | 读锁(共享锁)
      • 七. 自旋锁SpinLock
      • 八. 无锁 -> 独占锁 -> 读写锁 -> 邮戳锁
          • 1.有无比读写锁更快的锁?
      • 九. 无锁 -> 偏向锁 -> 轻量锁 -> 重量锁
      • 十. 其他细节

⭐NO.3 说说Java锁

一. 乐观锁 & 悲观锁

1.悲观锁

适合写操作多的场景,先加锁可以保证写操作时数据正确

显示的锁定之后再操作同步资源

认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改.

synchronized关键子和Lock的实现类都是悲观锁

2.伪代码

.\JUC.assets\image-20220426113556066

3.乐观锁

乐观锁认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据.

如果这个数据没有被更新,当前线程将自己修改的数据成功写入,如果数据已经被其他线程更新了,则根据不同的实现方法执行不同的操作

乐观锁在Java中是通过使用无锁编程来实现的,最常用的CAS算法,Java原子类中的递增操作就通过CAS自旋实现的

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

  • 乐观锁则直接去操作同步资源,是一种无锁算法,得之我幸失之我命,再抢

  • 乐观锁一般有两种实现方式

    1. 采用版本号机制
    2. CAS(compare-and-Swap,即比较并替换)算法实现

    CAS 方式为乐观锁,synchronized 为悲观锁。因此使用 CAS 解决并发问题通常情况下性能更优。

    但使用 CAS 方式也会有几个问题:

    • ABA问题

      因为CAS需要在操作值的时候,检查值有没有发生变化,比如没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时则会发现它的值没有发生变化,但是实际上却变化了。

      ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加1,那么A->B->A就会变成1A->2B->3A。

      从Java 1.5开始,JDK的Atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法的作用是首先检查当前引用是否等于预期引用,并且检查当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。

    • 循环时间长开销大

      自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。如果JVM能支持处理器提供的pause指令,那么效率会有一定的提升。pause指令有两个作用:第一,它可以延迟流水线执行命令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零;第二,它可以避免在退出循环的时候因内存顺序冲突(Memory Order Violation)而引起CPU流水线被清空(CPU Pipeline Flush),从而提高CPU的执行效率。

    • 只能保证一个共享变量的原子操作

      当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁。

      还有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如,有两个共享变量i = 2,j = a,合并一下ij = 2a,然后用CAS来操作ij。

      从Java 1.5开始,JDK提供了AtomicReference类来保证引用对象之间的原子性,就可以把多个变量放在一个对象里来进行CAS操作。

二. 通过8种情况演示锁运行案例,看看我们到底锁的是什么

1.8种锁案例

(1). 标准访问有ab两个线程,请问先打印邮件还是短信
public class Lock8Demo {//一切程序的入口,主线程public static void main(String[] args) {//资源类1Phone phone = new Phone();new Thread(() -> phone.sendEmail(), "a").start();//暂停毫秒try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> phone.sendSMS(), "b").start();}
}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");}
}// 控制台输出
-------sendEmail
-------sendSMS

一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,

其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一的一个线程去访问这些synchronized方法

锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法

(2). sendEmail方法暂停3秒钟,请问先打印邮件还是短信

同理情况1

(3).新增一个普通的hello方法,请问先打印邮件还是hello
public class Lock8Demo {//一切程序的入口,主线程public static void main(String[] args) {//资源类1Phone phone = new Phone();new Thread(() -> phone.sendEmail(), "a").start();//暂停毫秒try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> phone.hello(), "b").start();}
}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");}
}// 控制台输出
-------hello
-------sendEmail

加个普通方法后发现和同步锁无关
普通方法和同步锁方法是没有关系的不发生争抢

(4).有两部手机,请问先打印邮件还是短信
public class Lock8Demo {//一切程序的入口,主线程public static void main(String[] args) {//资源类1Phone phone1 = new Phone();//资源类2Phone phone2 = new Phone();new Thread(() -> phone1.sendEmail(), "a").start();//暂停毫秒try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> phone2.sendSMS(), "b").start();}
}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");}
}// 控制台
-------sendSMS
-------sendEmail

换成两个对象后,不是同一把锁了,情况立刻变化。

(5).两个静态同步方法,同1部手机,请问先打印邮件还是短信
public class Lock8Demo {//一切程序的入口,主线程public static void main(String[] args) {//资源类1Phone phone = new Phone();new Thread(() -> phone.sendEmail(), "a").start();//暂停毫秒try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> phone.sendSMS(), "b").start();}
}//资源类
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");}
}// 控制台
-------sendEmail
-------sendSMS
  • 对于普通同步方法,锁的是当前实例对象,通常指this,具体的一部手机,所有的普通同步方法用的都是同一把锁——实例对象本身
  • 对于静态同步方法,锁的是当前类的Class对象,如Phone.class唯一的一个模板
  • 对于同步方法块,锁的是 synchronized 括号内的对象
  • -> 阿里规范,能用对象锁就不要用类锁
  • 静态同步方法加的就是类锁
(6).两个静态同步方法, 2部手机,请问先打印邮件还是短信
public class Lock8Demo {//一切程序的入口,主线程public static void main(String[] args) {//资源类1Phone phone1 = new Phone();//资源类2Phone phone2 = new Phone();new Thread(() -> phone1.sendEmail(), "a").start();//暂停毫秒try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> phone2.sendSMS(), "b").start();}
}//资源类
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");}
}// 控制台
-------sendEmail
-------sendSMS

同理情况5

(7).1个静态同步方法,1个普通同步方法,同1部手机,请问先打印邮件还是短信
public class Lock8Demo {//一切程序的入口,主线程public static void main(String[] args) {//资源类1Phone phone = new Phone();new Thread(() -> phone.sendEmail(), "a").start();//暂停毫秒try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> phone.sendSMS(), "b").start();}
}//资源类
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");}
}// 控制台
-------sendSMS
-------sendEmail
  • 当一个线程试图访问同步代码时它首先必须得到锁,退出或抛出异常时必须释放锁。
  • 所有的普通同步方法用的都是同一把锁——实例对象本身,就是new出来的具体实例对象本身,本类this也就是说如果一个实例对象的普通同步方法获取锁后,该实例对象的其他普通同步方法必须等待获取锁的方法释放锁后才能获取锁。
  • 所有的静态同步方法用的也是同一把锁——类对象本身,就是我们说过的唯一模板Class.具体实例对象this和唯一模板Class,这两把锁是两个不同的对象,所以静态同步方法与普通同步方法之间是不会有竞态条件的
  • 但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁。
(8).1个静态同步方法,1个普通同步方法,2部手机,请问先打印邮件还是短信
public class Lock8Demo {//一切程序的入口,主线程public static void main(String[] args) {//资源类1Phone phone1 = new Phone();//资源类2Phone phone2 = new Phone();new Thread(() -> phone1.sendEmail(), "a").start();//暂停毫秒try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> phone2.sendSMS(), "b").start();}
}//资源类
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");}
}// 控制台
-------sendSMS
-------sendEmail

同理7

.\JUC.assets\image-20220426141236152

2.synchronizde有三种应用方式

  • JDK源码(notify方法)说明

    .\JUC.assets\image-20220426163116815

  • 8种锁的案例实际体现在三个地方

    1. 作用于实例方法,当前实例加锁,进入同步代码前腰获得当前实例的锁
    2. 作用于代码块,对括号里配置的对象加锁
    3. 作用于静态方法,当前类加锁,进入(静态)+同步代码前要获得当前类对象的锁

3.从字节码角度分析synchronized实现

  • javap -c ***.class文件反汇编

  • javap -v ***.class文件反编译

  • javap -v -verbose ***.class文件反编译输出附加信息(包括行号,本地变量表,反汇编等详细信息)

  • synchronized同步代码块:

    实现使用的是monitorenter和monitorexit指令

  • 每一个synchronized一定是一个monitorenter和两个monitorexit么?

    一般来说会是1:2的情况是第一个是程序正常完成第二个是为了保证当出现异常时可以over程序

    但是如果你在程序的结尾throw new RuntimeException的情况下,比例将会是1:1但同时字节码指令中会多出一个 athrow

  • 普通同步方法

    调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置.如果设置了,执行线程会将现持有monitor然后在执行方法,最后在方法完成(无论是正常完成还是非正常完成)时释放monitor

  • 静态同步方法

    比普通同步方法多了一个ACC_STATIC标识从而区分是否为静态同步方法

  • 反编译synchronized锁的究竟是什么? -> 什么是monitor?

    1. 为什么任何一个对象都可以成为一个锁?

      -> 所有的对象都继承了Object

      -> 在HotSpot虚拟机中,omitor采用ObjectMonitor实现

      -> \hotspot\agent\src\share\classes\sun\jvm\hotspot\runtimeObjectMonitor.java -> \hotspot\src\share\vm\runtime\ObjectMonitor.cpp -> \hotspot\src\share\vm\runtime\ObjectMonitor.hpp

       ObjectMonitor() {_header       = NULL;_count        = 0; // 用来记录该线程获取锁的次数_waiters      = 0,_recursions   = 0; // 锁的重入次数_object       = NULL;_owner        = NULL; // 指向持有ObjectMonitor对象的线程_WaitSet      = NULL; // 存放处于wait状态的线程队列_WaitSetLock  = 0 ;_Responsible  = NULL ;_succ         = NULL ;_cxq          = NULL ;FreeNext      = NULL ;_EntryList    = NULL ; // 存放处于等待锁block状态的线程队列_SpinFreq     = 0 ;_SpinClock    = 0 ;OwnerIsThread = 0 ;_previous_owner_tid = 0;}
      

      -> 每一个对象都天生带一个对象监视器

4.对于synchronized关键字,我们在<synchronized与锁升级>章节还会在深度讲解

sychronized必须作用于某个对象中,所以java在对象的头文件储存了锁的相关信息.锁升级功能主要依赖于MarkWord中的锁标志位和释放偏向锁标志位,后续笔记锁升级的时候再加深,没有钱了解一下,下图

Hotspot的实现

.\JUC.assets\image-20220427104150939

三. 公平锁 & 非公平锁

1.从ReentrantLock卖票编码演示公平和非公平

  • codeDemo(公平锁)

    public class SaleTicketDemo {public static void main(String[] args) {Ticket ticket = new Ticket();new Thread(() -> {for (int i = 1; i <= 55; i++) ticket.sale();}, "a").start();new Thread(() -> {for (int i = 1; i <= 55; i++) ticket.sale();}, "b").start();new Thread(() -> {for (int i = 1; i <= 55; i++) ticket.sale();}, "c").start();new Thread(() -> {for (int i = 1; i <= 55; i++) ticket.sale();}, "d").start();new Thread(() -> {for (int i = 1; i <= 55; i++) ticket.sale();}, "e").start();}
    }class Ticket {private int number = 50;//默认用的是非公平锁private Lock lock = new ReentrantLock();public void sale() {lock.lock();try {if (number > 0) {System.out.println(Thread.currentThread().getName() + "\t 卖出第: " + (number--) + "\t 还剩下: " + number);}} finally {lock.unlock();}}
    }// 控制台
    a	 卖出第: 50	 还剩下: 49
    a	 卖出第: 49	 还剩下: 48
    a	 卖出第: 48	 还剩下: 47
    a	 卖出第: 47	 还剩下: 46
    a	 卖出第: 46	 还剩下: 45
    a	 卖出第: 45	 还剩下: 44
    a	 卖出第: 44	 还剩下: 43
    a	 卖出第: 43	 还剩下: 42
    a	 卖出第: 42	 还剩下: 41
    a	 卖出第: 41	 还剩下: 40
    a	 卖出第: 40	 还剩下: 39
    a	 卖出第: 39	 还剩下: 38
    a	 卖出第: 38	 还剩下: 37
    a	 卖出第: 37	 还剩下: 36
    a	 卖出第: 36	 还剩下: 35
    a	 卖出第: 35	 还剩下: 34
    a	 卖出第: 34	 还剩下: 33
    a	 卖出第: 33	 还剩下: 32
    a	 卖出第: 32	 还剩下: 31
    a	 卖出第: 31	 还剩下: 30
    a	 卖出第: 30	 还剩下: 29
    a	 卖出第: 29	 还剩下: 28
    a	 卖出第: 28	 还剩下: 27
    a	 卖出第: 27	 还剩下: 26
    c	 卖出第: 26	 还剩下: 25
    c	 卖出第: 25	 还剩下: 24
    b	 卖出第: 24	 还剩下: 23
    b	 卖出第: 23	 还剩下: 22
    b	 卖出第: 22	 还剩下: 21
    b	 卖出第: 21	 还剩下: 20
    b	 卖出第: 20	 还剩下: 19
    b	 卖出第: 19	 还剩下: 18
    b	 卖出第: 18	 还剩下: 17
    b	 卖出第: 17	 还剩下: 16
    b	 卖出第: 16	 还剩下: 15
    b	 卖出第: 15	 还剩下: 14
    b	 卖出第: 14	 还剩下: 13
    b	 卖出第: 13	 还剩下: 12
    b	 卖出第: 12	 还剩下: 11
    b	 卖出第: 11	 还剩下: 10
    b	 卖出第: 10	 还剩下: 9
    b	 卖出第: 9	 还剩下: 8
    b	 卖出第: 8	 还剩下: 7
    b	 卖出第: 7	 还剩下: 6
    b	 卖出第: 6	 还剩下: 5
    b	 卖出第: 5	 还剩下: 4
    b	 卖出第: 4	 还剩下: 3
    b	 卖出第: 3	 还剩下: 2
    b	 卖出第: 2	 还剩下: 1
    b	 卖出第: 1	 还剩下: 0
    

    非公平锁容易出现锁饥饿现象,有的线程不一定会抢到锁

  • codeDemo(公平锁)

    public class SaleTicketDemo {public static void main(String[] args) {Ticket ticket = new Ticket();new Thread(() -> {for (int i = 1; i <= 55; i++) ticket.sale();}, "a").start();new Thread(() -> {for (int i = 1; i <= 55; i++) ticket.sale();}, "b").start();new Thread(() -> {for (int i = 1; i <= 55; i++) ticket.sale();}, "c").start();new Thread(() -> {for (int i = 1; i <= 55; i++) ticket.sale();}, "d").start();new Thread(() -> {for (int i = 1; i <= 55; i++) ticket.sale();}, "e").start();}
    }class Ticket {private int number = 50;//默认用的是非公平锁,分配的平均一点,=--》公平一点private Lock lock = new ReentrantLock(true);public void sale() {lock.lock();try {if (number > 0) {System.out.println(Thread.currentThread().getName() + "\t 卖出第: " + (number--) + "\t 还剩下: " + number);}} finally {lock.unlock();}}}// 控制台
    a	 卖出第: 50	 还剩下: 49
    a	 卖出第: 49	 还剩下: 48
    a	 卖出第: 48	 还剩下: 47
    a	 卖出第: 47	 还剩下: 46
    a	 卖出第: 46	 还剩下: 45
    a	 卖出第: 45	 还剩下: 44
    a	 卖出第: 44	 还剩下: 43
    a	 卖出第: 43	 还剩下: 42
    a	 卖出第: 42	 还剩下: 41
    a	 卖出第: 41	 还剩下: 40
    a	 卖出第: 40	 还剩下: 39
    a	 卖出第: 39	 还剩下: 38
    a	 卖出第: 38	 还剩下: 37
    a	 卖出第: 37	 还剩下: 36
    a	 卖出第: 36	 还剩下: 35
    b	 卖出第: 35	 还剩下: 34
    a	 卖出第: 34	 还剩下: 33
    b	 卖出第: 33	 还剩下: 32
    a	 卖出第: 32	 还剩下: 31
    b	 卖出第: 31	 还剩下: 30
    a	 卖出第: 30	 还剩下: 29
    b	 卖出第: 29	 还剩下: 28
    a	 卖出第: 28	 还剩下: 27
    b	 卖出第: 27	 还剩下: 26
    c	 卖出第: 26	 还剩下: 25
    a	 卖出第: 25	 还剩下: 24
    b	 卖出第: 24	 还剩下: 23
    c	 卖出第: 23	 还剩下: 22
    a	 卖出第: 22	 还剩下: 21
    b	 卖出第: 21	 还剩下: 20
    c	 卖出第: 20	 还剩下: 19
    a	 卖出第: 19	 还剩下: 18
    d	 卖出第: 18	 还剩下: 17
    b	 卖出第: 17	 还剩下: 16
    c	 卖出第: 16	 还剩下: 15
    a	 卖出第: 15	 还剩下: 14
    d	 卖出第: 14	 还剩下: 13
    b	 卖出第: 13	 还剩下: 12
    c	 卖出第: 12	 还剩下: 11
    a	 卖出第: 11	 还剩下: 10
    d	 卖出第: 10	 还剩下: 9
    e	 卖出第: 9	 还剩下: 8
    b	 卖出第: 8	 还剩下: 7
    c	 卖出第: 7	 还剩下: 6
    a	 卖出第: 6	 还剩下: 5
    d	 卖出第: 5	 还剩下: 4
    e	 卖出第: 4	 还剩下: 3
    b	 卖出第: 3	 还剩下: 2
    c	 卖出第: 2	 还剩下: 1
    a	 卖出第: 1	 还剩下: 0
    

    公平锁相对于非公平锁雨露均沾

2.何为公平锁 | 非公平锁?

  • 生活中,排队讲究先来后到视为公平.程序中的公平性也是符合请求锁的绝对时间的,其实就是FIFO(先入先出队列(First Input First Output, FIFO)这是一种传统的按序执行方法,先进入的指令先完成并引退,跟着才执行第二条指令.),否则视为不公平

  • 源码解读

    按序排队公平锁,就是判断同步队列是否还有先驱节点的存在(我前面还有人么?),如果没有先驱节点才能获取锁;先占先得非公平锁,是不管这个事的,只要能抢获到同步状态就可以
    .\JUC.assets\image-20220427150155166

  • 为什么会有公平锁 | 非公平锁的设计为什么默认非公平锁?

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

    公平锁保证了排队的公平性,等待锁的线程不会饿死。缺点是整体吞吐效率相对非公平锁要低,等待队列中除第一个线程以外的所有线程都会阻塞,CPU唤醒阻塞线程的开销比非公平锁大,非公平锁霸气的忽略了这个规则,所以就有可能导致排队的长时间在排队,也没有机会获取到锁,这就是传说中的"锁饥饿"

  • 什么时候用公平?什么时候用非公平?

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

3.预埋伏AQS

  • code

    public class ReentrantLock implements Lock, java.io.Serializable {private static final long serialVersionUID = 7373984872572414699L;/** Synchronizer providing all implementation mechanics */private final Sync sync;/*** Base of synchronization control for this lock. Subclassed* into fair and nonfair versions below. Uses AQS state to* represent the number of holds on the lock.*/abstract static class Sync extends AbstractQueuedSynchronizer {...}/*** Sync object for non-fair locks*/static final class NonfairSync extends Sync {...}/*** Sync object for fair locks*/static final class FairSync extends Sync {}
    .......
    

    .\JUC.assets\image-20220427154434744

四. 可重入锁(又名递归锁)

1.说明

  • 可重入锁又名递归锁
  • 是指在同一个线程在外面方法获取锁的时候,在进入该线程的内层方法会自动获取锁(前提,锁对象得是同一个对象),不会因为之前已经获取过还没释放而阻塞
  • 如果是1个有synchronized修饰的递归调用方法,程序第2次进入被自己阻塞了岂不是天大的笑话,出现了作茧自缚
  • 所以Java中Reentrantlock和sychronized都是可重入锁,可重入锁的一个有点是可一定程度避免死锁

2."可重入锁"这四个字分开来解释:

  • 可: 可以

  • 重: 再次

  • 入: 进入

    进入什么?

    进入同步域(即同步代码块/方法或显式锁锁定的代码)

  • 锁: 同步锁

  • 一句话: 一个线程中的多个流程可以获取同一把锁,只有这把同步锁可以再次进入,自己可以获取自己的内部锁

3.可重入锁种类

(1).隐式锁(即synchronized关键字使用的锁)默认是可重入锁
  1. 指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫可重入锁

  2. 简单的来说就是: 在一个synchronized修饰的方法或代码块的内部调用本类的其他synchronized修饰的方法或代码块时,是永远可以得到锁的

    • 同步代码块

       static Object objectLock = new Object();public static void main(String[] args){new Thread(() -> {synchronized (objectLock) {// lockSystem.out.println("-----外层");synchronized (objectLock){System.out.println("-----中层");synchronized (objectLock){System.out.println("-----内层");}}}//unlock},"t1").start();}// 控制台
      -----外层
      -----中层
      -----内层
      

      以上这种递归写法一般来说没有人会这么写东西,只展示语法规定

  3. 与可重入锁相反,不可重入锁不可递归调用,递归调用就发生死锁

(2).synchronize的重入的实现机理
  • 每个锁对象拥有一个锁计数器和一个指向持有该锁的线程指针
  • 当执行monitorenter时,如果目标锁对象的计数器为零,那么说明他没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1
  • 在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么JVM可以将其计数器加1,否则需要等待,直至持有线程释放该锁
  • 当执行monitorexit时,JVM则需将锁对象的计数器减1.计数器为零代表锁已经被释放了
(3).显示锁(Lock)也有ReentrantLock这样的可重入锁
  • lock()和unlock() 1:1的情况下.不会有什么问题

     public static void main(String[] args) {Lock lock = new ReentrantLock();new Thread(() -> {lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t" + "-----外层");lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t" + "-----内层");} finally {lock.unlock();}} finally {lock.unlock();}}, "t1").start();new Thread(() -> {lock.lock();try {System.out.println("------22222");} finally {lock.unlock();}}, "t2").start();}// 控制台
    -----外层
    -----中层
    -----内层
    
  • 当lock()多于unlock()的情况下如果只有一个线程程序(可能)会正常运行

     public static void main(String[] args) {Lock lock = new ReentrantLock();new Thread(() -> {lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t" + "-----外层");lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t" + "-----内层");} finally {}} finally {lock.unlock();}}, "t1").start();}// 控制台
    t1	-----外层
    t1	-----内层
    
  • 出大问题的情况,当lock()多于unlock()的情况下多个线程: 注意这种情况只是会导致t2一直没有办法获取锁但是不属于死锁的情况

     public static void main(String[] args) {Lock lock = new ReentrantLock();new Thread(() -> {lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t" + "-----外层");lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t" + "-----内层");} finally {}} finally {lock.unlock();}}, "t1").start();new Thread(() -> {lock.lock();try {System.out.println("------22222");} finally {lock.unlock();}}, "t2").start();}// 控制台
    t1	-----外层
    t1	-----内层
    

    .\JUC.assets\image-20220428173014485

    问题: 程序没有结束,因为t2蚌住了他没有办法获取到锁,因为t1没有释放完全

五. 死锁及排查

1.死锁是什么?(验证死锁是死锁方法)

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

  • code ->死锁Demo

    public static void main(String[] args) {Thread a = new Thread(() -> {synchronized (lockA) {System.out.println(Thread.currentThread().getName() + "\t" + " 自己持有A锁,期待获得B锁");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lockB) {System.out.println(Thread.currentThread().getName() + "\t 获得B锁成功");}}}, "a");a.start();new Thread(() -> {synchronized (lockB) {System.out.println(Thread.currentThread().getName() + "\t" + " 自己持有B锁,期待获得A锁");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lockA) {System.out.println(Thread.currentThread().getName() + "\t 获得A锁成功");}}}, "b").start();}// 控制台
    a	 自己持有A锁,期待获得B锁
    b	 自己持有B锁,期待获得A......同时程序没有结束
    
  • 论证是死锁方法一: terminal查询

    ## jps -> Java的ps
    ## 查看所有的正在运行的进程
    [azang]$ jps -l
    21152 com.atguigu.juc.locks.DeadLockDemo
    6592
    2532 org.jetbrains.idea.maven.server.RemoteMavenServer36
    6836 org.jetbrains.jps.cmdline.Launcher
    14728 org.jetbrains.idea.maven.server.RemoteMavenServer36
    556 sun.tools.jps.Jps## 打印出对应进程ID的堆栈信息
    [azang]$ jstack 21152
    2022-04-28 17:48:03
    Full thread dump OpenJDK 64-Bit Server VM (17.0.2+8-LTS mixed mode, sharing):Threads class SMR info:
    _java_thread_list=0x000001cc44be2f50, length=15, elements={
    0x000001cc435de300, 0x000001cc44800080, 0x000001cc44823cc0, 0x000001cc44824670,
    0x000001cc44827030, 0x000001cc448279e0, 0x000001cc44828770, 0x000001cc44829210,
    0x000001cc44832950, 0x000001cc435cb4b0, 0x000001cc44a0b5c0, 0x000001cc44b63d50,
    0x000001cc44c316a0, 0x000001cc44c31ff0, 0x000001cc171e8720
    }"Reference Handler" #2 daemon prio=10 os_prio=2 cpu=0.00ms elapsed=47.87s tid=0x000001cc
    435de300 nid=0x3af8 waiting on condition  [0x000000851e0ff000]java.lang.Thread.State: RUNNABLEat java.lang.ref.Reference.waitForReferencePendingList(java.base@17.0.2/Native M
    ethod)at java.lang.ref.Reference.processPendingReferences(java.base@17.0.2/Reference.j
    ava:253)at java.lang.ref.Reference$ReferenceHandler.run(java.base@17.0.2/Reference.java:
    215)"Finalizer" #3 daemon prio=8 os_prio=1 cpu=0.00ms elapsed=47.87s tid=0x000001cc44800080
    nid=0x1c80 in Object.wait()  [0x000000851e1fe000]java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(java.base@17.0.2/Native Method)- waiting on <0x0000000623c0d5c8> (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(java.base@17.0.2/ReferenceQueue.java:155)- locked <0x0000000623c0d5c8> (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(java.base@17.0.2/ReferenceQueue.java:176)at java.lang.ref.Finalizer$FinalizerThread.run(java.base@17.0.2/Finalizer.java:1
    72)"Signal Dispatcher" #4 daemon prio=9 os_prio=2 cpu=0.00ms elapsed=47.86s tid=0x000001cc4
    4823cc0 nid=0x2924 waiting on condition  [0x0000000000000000]java.lang.Thread.State: RUNNABLE"Attach Listener" #5 daemon prio=5 os_prio=2 cpu=15.62ms elapsed=47.86s tid=0x000001cc44
    824670 nid=0x6384 waiting on condition  [0x0000000000000000]java.lang.Thread.State: RUNNABLE"Service Thread" #6 daemon prio=9 os_prio=0 cpu=0.00ms elapsed=47.86s tid=0x000001cc4482
    7030 nid=0x5708 runnable  [0x0000000000000000]java.lang.Thread.State: RUNNABLE"Monitor Deflation Thread" #7 daemon prio=9 os_prio=0 cpu=0.00ms elapsed=47.86s tid=0x00
    0001cc448279e0 nid=0x5748 runnable  [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C2 CompilerThread0" #8 daemon prio=9 os_prio=2 cpu=46.88ms elapsed=47.86s tid=0x000001c
    c44828770 nid=0x49dc waiting on condition  [0x0000000000000000]java.lang.Thread.State: RUNNABLENo compile task"C1 CompilerThread0" #11 daemon prio=9 os_prio=2 cpu=31.25ms elapsed=47.86s tid=0x000001
    cc44829210 nid=0x4214 waiting on condition  [0x0000000000000000]java.lang.Thread.State: RUNNABLENo compile task"Sweeper thread" #12 daemon prio=9 os_prio=2 cpu=0.00ms elapsed=47.86s tid=0x000001cc448
    32950 nid=0x5fc4 runnable  [0x0000000000000000]java.lang.Thread.State: RUNNABLE"Common-Cleaner" #13 daemon prio=8 os_prio=1 cpu=0.00ms elapsed=47.84s tid=0x000001cc435
    cb4b0 nid=0x610c in Object.wait()  [0x000000851e9fe000]java.lang.Thread.State: TIMED_WAITING (on object monitor)at java.lang.Object.wait(java.base@17.0.2/Native Method)- waiting on <0x0000000623d2b3f0> (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(java.base@17.0.2/ReferenceQueue.java:155)- locked <0x0000000623d2b3f0> (a java.lang.ref.ReferenceQueue$Lock)at jdk.internal.ref.CleanerImpl.run(java.base@17.0.2/CleanerImpl.java:140)at java.lang.Thread.run(java.base@17.0.2/Thread.java:833)at jdk.internal.misc.InnocuousThread.run(java.base@17.0.2/InnocuousThread.java:1
    62)"Monitor Ctrl-Break" #14 daemon prio=5 os_prio=0 cpu=0.00ms elapsed=47.80s tid=0x000001c
    c44a0b5c0 nid=0x62a0 runnable  [0x000000851ebfe000]java.lang.Thread.State: RUNNABLEat sun.nio.ch.SocketDispatcher.read0(java.base@17.0.2/Native Method)at sun.nio.ch.SocketDispatcher.read(java.base@17.0.2/SocketDispatcher.java:46)at sun.nio.ch.NioSocketImpl.tryRead(java.base@17.0.2/NioSocketImpl.java:261)at sun.nio.ch.NioSocketImpl.implRead(java.base@17.0.2/NioSocketImpl.java:312)at sun.nio.ch.NioSocketImpl.read(java.base@17.0.2/NioSocketImpl.java:350)at sun.nio.ch.NioSocketImpl$1.read(java.base@17.0.2/NioSocketImpl.java:803)at java.net.Socket$SocketInputStream.read(java.base@17.0.2/Socket.java:966)at sun.nio.cs.StreamDecoder.readBytes(java.base@17.0.2/StreamDecoder.java:270)at sun.nio.cs.StreamDecoder.implRead(java.base@17.0.2/StreamDecoder.java:313)at sun.nio.cs.StreamDecoder.read(java.base@17.0.2/StreamDecoder.java:188)- locked <0x00000006238abbf8> (a java.io.InputStreamReader)at java.io.InputStreamReader.read(java.base@17.0.2/InputStreamReader.java:177)at java.io.BufferedReader.fill(java.base@17.0.2/BufferedReader.java:162)at java.io.BufferedReader.readLine(java.base@17.0.2/BufferedReader.java:329)- locked <0x00000006238abbf8> (a java.io.InputStreamReader)at java.io.BufferedReader.readLine(java.base@17.0.2/BufferedReader.java:396)at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:49)"Notification Thread" #15 daemon prio=9 os_prio=0 cpu=0.00ms elapsed=47.80s tid=0x000001
    cc44b63d50 nid=0x730 runnable  [0x0000000000000000]java.lang.Thread.State: RUNNABLE"a" #16 prio=5 os_prio=0 cpu=0.00ms elapsed=47.80s tid=0x000001cc44c316a0 nid=0x34ec wai
    ting for monitor entry  [0x000000851eeff000]java.lang.Thread.State: BLOCKED (on object monitor)at com.atguigu.juc.locks.DeadLockDemo.lambda$main$0(DeadLockDemo.java:27)- waiting to lock <0x000000062384f1f0> (a java.lang.Object)- locked <0x000000062384f1e0> (a java.lang.Object)at com.atguigu.juc.locks.DeadLockDemo$$Lambda$14/0x0000000800c01200.run(Unknown
    Source)at java.lang.Thread.run(java.base@17.0.2/Thread.java:833)"b" #17 prio=5 os_prio=0 cpu=0.00ms elapsed=47.80s tid=0x000001cc44c31ff0 nid=0x33d0 wai
    ting for monitor entry  [0x000000851efff000]java.lang.Thread.State: BLOCKED (on object monitor)at com.atguigu.juc.locks.DeadLockDemo.lambda$main$1(DeadLockDemo.java:44)- waiting to lock <0x000000062384f1e0> (a java.lang.Object)- locked <0x000000062384f1f0> (a java.lang.Object)at com.atguigu.juc.locks.DeadLockDemo$$Lambda$15/0x0000000800c01418.run(Unknown
    Source)at java.lang.Thread.run(java.base@17.0.2/Thread.java:833)"DestroyJavaVM" #18 prio=5 os_prio=0 cpu=93.75ms elapsed=47.80s tid=0x000001cc171e8720 n
    id=0x37ec waiting on condition  [0x0000000000000000]java.lang.Thread.State: RUNNABLE"VM Thread" os_prio=2 cpu=0.00ms elapsed=47.87s tid=0x000001cc435da870 nid=0x1ffc runnab
    le"GC Thread#0" os_prio=2 cpu=0.00ms elapsed=47.88s tid=0x000001cc1508a850 nid=0x24d0 runn
    able"G1 Main Marker" os_prio=2 cpu=0.00ms elapsed=47.88s tid=0x000001cc1508b570 nid=0xa28 ru
    nnable"G1 Conc#0" os_prio=2 cpu=0.00ms elapsed=47.88s tid=0x000001cc1508bd90 nid=0x2dd8 runnab
    le"G1 Refine#0" os_prio=2 cpu=0.00ms elapsed=47.88s tid=0x000001cc434a6d90 nid=0x59e0 runn
    able"G1 Service" os_prio=2 cpu=0.00ms elapsed=47.88s tid=0x000001cc434a77b0 nid=0x5254 runna
    ble"VM Periodic Task Thread" os_prio=2 cpu=0.00ms elapsed=47.80s tid=0x000001cc1727e450 nid
    =0x33f0 waiting on conditionJNI global refs: 15, weak refs: 0####################################################################################################################
    ## 发现死锁
    ####################################################################################################################
    Found one Java-level deadlock:
    =============================
    "a":waiting to lock monitor 0x000001cc44823740 (object 0x000000062384f1f0, a java.lang.Obj
    ect),which is held by "b""b":waiting to lock monitor 0x000001cc44823040 (object 0x000000062384f1e0, a java.lang.Obj
    ect),which is held by "a"
    ####################################################################################################################
    ## 问题所在
    ####################################################################################################################
    Java stack information for the threads listed above:
    ===================================================
    "a":at com.atguigu.juc.locks.DeadLockDemo.lambda$main$0(DeadLockDemo.java:27)- waiting to lock <0x000000062384f1f0> (a java.lang.Object)- locked <0x000000062384f1e0> (a java.lang.Object)at com.atguigu.juc.locks.DeadLockDemo$$Lambda$14/0x0000000800c01200.run(Unknown
    Source)at java.lang.Thread.run(java.base@17.0.2/Thread.java:833)
    "b":at com.atguigu.juc.locks.DeadLockDemo.lambda$main$1(DeadLockDemo.java:44)- waiting to lock <0x000000062384f1e0> (a java.lang.Object)- locked <0x000000062384f1f0> (a java.lang.Object)at com.atguigu.juc.locks.DeadLockDemo$$Lambda$15/0x0000000800c01418.run(Unknown
    Source)at java.lang.Thread.run(java.base@17.0.2/Thread.java:833)Found 1 deadlock.
    
  • 论证是死锁方法二: win+r 或者mac 终端打开jconsole

    连接对应想查看的进程

    .\JUC.assets\image-20220428175738079

    等待一会儿

    .\JUC.assets\image-20220428175817219

    进入控制台 -> 点击线程 -> 检查死锁

    .\JUC.assets\image-20220428175905473

    出现死锁

    .\JUC.assets\image-20220428175932843

2.产生死锁的主要原因
  • 系统资源不足
  • 进程运行推进的顺序不合适
  • 资源分配不当

六. 写锁(独占锁) | 读锁(共享锁)

七. 自旋锁SpinLock

八. 无锁 -> 独占锁 -> 读写锁 -> 邮戳锁

1.有无比读写锁更快的锁?

StampedLock(邮戳锁,票据锁)

九. 无锁 -> 偏向锁 -> 轻量锁 -> 重量锁

十. 其他细节

不可以String同一把锁 -> 严禁这么搞事情
因为String有一个不可变的常量池,下方代码定义的不是两把锁 ->享元对象

String lockA = "A";
String lockB = "A";

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/25876.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

运动用品品牌排行榜,双十一运动好物选购清单

健身运动就像打游戏一样&#xff0c;如何区分你和其他玩家的差别呢&#xff1f;有时候靠身材&#xff0c;当然有时候也会拼装备&#xff0c;那么这些运动装备能否增加buff呢&#xff1f;是否值得入手呢&#xff1f;作为一名资深的运动爱好者&#xff0c;下面我就从实用角度聊一…

健身运动装备有哪些?双十一运动健身装备选购指南

近年来&#xff0c;各地的各种运动赛事越来越多&#xff0c;对运动也是非常好的推动。很多名人都开始运动起来&#xff0c;因为运动之后多巴胺分泌&#xff0c;让人觉得神清气爽。隔几天不运动&#xff0c;就让人浑身不自在。当然运动也要注意方式方法、注意姿势&#xff0c;还…

以数字化视角看世界杯,我预测荷兰夺冠

编者按&#xff1a;2022世界杯隆重开幕&#xff01;各路英豪齐聚卡塔尔&#xff01;让我们从一个ITer的角度&#xff0c;用数字化的视角&#xff0c;看看谁能最后夺冠&#xff01; 本文已经得到原作者张戈授权&#xff0c;在此表示感谢&#xff01; 真不是嘚瑟。 我是有40年球龄…

别人熬夜看世界杯 我熬夜改代码 你满意了

2022年卡塔尔世界杯正如火如荼地进行着&#xff0c; 一边是热火朝天的比赛&#xff0c;一边是让人惊掉下巴的爆冷结局&#xff0c; 但正因为这些不确定因素&#xff0c;反倒让世界杯增添了几分魅力和乐趣&#xff01; 小编在看球赛的过程中&#xff0c;不禁起了联想&#xff…

【进度2】从阿里云迁至腾讯云,并添加网站备案号

注&#xff1a;在阿里云备案成功网站域名不可以直接解析到腾讯云服务器&#xff0c;会被腾讯云的DNS拦截并跳转。 腾讯云服务器从2023.2.1-2023.2.15限时优惠&#xff0c;这里我选择的是2核2G这个。 HTML源码和备案号的添加 由于域名之前在阿里云和工信部已经备案过&#xff0c…

浅谈明日方舟游戏系统

主要玩法&#xff1a;敌方阵营从敌方初始点进入战斗并且沿着怪物前进路线行驶到己方保护目标。玩家可以通过部署干员守护己方保护目标&#xff0c;防止敌方阵营进入&#xff1b;当保护目标的生命值为0时&#xff0c;则战斗失败&#xff0c;任务结束。 1 干员系统 1.1 职业分支…

著名球星罗纳尔迪尼奥担任巴西旅游大使

近日&#xff0c;巴西著名球星罗纳尔迪尼奥应巴西旅游局&#xff08;Embratur&#xff09;邀请&#xff0c;担任巴西旅游大使。他志愿将“最好的巴西”推向全世界。罗纳尔迪尼奥是前巴西男足运动员&#xff0c;世界体坛最优秀的球星之一&#xff0c;曾荣获1999年美洲杯冠军、20…

java安装教程win7_Win7系统安装JDK环境变量的配置方法

Win7系统安装JDK环境变量前需要用户先安装JAVA环境&#xff0c;才能继续配置JDK。如果用户曾重装过操作系统&#xff0c;依然是需要重新安装JDK环境的&#xff0c;具体操作不过可以按照下文中的步骤进行处理&#xff0c;但因为版本不一样&#xff0c;细节方面可能有差别。 Win7…

又到一轮德比时

这是杂货铺的第456篇文章 结束了中超联赛两连客&#xff0c;本轮比赛&#xff0c;中赫国安回到了京城&#xff0c;“客场”挑战北京人和。虽然从实力上看&#xff0c;中赫国安占据绝对上风&#xff0c;但不要忘了&#xff0c;上个赛季&#xff0c;丰体0:3的比分&#xff0c;让国…

五连胜?这才刚刚开始!

这是杂货铺的第460篇文章 经历了客场和武里南的亚冠联赛&#xff0c;本周迎来了河南建业的挑战&#xff0c;虽然是主场作战&#xff0c;但相比之下&#xff0c;河南已经歇了两周了(和武汉的比赛&#xff0c;因场地问题延期)&#xff0c;从体能上&#xff0c;河南更占优。就连施…

疫情下,嵌入式er该怎么进行职业规划,难点在哪?

整理&#xff1a;付斌&#xff0c;转自嵌入式ARM&#xff0c;参考已标注至原文 01 嵌入式系统的概念 着重理解“嵌入”的概念 主要从三个方面上来理解&#xff1a; 1、从硬件上&#xff0c;将基于CPU的处围器件&#xff0c;整合到CPU芯片内部。 比如早期基于X86体系结构下的计算…

DSkin控件使用

DSkinCode验证码控件&#xff1a; ClickNewCode 是否可以点击刷新验证码 CodeCount 验证码字数 CodeStr 验证码的值

TouchDesigner学习 TOP与CHOP结合制作小应用

效果预览&#xff08;快速滑动鼠标&#xff0c;闪光效果制作&#xff09;&#xff1a; 官方教程&#xff1a; http://www.touchdesigner.co/beginner OP: 右键 view... 跳出程序预览效果 点下按钮是在程序内预览 特别说明的注意点&#xff1a; 上图中的distance&#xff0c;v…

9款超棒的设计协作工具!值得你收藏

设计协作工具是为团队合作而生的。设计协作工具可以帮助团队成员在一个平台上合作&#xff0c;从而提高设计效率和质量。本文将与大家分享9款设计行业公认好用的设计协作工具。来看看吧&#xff01; 即时设计 即时设计是一种基于云的设计协作工具&#xff0c;可以让设计团队合…

【实战】轻轻松松使用StyleGAN(一):创建令人惊讶的黄种人脸和专属于自己的老婆动漫头像

NVIDIA&#xff08;英伟达&#xff09;开源了StyleGAN&#xff0c;用它可以生成令人惊讶的逼真人脸&#xff1b;也可以像某些人所说的&#xff0c;生成专属于自己的老婆动漫头像。这些生成的人脸或者动漫头像都是此前这个世界上从来没有过的&#xff0c;完全是被“伟大的你”所…

话说软件详细设计工具

在软件设计是需要写软件详细说明书,设计此文档的时候,肯定少不了工具.现在我们就来了解一下软件详细设计的 工具. 1)程序流程图 程序流程图又称为程序框图,它是最古老,应用最广泛且最有争议描述详细设计的工具.它易学,表达算法直观,缺点是 不够规范,特别是使用箭头会使质量受到…

手把手教系列之梳状滤波器设计实现

[导读]:前面一篇文章关于IIR/移动平均滤波器设计的文章。本文来聊一聊陷波滤波器,该滤波器在混入谐波干扰时非常有用,算法简单,实现代价低。本文来一探其在机理、应用场景。 注:尽量在每篇文章写写摘要,方便阅读。信息时代,大家时间都很宝贵,如此亦可节约粉丝们的宝贵…

11款程序员实用工具,老少皆宜,你一定用得上

优秀程序员之所以优秀的原因并不一定是他写代码的速度比所有人都快&#xff0c;但他解决事情的效率一定是比很多人都要高的&#xff0c;提升工作效率的方法并不需要我们十八般武艺样样精通&#xff0c;有时候使用好的工具就能帮助我们大大提升办事效率。 今天给大家分享11个程…

2022FW柯罗芭KLOVA 用极简主义演绎服装美学

万物伊始&#xff0c;一切都是最简单的&#xff0c;后疫情时代&#xff0c;时尚似乎也开始化繁为简&#xff0c;回归本真。LESS IS MORE 诉说着真实的高贵&#xff0c;从不喧哗&#xff0c;服装里讲究的极简风&#xff0c;正是当下时尚和生活方式的最佳体现。 款式极简 极简又高…

Fences4 桌面整理好帮手 让你的桌面整洁美观

前言 Fences 4将电脑桌面分为不同功能的小区域&#xff0c;如办公区、程序区、文件夹、TXT文本区等&#xff0c;让你一眼就可以找到自己想要的区域文件夹或程序。除了这些之外&#xff0c;新软件还具备拥有记忆功能和自定义功能&#xff0c;帮助用户可以快速恢复电脑整洁的状态…