并发
同一个对象被多个线程同时操作
线程同步
解决方案:队列 + 锁
线程同步其实是一种等待机制,多个需要同时访问该对象的线程进入 这个对象的 等待池,形成队列。等待前面线程使用完毕,下一个线程再使用。
关键词 synchronized
存在的问题:
- 导致性能下降,一个线程持有锁,会导致其他所有需要此锁的线程挂起
- 多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
- 如果一个优先级高的线程等待优先级低的线程释放锁,会导致优先级倒挂,引起性能问题。
// 用在代码块上synchronized() {// 同步代码块}// 用在方法声明上, 一定是用this对象锁,表示整个方法体都需要同步。// 可能会无故扩大同步范围,导致程序执行效率降低,所以不太常用。public synchronized void withdraw() {...}// 在静态方法上使用synchronized,找的是类锁// 一个对象一把锁,100个对象100把对象锁。但是100个对象,只有一个类锁public synchronized static void withdraw() {}
synchronized后面小括号中的数据是多线程共享的数据,才能达到多线程排队。
哪些变量
- 实例变量:堆中
- 静态变量:方法区中
- 局部变量:栈中
- 常量:不可修改
常量不可修改,局部变量不共享,所以局部变量和常量永远不会有 线程安全问题。但是实例变量和静态变量可能存在线程安全问题。
同步范围越小越好
所以在局部变量时,建议使用StringBuilder,因为局部变量不存在线程安全问题,StringBuilder也没有同步机制
等待/通知机制
需要注意的是等待/通知机制使用的是必须使用同一个对象锁,如果你两个线程使用的是不同的对象锁,那它们之间是不能用等待/通知机制通信的
synchronized版
package com.liangwei.kuang.demo02;/*** 线程间通信问题:生产者、消费者模型! 等待唤醒,通知唤醒* 线程交替执行,生产者 和 消费者 操作同一个变量 num=0* 生产者 生产 使 num++* 消费者 消费 使 num--*/public class Demo02 {public static void main(String[] args) {Data data = new Data();new Thread(()->{for (int i = 0; i < 10; i++) {try {data.increment();} catch (InterruptedException e) {e.printStackTrace();}}}, "producer").start();new Thread(()->{for (int i = 0; i < 10; i++) {try {data.decrease();} catch (InterruptedException e) {e.printStackTrace();}}}, "consumer").start();}}/*** 生产者、消费者模型:等待、业务、通知*/class Data {private int num = 0;public synchronized void increment() throws InterruptedException {if(num != 0) { // 如果num不为0,就等待,num为0就工作// 等待this.wait();}num++;System.out.println(Thread.currentThread().getName() + "->" + num);// 通知this.notify();}public synchronized void decrease() throws InterruptedException {if(num == 0) { // 如果num为0就等待,不为0就开始工作// 等待this.wait();}num--;System.out.println(Thread.currentThread().getName() + "->" + num);// 通知this.notify();}}
问题:如果有四个线程的情况下A,B, C, D,两个生产两个消费 —-》 虚假唤醒问题

CPU底层的唤醒实现机制,注定了虚假唤醒是存在的。因此,在 if 语句下,如果线程被虚假唤醒了,但是又没有获得锁,会被
