偏向锁是在没有发生锁争用的情况下使用的,一旦有了第二个线程争用锁,偏向锁就会升级为轻量级锁。如果锁争用很激烈,轻量级锁的CAS自旋到达阈值后,轻量级锁就会升级为重量级锁。
锁 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
偏向锁 | 加锁和解锁不需要额外的消耗,和执行非同步方法比较,仅存在纳秒级的差距。 | 如果线程间存在锁竞争,会带来额外的锁撤销的消耗。 | 适用只有一个线程访问的临界区场景。 |
轻量级锁 | 竞争的线程不会阻塞,提高了程序的响应速度。 | 抢不到锁竞争的线程使用CAS自旋等待,会消耗 CPU | 锁占用时间很短,吞吐量低 |
重量级锁 | 线程竞争不使用自旋,不会消耗 CPU | 线程阻塞,相应时间缓慢 | 锁占用时间较长,吞吐量高 |
synchronized void method() {
// todo 业务代码
}
synchronized void staic method() {
//业务代码
}
// 线程安全的懒汉模式 - 单例
public class Singleton {
//保证有序性,防止指令重排
private volatile static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
//先判断对象是否已经实例过,没有实例化过才进入加锁代码
if (uniqueInstance == null) {
//类对象加锁
synchronized (Singleton.class) {
if (uniqueInstance == null) uniqueInstance = new Singleton();
}
}
return uniqueInstance;
}
}
① 线程抢锁时,JVM首先检测内置锁对象Mark Word中的 biased_lock(偏向锁标识)是否设置成1,lock(锁标志位)是否为 01,如果都满足,确认内置锁对象为可偏向状态。
② 在内置锁对象确认为可偏向状态之后,JVM检查Mark Word中 的线程ID是否为抢锁线程ID,如果是,就表示抢锁线程处于偏向锁状态,抢锁线程快速获得锁,开始执行临界区代码。
③ 如果Mark Word中的线程ID并未指向抢锁线程,就通过CAS操作竞争锁。如果竞争成功,就将Mark Word中的线程ID设置为抢锁线程,偏向标志位设置为1,锁标志位设置为01,然后执行临界区代码,此时内置锁对象处于偏向锁状态。
④ 如果CAS操作竞争失败,就说明发生了竞争,撤销偏向锁,进而升级为轻量级锁。
⑤ JVM使用CAS将锁对象的Mark Word替换为抢锁线程的锁记录指针,如果成功,抢锁线程就获得锁。如果替换失败,就表示其他线程竞争锁,JVM尝试使用CAS自旋替换抢锁线程的锁记录指针,如果自旋成功(抢锁成功),那么锁对象依然处于轻量级锁状态。
⑥如果JVM的CAS替换锁记录指针自旋失败,轻量级锁就膨胀为重量级锁,后面等待锁的线程也要进入阻塞状态。
线程间的通信:当多个线程共同操作共享的资源时,线程间通过某种方式互相告知自己的状态,以避免无效的资源争夺。
线程间通信的方式可以有很多种:等待-通知、共享内存、管道流。而且"等待-通知"通信方式是Java中使用普遍的线程间通信方式。该方式的线程间通信使用对象的wait()、notify()两类方法来实现。每个Java对象都有wait()、notify()两类实例方法,并且wait()、notify()方法和对象的监视器是紧密相关的。
在调用同步对象的wait()和notify()系列方法时,“当前线程”必须拥有该对象的同步锁,也就是说,wait()和notify()系列方法需要在同步块中使用,否则JVM会抛出 IllegalMonitorStateException 异常。
对象的wait()方法的主要作用是让当前线程阻塞并等待被唤醒。wait()方法与对象监视器紧密相关,在当前线程执行 wait() 方法前,必须通过 synchronized() 方法成为对象锁的监视器的Owner。使用wait()方法时一定要放在同步块中。
Object类中 wait()
方法的三个版本:
// 当前线程调用了同步对象的 wait()实例方法后,将导致当前的线程等待,
// 当前线程进入同步对象的监视器WaitSet,等待被其他线程唤醒。
public final void wait() throws InterruptedException {
wait(0L);
}
// 当前的线程等待,等待被其他线程唤醒,或者指定的时间timeout用完,线程不再等待。
public final native void wait(long timeoutMillis) throws InterruptedException;
// 高精度限时等待版本,参数nanos是一个附加的纳秒级别的等待时间,而实现更加高精度的等待时间控制
// 1秒=1000毫秒=1000,000微秒=1000,000,000纳秒。
public final void wait(long timeoutMillis, int nanos) throws InterruptedException {……}
为方便表达,设某个同步锁对象为:locko.
对象的 wait() 方法的核心原理
大致如下:
对象的 notify() 方法的主要作用是唤醒在等待的线程。notify() 方法与对象监视器紧密相关,在执行notify()方法前,当前线程也必须通过 synchronized() 方法成为对象锁的监视器的Owner。所以调用notify()方法时也需要放在同步块中。
Object类中 notify()
方法的两个版本:
// locko.notify()调用后,唤醒locko 监视器等待集中的第一条等待线程;
// 被唤醒的线程进入EntryList,其状态从 WAITING 变成 BLOCKED。
@HotSpotIntrinsicCandidate
public final native void notify();
// locko.notifyAll()被调用后,唤醒locko监视器等待集中的全部等待线程,
// 所有被唤醒的线程进入EntryList,线程状态从 WAITING 变成 BLOCKED。
@HotSpotIntrinsicCandidate
public final native void notifyAll();
为方便表达,设某个同步锁对象为:locko.
对象的 notify() 或者 notifyAll() 方法的核心原理:
① 当线程调用了locko(某个同步锁对象)的notify()方法后,JVM会唤醒locko监视器WaitSet中的第一条等待线程。
② 当线程调用了locko的notifyAll()方法后,JVM会唤醒locko监视器WaitSet中的所有等待线程。
③ 等待线程被唤醒后,会从监视器的WaitSet移动到EntryList,线程具备了排队抢夺监视器Owner权利的资格,其状态从WAITING变成BLOCKED。
④ EntryList中的线程抢夺到监视器的Owner权利之后,线程的状态从BLOCKED变成Runnable,具备重新执行的资格。
调用 wait() 和 notify() 系列方法进行线程通信的要点如下:
① 调用某个同步对象locko的wait()和notify()类型方法前,必须要取得这个锁对象的监视锁,所以 wait() 和 notify() 类型方法必须放在 synchronized(locko) 同步块中,如果没有获得监视锁,JVM就会报 IllegalMonitorStateException 异常。
② 调用wait()方法时使用while进行条件判断,如果是在某种条件下进行等待,对条件的判断就不能使用if语句做一次性判断,而是使用while循环进行反复判断。只有这样才能在线程被唤醒后继续检查 wait 的条件,并在条件没有满足的情况下继续等待。
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import java.math.BigInteger;
import java.util.LinkedList;
import java.util.List;
/**
* 数据缓存区
*/
@Slf4j
public class DataBuffer<T> {
/**
* 数据缓冲区队列的最大长度
*/
public static final int MAX_AMOUNT = 10;
/**
* 数据缓冲区队列当前长度
*/
private Integer amount = 0;
/**
* 缓存区队列
*/
private List<T> dataList = new LinkedList<>();
// 同步锁对象:lockObject 用来锁队列 dataList,保证dataList线程安全
private final Object lockObject = new Object();
// 同步锁对象:notFull
private final Object notFull = new Object();
// 同步锁对象:notEmpty
private final Object notEmpty = new Object();
/**
* 向数据区增加一个元素
*/
public void add(T element) throws Exception {
// 当缓存区队列达到最大的长度时,锁住notFull,锁住notFull对象锁等待
while (amount >= MAX_AMOUNT) {
synchronized (notFull) {
log.info("缓存区队列已满!!!");
// 等待未满通知,调用notFull对象锁的wait()方法,让当前线程进入 WAITING 状态,
// 等待被其他线程唤醒
notFull.wait();
}
}
// 这个地方需要注意,当生产速度*生产者线程数 > 消费者速度*消费者线程数时,
// 会出现 队列长度大于最大队列长度的情况。
// 且 dataList 的长度最大为 11
// add() -> dataList 缓存区队列的长度 -> 11
synchronized (lockObject) {
dataList.add(element);
amount++;
log.info("add() -> dataList 缓存区队列的长度 -> {}", dataList.size());
}
// 上一步 往对列中添加了元素,能走到这一步,队列中肯定不为空,
// 所以去唤醒消费者中的 notEmpty对象锁 锁住的线程
synchronized (notEmpty) {
//发送未空通知
notEmpty.notify();
}
}
/**
* 从数据区取出一条元素
*/
public T fetch() throws Exception {
while (amount <= 0) {
synchronized (notEmpty) {
log.info("缓存区队列已空!!!");
//等待未空通知
notEmpty.wait();
}
}
T element = null;
synchronized (lockObject) {
// 这里需要添加校验,否则当 多线程情况下,消费速度快于生产速度时,会有异常
if (!CollectionUtils.isEmpty(dataList)) {
element = dataList.remove(BigInteger.ZERO.intValue());
amount--;
log.info("fetch() -> dataList 缓存区队列的长度 -> {}", dataList.size());
} else {
log.info("fetch() -> dataList 缓存区队列的长度为 0 不消费");
}
}
synchronized (notFull) {
//发送未满通知
notFull.notify();
}
return element;
}
}
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @description: 生产者
*/
@Slf4j
public class Producer implements Runnable {
// 生产次数计数器
static final AtomicInteger TURN = new AtomicInteger(1);
// 生产的动作
Callable action = null;
// 生产者生产,默认耗时2s
private int gap = 2;
public Producer(Callable action, int gap) {
this.action = action;
this.gap = gap;
}
@Override
public void run() {
// 这里一直生产
while (true) {
try {
//执行生产动作
Object out = action.call();
// 模拟生产者生产耗时
TimeUnit.SECONDS.sleep(gap);
//增加生产轮次
TURN.incrementAndGet();
//输出生产的结果
if (null != out) {
log.info("线程{}->第{}次生产:{}", Thread.currentThread().getName(),
TURN.get(), out);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
public class Consumer extends Thread {
//消费总次数计数器
static final AtomicInteger TURN = new AtomicInteger(0);
//消费的动作
Callable action = null;
//消费默认消费耗时3s
int gap = 3;
public Consumer(Callable action, int gap) {
this.action = action;
this.gap = gap;
}
@Override
public void run() {
while (true) {
//增加消费次数
TURN.incrementAndGet();
try {
//执行消费动作
Object out = action.call();
if (null != out) {
log.info("线程{}->第{}次消费:{}", Thread.currentThread().getName(),
TURN.get(), out);
}
// 模拟消费者消费耗时
TimeUnit.SECONDS.sleep(gap);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @description: 生产的产品
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Good implements Cloneable {
private String name;
@Override
public Good clone() {
Good good = null;
try {
good = (Good) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return good;
}
}
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 执行类
*/
@Slf4j
public class ThreadCommunicationDemo {
public static void main(String[] args) {
Good goodBase = new Good("香蕉");
final AtomicInteger goodCount = new AtomicInteger(0);
//共享数据区,实例对象
DataBuffer<Good> dataBuffer = new DataBuffer<>();
//生产者执行的动作
Callable<Good> produceAction = () -> {
//首先生成一个随机的商品
Good good = goodBase.clone();
int aa = goodCount.incrementAndGet();
log.info("生产总次数 -> {}", aa);
good.setName(good.getName() + aa);
//将商品加上共享数据区
dataBuffer.add(good);
return good;
};
//消费者执行的动作
Callable<Good> consumerAction = () -> {
// 从缓存区获取商品
Good goods = null;
goods = dataBuffer.fetch();
return goods;
};
//线程池,用于多线程模拟测试
ExecutorService threadPool = Executors.newFixedThreadPool(10);
//假定共6个线程,其中有1个消费者,但是有5个生产者
final int consumerTotal = 1;
final int produceTotal = 5;
for (int i = 0; i < produceTotal; i++) {
//生产者线程每生产一个商品,需要1秒
threadPool.submit(new Producer(produceAction, 2));
}
for (int i = 0; i < consumerTotal; i++) {
//消费者线程每消费一个商品,需要两秒
threadPool.submit(new Consumer(consumerAction, 1));
}
threadPool.shutdown();
}
}
Connected to the target VM, address: '127.0.0.1:51914', transport: 'socket'
21:52:10.320 [pool-1-thread-6] INFO com.DataBuffer - 缓存区队列已空!!!
21:52:10.320 [pool-1-thread-2] INFO com.ThreadCommunicationDemo - 生产总次数 -> 2
21:52:10.320 [pool-1-thread-3] INFO com.ThreadCommunicationDemo - 生产总次数 -> 4
21:52:10.320 [pool-1-thread-5] INFO com.ThreadCommunicationDemo - 生产总次数 -> 5
21:52:10.326 [pool-1-thread-2] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 1
21:52:10.320 [pool-1-thread-4] INFO com.ThreadCommunicationDemo - 生产总次数 -> 1
21:52:10.320 [pool-1-thread-1] INFO com.ThreadCommunicationDemo - 生产总次数 -> 3
21:52:10.327 [pool-1-thread-4] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 2
21:52:10.327 [pool-1-thread-5] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 3
21:52:10.327 [pool-1-thread-3] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 4
21:52:10.327 [pool-1-thread-1] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 5
21:52:10.330 [pool-1-thread-6] INFO com.DataBuffer - fetch() -> dataList 缓存区队列的长度 -> 4
21:52:10.331 [pool-1-thread-6] INFO com.Consumer - 线程pool-1-thread-6->第1次消费:Good(name=香蕉2)
21:52:11.352 [pool-1-thread-6] INFO com.DataBuffer - fetch() -> dataList 缓存区队列的长度 -> 3
21:52:11.352 [pool-1-thread-6] INFO com.Consumer - 线程pool-1-thread-6->第2次消费:Good(name=香蕉1)
21:52:12.330 [pool-1-thread-3] INFO com.Producer - 线程pool-1-thread-3->第6次生产:Good(name=香蕉4)
21:52:12.330 [pool-1-thread-1] INFO com.Producer - 线程pool-1-thread-1->第6次生产:Good(name=香蕉3)
21:52:12.330 [pool-1-thread-5] INFO com.Producer - 线程pool-1-thread-5->第6次生产:Good(name=香蕉5)
21:52:12.330 [pool-1-thread-2] INFO com.Producer - 线程pool-1-thread-2->第6次生产:Good(name=香蕉2)
21:52:12.330 [pool-1-thread-1] INFO com.ThreadCommunicationDemo - 生产总次数 -> 7
21:52:12.330 [pool-1-thread-4] INFO com.Producer - 线程pool-1-thread-4->第6次生产:Good(name=香蕉1)
21:52:12.330 [pool-1-thread-5] INFO com.ThreadCommunicationDemo - 生产总次数 -> 8
21:52:12.330 [pool-1-thread-3] INFO com.ThreadCommunicationDemo - 生产总次数 -> 6
21:52:12.330 [pool-1-thread-2] INFO com.ThreadCommunicationDemo - 生产总次数 -> 9
21:52:12.330 [pool-1-thread-1] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 4
21:52:12.330 [pool-1-thread-4] INFO com.ThreadCommunicationDemo - 生产总次数 -> 10
21:52:12.331 [pool-1-thread-5] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 5
21:52:12.331 [pool-1-thread-4] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 6
21:52:12.331 [pool-1-thread-3] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 7
21:52:12.332 [pool-1-thread-2] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 8
21:52:12.369 [pool-1-thread-6] INFO com.DataBuffer - fetch() -> dataList 缓存区队列的长度 -> 7
21:52:12.369 [pool-1-thread-6] INFO com.Consumer - 线程pool-1-thread-6->第3次消费:Good(name=香蕉5)
21:52:13.389 [pool-1-thread-6] INFO com.DataBuffer - fetch() -> dataList 缓存区队列的长度 -> 6
21:52:13.389 [pool-1-thread-6] INFO com.Consumer - 线程pool-1-thread-6->第4次消费:Good(name=香蕉4)
21:52:14.336 [pool-1-thread-3] INFO com.Producer - 线程pool-1-thread-3->第8次生产:Good(name=香蕉6)
21:52:14.336 [pool-1-thread-2] INFO com.Producer - 线程pool-1-thread-2->第8次生产:Good(name=香蕉9)
21:52:14.336 [pool-1-thread-4] INFO com.Producer - 线程pool-1-thread-4->第11次生产:Good(name=香蕉10)
21:52:14.336 [pool-1-thread-5] INFO com.Producer - 线程pool-1-thread-5->第11次生产:Good(name=香蕉8)
21:52:14.336 [pool-1-thread-1] INFO com.Producer - 线程pool-1-thread-1->第11次生产:Good(name=香蕉7)
21:52:14.336 [pool-1-thread-2] INFO com.ThreadCommunicationDemo - 生产总次数 -> 12
21:52:14.336 [pool-1-thread-3] INFO com.ThreadCommunicationDemo - 生产总次数 -> 11
21:52:14.336 [pool-1-thread-5] INFO com.ThreadCommunicationDemo - 生产总次数 -> 14
21:52:14.336 [pool-1-thread-4] INFO com.ThreadCommunicationDemo - 生产总次数 -> 13
21:52:14.336 [pool-1-thread-2] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 7
21:52:14.336 [pool-1-thread-1] INFO com.ThreadCommunicationDemo - 生产总次数 -> 15
21:52:14.336 [pool-1-thread-3] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 8
21:52:14.337 [pool-1-thread-1] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 9
21:52:14.337 [pool-1-thread-4] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 10
21:52:14.337 [pool-1-thread-5] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 11
21:52:14.403 [pool-1-thread-6] INFO com.DataBuffer - fetch() -> dataList 缓存区队列的长度 -> 10
21:52:14.403 [pool-1-thread-6] INFO com.Consumer - 线程pool-1-thread-6->第5次消费:Good(name=香蕉3)
21:52:15.419 [pool-1-thread-6] INFO com.DataBuffer - fetch() -> dataList 缓存区队列的长度 -> 9
21:52:15.419 [pool-1-thread-6] INFO com.Consumer - 线程pool-1-thread-6->第6次消费:Good(name=香蕉7)
21:52:16.342 [pool-1-thread-5] INFO com.Producer - 线程pool-1-thread-5->第16次生产:Good(name=香蕉14)
21:52:16.342 [pool-1-thread-3] INFO com.Producer - 线程pool-1-thread-3->第16次生产:Good(name=香蕉11)
21:52:16.342 [pool-1-thread-1] INFO com.Producer - 线程pool-1-thread-1->第16次生产:Good(name=香蕉15)
21:52:16.342 [pool-1-thread-5] INFO com.ThreadCommunicationDemo - 生产总次数 -> 16
21:52:16.342 [pool-1-thread-3] INFO com.ThreadCommunicationDemo - 生产总次数 -> 17
21:52:16.342 [pool-1-thread-2] INFO com.Producer - 线程pool-1-thread-2->第16次生产:Good(name=香蕉12)
21:52:16.342 [pool-1-thread-4] INFO com.Producer - 线程pool-1-thread-4->第16次生产:Good(name=香蕉13)
21:52:16.343 [pool-1-thread-5] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 10
21:52:16.343 [pool-1-thread-2] INFO com.ThreadCommunicationDemo - 生产总次数 -> 19
21:52:16.343 [pool-1-thread-1] INFO com.ThreadCommunicationDemo - 生产总次数 -> 18
21:52:16.343 [pool-1-thread-4] INFO com.ThreadCommunicationDemo - 生产总次数 -> 20
21:52:16.343 [pool-1-thread-3] INFO com.DataBuffer - 缓存区队列已满!!!
21:52:16.343 [pool-1-thread-4] INFO com.DataBuffer - 缓存区队列已满!!!
21:52:16.343 [pool-1-thread-2] INFO com.DataBuffer - 缓存区队列已满!!!
21:52:16.343 [pool-1-thread-1] INFO com.DataBuffer - 缓存区队列已满!!!
21:52:16.443 [pool-1-thread-6] INFO com.DataBuffer - fetch() -> dataList 缓存区队列的长度 -> 9
21:52:16.443 [pool-1-thread-6] INFO com.Consumer - 线程pool-1-thread-6->第7次消费:Good(name=香蕉8)
21:52:16.443 [pool-1-thread-3] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 10
21:52:17.459 [pool-1-thread-6] INFO com.DataBuffer - fetch() -> dataList 缓存区队列的长度 -> 9
21:52:17.460 [pool-1-thread-6] INFO com.Consumer - 线程pool-1-thread-6->第8次消费:Good(name=香蕉10)
21:52:17.460 [pool-1-thread-4] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 10
21:52:18.357 [pool-1-thread-5] INFO com.Producer - 线程pool-1-thread-5->第17次生产:Good(name=香蕉16)
21:52:18.358 [pool-1-thread-5] INFO com.ThreadCommunicationDemo - 生产总次数 -> 21
21:52:18.358 [pool-1-thread-5] INFO com.DataBuffer - 缓存区队列已满!!!
21:52:18.453 [pool-1-thread-3] INFO com.Producer - 线程pool-1-thread-3->第18次生产:Good(name=香蕉17)
21:52:18.453 [pool-1-thread-3] INFO com.ThreadCommunicationDemo - 生产总次数 -> 22
21:52:18.453 [pool-1-thread-3] INFO com.DataBuffer - 缓存区队列已满!!!
21:52:18.468 [pool-1-thread-6] INFO com.DataBuffer - fetch() -> dataList 缓存区队列的长度 -> 9
21:52:18.468 [pool-1-thread-2] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 10
21:52:18.468 [pool-1-thread-6] INFO com.Consumer - 线程pool-1-thread-6->第9次消费:Good(name=香蕉6)
21:52:19.462 [pool-1-thread-4] INFO com.Producer - 线程pool-1-thread-4->第19次生产:Good(name=香蕉20)
21:52:19.462 [pool-1-thread-4] INFO com.ThreadCommunicationDemo - 生产总次数 -> 23
21:52:19.462 [pool-1-thread-4] INFO com.DataBuffer - 缓存区队列已满!!!
21:52:19.477 [pool-1-thread-6] INFO com.DataBuffer - fetch() -> dataList 缓存区队列的长度 -> 9
21:52:19.477 [pool-1-thread-6] INFO com.Consumer - 线程pool-1-thread-6->第10次消费:Good(name=香蕉9)
21:52:19.477 [pool-1-thread-1] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 10
21:52:20.484 [pool-1-thread-6] INFO com.DataBuffer - fetch() -> dataList 缓存区队列的长度 -> 9
21:52:20.484 [pool-1-thread-2] INFO com.Producer - 线程pool-1-thread-2->第20次生产:Good(name=香蕉19)
21:52:20.484 [pool-1-thread-5] INFO com.DataBuffer - add() -> dataList 缓存区队列的长度 -> 10
21:52:20.484 [pool-1-thread-2] INFO com.ThreadCommunicationDemo - 生产总次数 -> 24
21:52:20.484 [pool-1-thread-6] INFO com.Consumer - 线程pool-1-thread-6->第11次消费:Good(name=香蕉12)
21:52:20.484 [pool-1-thread-2] INFO com.DataBuffer - 缓存区队列已满!!!
…………
…………
…………
import java.util.concurrent.TimeUnit;
/**
* 该例子 call() 和 sendMes() 共用一个锁,谁先拿到谁先执行
*/
public class Lock1 {
public static void main(String[] args) {
Phone1 phone = new Phone1();
new Thread(() -> phone.sendMes(), "A").start();
// sleep 一秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> phone.call(), "B").start();
}
}
class Phone1 {
// synchronized 修饰非静态方法,是对调用该方法的对象加锁,俗称"对象锁"
public synchronized void sendMes() {
System.out.println(Thread.currentThread().getName() + " -> 发送消息");
}
public synchronized void call() {
System.out.println(Thread.currentThread().getName() + " -> 打电话");
}
}
/************************* 执行结果: *****************************/
A -> 发送消息
B -> 打电话
import java.util.concurrent.TimeUnit;
/**
* 该例子 call() 和 sendMes() 共用一个锁,谁先拿到谁先执行
*/
public class Lock2 {
public static void main(String[] args) {
Phone2 phone2 = new Phone2();
new Thread(() -> phone2.sendMes(), "A").start();
// sleep 一秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> phone2.call(), "B").start();
}
}
class Phone2 {
// synchronized 修饰非静态方法,是对调用该方法的对象加锁,俗称"对象锁"
public synchronized void sendMes() {
try {
TimeUnit.SECONDS.sleep(4); // sleep 4秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " -> 发送消息");
}
public synchronized void call() {
System.out.println(Thread.currentThread().getName() + " -> 打电话");
}
}
/************************* 执行结果: *****************************/
A -> 发送消息
B -> 打电话
import java.util.concurrent.TimeUnit;
/**
* call() 方法没有 synchronized 不受锁影响
*/
public class Lock3 {
public static void main(String[] args) {
Phone3 phone3 = new Phone3();
new Thread(() -> phone3.sendMes(), "A").start();
// sleep 一秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> phone3.call(), "B").start();
}
}
class Phone3 {
// synchronized 修饰非静态方法,是对调用该方法的对象加锁,俗称"对象锁"
public synchronized void sendMes() {
// sleep 4秒
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " -> 发送消息");
}
// 该方法没有 synchronized 不受锁影响
public void call() {
System.out.println(Thread.currentThread().getName() + " -> 打电话");
}
}
/************************* 执行结果: *****************************/
B -> 打电话
A -> 发送消息
import java.util.concurrent.TimeUnit;
/**
* 两个对象,两个非静态同步方法,该例子实际上是两把锁。
*/
public class Lock4 {
public static void main(String[] args) {
Phone4 phone41 = new Phone4();
Phone4 phone42 = new Phone4();
new Thread(() -> phone41.sendMes(), "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> phone42.call(), "B").start();
}
}
class Phone4 {
// synchronized 修饰非静态方法,是对调用该方法的对象加锁,俗称"对象锁"
public synchronized void sendMes() {
// sleep 4秒
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " -> 发送消息");
}
public synchronized void call() {
System.out.println(Thread.currentThread().getName() + " -> 打电话");
}
}
/************************* 执行结果: *****************************/
B -> 打电话
A -> 发送消息
import java.util.concurrent.TimeUnit;
/**
* 一个对象,两个静态同步方法,该例子实际上是共用一个锁(Phone5.class),谁先拿到谁先执行。
*/
public class Lock5 {
public static void main(String[] args) {
Phone5 phone5 = new Phone5();
new Thread(() -> phone5.sendMes(), "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> phone5.call(), "B").start();
}
}
class Phone5 {
// synchronized 修饰静态方法,是对调用该方法的class 模板加锁,俗称"类锁"。
public static synchronized void sendMes() {
// sleep 4秒
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " -> 发送消息");
}
public static synchronized void call() {
System.out.println(Thread.currentThread().getName() + " -> 打电话");
}
}
/************************* 执行结果: *****************************/
A -> 发送消息
B -> 打电话
import java.util.concurrent.TimeUnit;
/**
* 两个对象,两个静态同步方法,该例子实际上是共用一个锁(Phone6.class),谁先拿到谁先执行。
*/
public class Lock6 {
public static void main(String[] args) {
Phone6 phone61 = new Phone6();
Phone6 phone62 = new Phone6();
new Thread(() -> phone61.sendMes(), "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> phone62.call(), "B").start();
}
}
class Phone6 {
// synchronized 修饰静态方法,是对调用该方法的class 模板加锁,俗称"类锁"。
public static synchronized void sendMes() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " -> 发送消息");
}
public static synchronized void call() {
System.out.println(Thread.currentThread().getName() + " -> 打电话");
}
}
/************************* 执行结果: *****************************/
A -> 发送消息
B -> 打电话
import java.util.concurrent.TimeUnit;
/**
* 一个对象,一个静态同步方法,一个非静态同步方法
* 该例子实际上是两个锁(Phone7.class 和 phone7对象)。
* 说明:如果一个线程 A 调用一个实例对象的非静态 synchronized 方法,
* 而线程 B 需要调用这个实例对象所属类的静态 synchronized 方法,是允许的,不会发生互斥现象,
* 因为访问静态 synchronized 方法占用的锁是当前类的锁,
* 而访问非静态 synchronized 方法占用的锁是当前实例对象锁。
*/
public class Lock7 {
public static void main(String[] args) {
Phone7 phone7 = new Phone7();
new Thread(() -> phone7.sendMes(), "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> phone7.call(), "B").start();
}
}
class Phone7 {
// synchronized 修饰静态方法,是对调用该方法的class 模板加锁,俗称"类锁"。
public static synchronized void sendMes() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " -> 发送消息");
}
// synchronized 修饰非静态方法,是对调用该方法的对象加锁,俗称"对象锁"
public synchronized void call() {
System.out.println(Thread.currentThread().getName() + " -> 打电话");
}
}
/************************* 执行结果: *****************************/
B -> 打电话
A -> 发送消息
import java.util.concurrent.TimeUnit;
/**
* 两个对象,一个静态同步方法,一个非静态同步方法
* 该例子实际上是两个锁(Phone8.class 和 phone82对象)。
*/
public class Lock8 {
public static void main(String[] args) {
Phone8 phone81 = new Phone8();
Phone8 phone82 = new Phone8();
new Thread(() -> phone81.sendMes(), "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> phone82.call(), "B").start();
}
}
class Phone8 {
// synchronized 修饰静态方法,是对调用该方法的class 模板加锁,俗称"类锁"。
public static synchronized void sendMes() {
// sleep 4秒
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " -> 发送消息");
}
// synchronized 修饰非静态方法,是对调用该方法的对象加锁,俗称"对象锁"
public synchronized void call() {
System.out.println(Thread.currentThread().getName() + " -> 打电话");
}
}
/************************* 执行结果: *****************************/
B -> 打电话
A -> 发送消息
线程系列博文:
线程系列 1 - 线程基础
线程系列 2 - 并发编程之线程池 ThreadPool 的那些事
线程系列 3 - 关于 CompletableFuture
线程系列 5 - CAS 和 JUC原子类
线程系列 6 - JUC相关的显示锁
线程系列 7 - JUC高并发容器类
.