【JUC 一】线程 进程 synchronized Lock锁 生产者 消费者 8锁 线程安全集合类

NEW,

// 运行

RUNNABLE,

// 阻塞

BLOCKED,

// 等待

WAITING,

//超时等待

TIMED_WAITING,

//终止

TERMINATED;

}

| 区别 | wait | sleep |

| — | — | — |

| 来自不同的类 | object类 | 线程类 |

| 锁的释放不同 | 会释放锁 | 不会释放锁 |

| 使用范围不同 | 必须在同步代码中 | 可以在任何地方睡 |

TimeUnit

TimeUnit.SECONDS.sleep(3);//睡眠3秒

3、Lock锁(重点)

===========

3.1、传统synchronized


本质: 队列和锁,放在方法上锁的是this,放在代码块中锁的是()里面的对象

synchronized(obj){

}

3.2、Lock 接口


  • reentrantLock构造器

非公平锁:NonfairSync()

十分不公平,可以插队(默认)

公平锁:FairSync()

十分公平,先来后到,一定要排队

public ReentrantLock() {

sync = new NonfairSync(); //无参默认非公平锁

}

public ReentrantLock(boolean fair) {

sync = fair ? new FairSync() : new NonfairSync();//传参为true为公平锁

}

3.3、synchronized 和 lock 区别


| 比较 | Lock | synchronized |

| — | — | — |

| 类型 | java接口 | 内置关键字 |

| 开启释放 | 显式锁手动 开启 和 关闭 | 隐式锁

出了作用域自动释放 |

| 代码锁 | 支持 | 支持 |

| 方法锁 | 不支持 | 支持 |

| 锁状态 | 可以判断是否获取到了锁 | 无法判断获取锁的状态 |

| 线程阻塞 | Lock锁就不一定会等待下去 | 线程1(获得锁,阻塞),线程2(等待) |

| 可重入性 | 可重入的,可以判断锁 | 可重入锁,不可以中断的 |

| 公平性 | 默认非公平的(可设置) | 非公平的 |

| 性能 | 性能更好

JVM将花费较少的时间来调度 | 性能一般

JVM将花费较多的时间来调度管理 |

| 扩展性 | 有更好的扩展性(提供更多的子类) | 不支持 |

| 适用场景 | 适合锁大量的同步代码 | 适合锁少量的代码同步问题 |

4、生产者和消费者问题

===========

面试高频:单例模式、八大排序、生产者消费者、死锁

4.1、synchronized实现


wait、notify 必须在 synchronizied 声明的代码中,否则会抛出异常 java.lang.IllegalMonitorStateException

package providerConsumer;

/**

  • @author ajun

  • Date 2021/7/3

  • @version 1.0

  • synchronized版生产者消费者

*/

public class Syn {

public static void main(String[] args) {

Data data = new Data();

//线程1

new Thread(() -> {

for (int i = 0; i < 5; i++) {

try {

data.increment();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

},“A”).start();

//线程2

new Thread(() -> {

for (int i = 0; i < 5; i++) {

try {

data.decrement();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

},“B”).start();

//线程3

new Thread(() -> {

for (int i = 0; i < 5; i++) {

try {

data.increment();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

},“C”).start();

//线程4

new Thread(() -> {

for (int i = 0; i < 5; i++) {

try {

data.decrement();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

},“D”).start();

}

}

class Data{

private int num;

public int getNum() {

return num;

}

//增加

public synchronized void increment() throws InterruptedException {

//判断等待

//用while,不用if,防止虚假唤醒

while(num!=0){

this.wait();

}

//业务代码

num++;

System.out.println(Thread.currentThread().getName() + " --> " + num);

//通知

this.notifyAll();

}

//减少

public synchronized void decrement() throws InterruptedException {

//判断等待

//用while,不用if,防止虚假唤醒

while(num == 0){

this.wait();

}

//业务代码

num–;

System.out.println(Thread.currentThread().getName() + " --> " + num);

//通知

this.notifyAll();

}

}

  • 可能存在的问题

虚假唤醒

在判断等待时,如果用 if ,当线程多的时候,可能会有虚假唤醒

解决办法

if 判断改为 while 判断

因为 if 只会执行一次,执行完会接着向下执行 if()外边的

而 while 不会,直到条件满足才会向下执行 while()外边的

4.2、JUC实现


在JUC中,Lock 替换 synchronized,await 替换 wait,signal 替换 notify。(signal 信号)

package providerConsumer;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;

/**

  • @author ajun

  • Date 2021/7/3

  • @version 1.0

*/

public class Loc {

public static void main(String[] args) {

Data2 data2 = new Data2();

new Thread(() -> {

for (int i = 0; i < 5; i++) {

try {

data2.increment();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

},“A”).start();

new Thread(() -> {

for (int i = 0; i < 5; i++) {

try {

data2.decrement();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

},“B”).start();

new Thread(() -> {

for (int i = 0; i < 5; i++) {

try {

data2.increment();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

},“C”).start();

new Thread(() -> {

for (int i = 0; i < 5; i++) {

try {

data2.decrement();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

},“D”).start();

}

}

class Data2{

private int num = 0;

Lock lock = new ReentrantLock();//定义锁

Condition condition = lock.newCondition();//定义同步监视器

public int getNum() {

return num;

}

//增加

public void increment() throws InterruptedException {

lock.lock();//加锁

try {

//判断等待

while (num != 0){

condition.await();//等待

}

//业务代码

num++;

System.out.println(Thread.currentThread().getName() + " --> " + num);

//通知

condition.signalAll();

} finally {

lock.unlock();//解锁

}

}

//减少

public void decrement() throws InterruptedException {

lock.lock();//加锁

try {

//判断等待

while (num == 0){

condition.await();//等待

}

//业务代码

num–;

System.out.println(Thread.currentThread().getName() + " --> " + num);

//通知

condition.signalAll();

} finally {

lock.unlock();//解锁

}

}

}

Condition实现精准通知唤醒

5、8锁现象

======

8锁就是关于锁的八个现象

① 非静态同步方法的默认锁是 this,静态同步方法的默认锁是 class

② 某一时刻内,只能有一个线程有锁,无论几个方法;前提是用的同一把锁

参考:https://www.cnblogs.com/shamao/p/11045282.html

5.1、2线程 1对象 2方法


两个线程 调用 同一个对象 的 两个同步方法

package lock8;

/**

  • @author ajun

  • Date 2021/7/4

  • @version 1.0

  • 两个线程调用同一个对象的两个同步方法

*/

public class Lock1 {

public static void main(String[] args) {

//同一对象

Number number = new Number();

//线程1

new Thread(() -> {number.getOne();},“A”).start();

//线程2

new Thread(() -> {number.getTwo();},“B”).start();

}

}

class Number{

//同步方法1 (非static)

public synchronized void getOne(){

System.out.println(Thread.currentThread().getName() + “: one”);

}

//同步方法2 (非static)

public synchronized void getTwo(){

System.out.println(Thread.currentThread().getName() + “: two”);

}

}

运行结果如下:

A: one

B: two

分析:

被 synchronized 修饰的方法,锁的对象是方法的调用者。因为两个方法的调用者是同一个,所以两个方法用的是同一个锁,先调用方法的先获得锁,先执行

5.2、2线程 1对象 2方法(1sleep)


新增sleep()给某个方法

TimeUnit.SECONDS.sleep(2);

分析:

不管在何处添加休眠后,会中途休眠,但不影响执行顺序。

被synchronized修饰的方法,锁的对象是方法的调用者。因为两个方法的调用者是同一个,所以两个方法用的是同一个锁,先调用方法的先获得锁,先执行,第二个方法只有在第一个方法执行完释放锁之后才执行

5.3、3线程 1对象 3方法(1普通)


新增一个线程调用 同一对象 新增的一个普通方法

分析:

新增的方法没有被 synchronized 修饰,不是同步方法,不受锁的影响,所以不需要等待。其他线程共用了一把锁,所以还需要等待。

5.4、2线程 2对象 2方法(1sleep)


两个线程 调用 两个对象 的同步方法,其中一个方法有sleep()

分析:

被 synchronized 修饰的方法,锁的对象是方法的调用者。因为用了两个对象调用各自的方法,所以两个方法的调用者不是同一个,所以两个方法用的不是同一个锁,后调用的方法不需要等待先调用的方法

5.5、2线程 1对象 2方法(1static)


sleep()的方法设置为static,并且让两个线程用同一个对象调用两个方法

分析:

被synchronized和static修饰的方法,锁的对象是类的class对象。仅仅被synchronized修饰的方法,锁的对象是方法的调用者。因为两个方法锁的对象不是同一个,所以两个方法用的不是同一个锁,后调用的方法不需要等待先调用的方法。

5.6、2线程 1对象 2方法(2sattic)


将两个方法均设置为static方法,并且让两个线程用同一个对象调用两个方法

分析:

被synchronized和static修饰的方法,锁的对象是类的class对象。因为两个同步方法都被static修饰了,所以两个方法用的是同一个锁,后调用的方法需要等待先调用的方法

5.7、2线程 2对象 2方法(1static)


将两个方法中有sleep()的方法设置为static方法,另一个方法去掉static修饰,让两个线程用 两个对象 调用两个方法

分析:

被synchronized和static修饰的方法,锁的对象是类的class对象。仅仅被synchronized修饰的方法,锁的对象是方法的调用者。即便是用同一个对象调用两个方法,锁的对象也不是同一个,所以两个方法用的不是同一个锁,后调用的方法不需要等待先调用的方法。

5.8、2线程 2对象 2方法(2static)


将两个方法均设置为static方法,并且让两个线程用 一个对象 调用两个方法

分析:

被synchronized和static修饰的方法,锁的对象是类的class对象。因为两个同步方法都被static修饰了,即便用了两个不同的对象调用方法,两个方法用的还是同一个锁,后调用的方法需要等待先调用的方法。

5.9、总结


  • 普通同步方法

  • 一个类里面如果有多个synchronized方法,在使用同一个对象调用的前提下,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,其他的线程都只能等待,换句话说,某一时刻内,只能有唯一一个线程去访问这些synchronized方法

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

  • 普通方法

  • 加个普通方法后发现和同步锁无关

  • 静态同步方法

  • 换成静态同步方法后,情况又变化

  • 所有的非静态同步方法用的都是同一把锁:实例对象本身

  • 也就是说如果一个对象的非静态同步方法获取锁后,该对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,可是其他对象的非静态同步方法因为跟该对象的非静态同步方法用的是不同的锁,所以毋须等待该对象的非静态同步方法释放锁就可以获取他们自己的锁

  • 所有的静态同步方法用的也是同一把锁:类对象本身

  • 这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间不会有竞争条件。但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个对象的静态同步方法,还是其他对象的静态同步方法,只要它们属于同一个类的对象,那么就需要等待当前正在执行的静态同步方法释放锁

| 方法 | 锁的类型 | 等待情况 |

| — | — | — |

| 普通方法 | 没有锁 | 不需要等待 |

| 静态方法 | 没有锁 | 不需要等待 |

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

学习分享,共勉

这里是小编拿到的学习资源,其中包括“中高级Java开发面试高频考点题笔记300道.pdf”和“Java核心知识体系笔记.pdf”文件分享,内容丰富,囊括了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。同时还有Java进阶学习的知识笔记脑图(内含大量学习笔记)!

资料整理不易,读者朋友可以转发分享下!

Java核心知识体系笔记.pdf

记一次蚂蚁金服Java研发岗的面试经历,分享下我的复习笔记面经

中高级Java开发面试高频考点题笔记300道.pdf

记一次蚂蚁金服Java研发岗的面试经历,分享下我的复习笔记面经

架构进阶面试专题及架构学习笔记脑图

记一次蚂蚁金服Java研发岗的面试经历,分享下我的复习笔记面经

Java架构进阶学习视频分享
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
道该从何学起的朋友,同时减轻大家的负担。**[外链图片转存中…(img-aCyP5OFl-1713528793508)]

[外链图片转存中…(img-2McYhUa7-1713528793511)]

[外链图片转存中…(img-pXMBFCuf-1713528793513)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

学习分享,共勉

这里是小编拿到的学习资源,其中包括“中高级Java开发面试高频考点题笔记300道.pdf”和“Java核心知识体系笔记.pdf”文件分享,内容丰富,囊括了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。同时还有Java进阶学习的知识笔记脑图(内含大量学习笔记)!

资料整理不易,读者朋友可以转发分享下!

Java核心知识体系笔记.pdf

[外链图片转存中…(img-uBmIe24Q-1713528793514)]

中高级Java开发面试高频考点题笔记300道.pdf

[外链图片转存中…(img-P8qpwrKt-1713528793516)]

架构进阶面试专题及架构学习笔记脑图

[外链图片转存中…(img-adrgQsqD-1713528793518)]

Java架构进阶学习视频分享
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 23
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值