Lock锁与八锁现象
一、Lock
传统 synchronized
// 基本的卖票例子
/*
* 真正的多线程开发,公司中的开发、降低耦合类
* 线程就是一个单独的资源类,没有任何的附属操作
* 1、 属性、方法
*/
public class SaleTicketDemo01 {
public static void main(String[] args) {
//并发:多个线程操作同一个资源类,把资源类丢入线程
Ticket2 ticket = new Ticket2();
// @FunctionalInterface 函数式接口,jdk1.8 lambda (参数)->{ 代码 }
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
};
},"B").start();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"C").start();
}
}
//资源类 OOP
class Ticket{
// 属性、方法
private int number = 50;
// 卖票的方式
// synchronized 本质 锁、排队
public synchronized void sale(){
if(number>0){
System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"张票,剩余"+number);
}
}
}
Lock 接口
公平锁:十分公平,先来后到
非公平锁:十分不公平:可以插队(默认)
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SaleTicketDemo02 {
public static void main(String[] args) {
//并发:多个线程操作同一个资源类,把资源类丢入线程
Ticket2 ticket = new Ticket2();
// @FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)->{ 代码 }
new Thread(()->{for (int i = 0; i < 60; i++)ticket.sale(); },"A").start();
new Thread(()->{for (int i = 0; i < 60; i++)ticket.sale(); },"B").start();
new Thread(()->{for (int i = 0; i < 60; i++)ticket.sale(); },"C").start();
}
}
// Lock三部曲
// 1、new ReentrantLock
// 2、lock.lock(); 加锁
// 3、finally=>lock.unlock(); 解锁
class Ticket2 {
// 属性、方法
private int number = 30;
Lock lock = new ReentrantLock();
public void sale(){
lock.lock(); // 加锁
//lock.trylock();
try {
// 业务代码
if(number>0){
System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"张票,剩余"+number);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();// 解锁
}
}
}
synchronized 和 Lock 区别
1、synchronized Java内置关键字,Lock是一个Java类
2、synchronized 无法判断获取锁的状态, Lock可以判断是否获取到了锁
3、synchronized 会自动释放锁, Lock必须要手动释放锁,如果不释放,死锁
4、synchronized 线程1(获得锁,阻塞),线程2(等待,傻傻的等);Lock就不一定会一直等待下去; lock.trylock 尝试获取
5、synchronized 可重入锁,不可以中断的,非公平的;Lock 可重入锁,可以判断锁,非公平(可以自己设置)
6、synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码!
锁是什么,如何判断锁的是什么
二、生产者和消费者问题
面试:单例模式、排序算法、生产者和消费者、死锁
生产者和消费者问题 synchronized 版本
notify是随机唤醒一个等待的线程
/*
* 线程之间的通信问题: 生产者和消费者问题! 等待唤醒,通知唤醒
* 线程交替执行 A B 操作同一个变量 num = 0
* A num+1
* B num-1
*/
public class ProducerConsumerSynchronized {
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();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
// 判断等待,业务,通知
class Data{ // 数字 资源类
private int number = 0;
// +1
public synchronized void increment() throws InterruptedException {
if(number!=0){ // 0
// 等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他线程, 我+1完毕了
this.notifyAll();
}
// -1
public synchronized void decrement() throws InterruptedException {
if(number==0){ // 1
// 等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他线程,我-1完毕了
this.notifyAll();
}
}
问题存在,A B C D 4个线程还安全么 虚假唤醒
if判断改为while唤醒
/*
* 线程之间的通信问题: 生产者和消费者问题! 等待唤醒,通知唤醒
* 线程交替执行 A B 操作同一个变量 num = 0
* A num+1
* B num-1
*/
public class ProducerConsumerSynchronized {
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();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
// 判断等待,业务,通知
class Data{ // 数字 资源类
private int number = 0;
// +1
public synchronized void increment() throws InterruptedException {
while(number!=0){ // 0 的时候干活
// 等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他线程, 我+1完毕了
this.notifyAll();
}
// -1
public synchronized void decrement() throws InterruptedException {
while(number==0){ // 1
// 等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他线程,我-1完毕了
this.notifyAll();
}
}
生产者消费者问题 JUC版
通过Lock找到Condition
代码实现
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ProducerConsumerJUC {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
// 判断等待,业务,通知
class Data2{ // 数字 资源类
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//condition.await(); // 等待
//condition.signalAll(); // 唤醒全部
// +1
public void increment() throws InterruptedException {
lock.lock();
try {
// 业务代码
while(number!=0){ // 0
// 等待
condition.await();
}
// condition.await(); 等待
// condition.signalAll(); 唤醒全部
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他线程, 我+1完毕了
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
// -1
public void decrement() throws InterruptedException {
lock.lock();
try {
while(number==0){ // 1
// 等待
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他线程, 我-1完毕了
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
任何一个新的技术,绝对不是仅仅覆盖了原来的技术,优势和补充
Condition 精准的通知和唤醒进程
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
* A执行完调用B B->C C->A
* A 执行完调用B,B执行完调用C,C执行完调用A
*/
public class ProducerConsumerJUCShun {
public static void main(String[] args) {
Data3 data = new Data3();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printC();
}
},"C").start();
}
}
class Data3{ // 资源类 Lock
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int number = 1; //1A 2B 3C
public void printA(){
lock.lock();
try {
//业务,判断->执行->通知
while (number!=1){
// 等待
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"=>AAAAAAAAA");
// 唤醒, 唤醒指定的人,B
number = 2;
//唤醒指定进程 condition2 B
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
//业务,判断->执行->通知
while (number!=2){
// 等待
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"=>BBBBBBBBB");
// 唤醒, 唤醒指定的人,C
number = 3;
condition3.signal(); //唤醒指定进程 condition3 C
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
//业务,判断->执行->通知
while (number!=3){
// 等待
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"=>CCCCCCCCC");
// 唤醒, 唤醒指定的人,c
number = 1;
condition1.signal(); //唤醒指定进程 condition1 A
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
三、8锁现象
如何判断锁的是谁。永远的知道什么是锁,锁到地锁的是谁!
深刻理解锁
import java.util.concurrent.TimeUnit;
/*** 8锁,就是关于锁的8个问题
* 1、标准情况下,两个线程先打印 发短信还是 打电话? 1/发短信 2/打电话
* 1、sendSms延迟4秒,两个线程先打印 发短信还是 打电话? 1/发短信 2/打电话
* synchronized 放在方法上锁的是方法调用者,这里只有一个调用者 phone
*/
public class Synchronized {
public static void main(String[] args) {
Phone phone = new Phone();
//锁的存在
new Thread(()->{
// 捕获
try {
phone.send();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t1").start();
// 延迟执行下面t2线程,但是结果无影响,因为他们用的是同把锁。
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"t2").start();
}
//两个线程持有的是同一个锁,谁先拿到谁先执行完,另一个才执行
}
class Phone {
// synchronized 锁的对象是方法的调用者!、
// 两个方法用的是同一个锁,谁先拿到谁执行!
public synchronized void send() throws InterruptedException {
System.out.println(Thread.currentThread().getName() + "sendMessage");
TimeUnit.SECONDS.sleep(2);
call();//与send()是同一把锁 this,锁的是方法调用者,不是两把锁
}
public synchronized void call() {
System.out.println(Thread.currentThread().getName() + "call");
}
}
/**
* 3、 增加了一个普通方法后!先执行发短信还是Hello? 普通方法
* 4. 两个对象,两个同步方法, 发短信还是 打电话? // 打电话
*/
public class Test2 {
public static void main(String[] args) {
// 两个对象,两个调用者,两把锁
Phone1 phone = new Phone1();
Phone1 phone2 = new Phone1();
//锁的存在
new Thread(() -> {
phone.sendMessage();
}, "A").start();
// 捕获
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
// phone.hello();
phone2.call();
}, "B").start();
}
}
class Phone1 {
// synchronized 锁的对象是方法的调用者!
public synchronized void sendMessage() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "sendMessage");
}
// 这里没有锁!不是同步方法,不受锁的影响
public synchronized void call() {
System.out.println(Thread.currentThread().getName() + "call");
}
//这里没有锁,不受锁的影响
public void hello(){
System.out.println("hello");
}
}
/**
* 5.增加两个静态的同步方法
* synchronized 锁的是方法调用者
* static 静态方法类一加载就有了 锁的是CLass
*
* 6. 两个对象,两个静态同步方法,先发短信还是先打电话 //发短信
*
*/
public class Test3 {
public static void main(String[] args) {
// 两个对象的Class类模板只有一个,static锁的是Class
Phone2 phone2 = new Phone2();
Phone2 phone3 = new Phone2();
//锁的存在
new Thread(() -> {
phone2.sendMessage();
}, "A").start();
// 捕获
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone3.call();
}, "B").start();
}
}
// Phone2唯一的一个 Class 对象
class Phone2 {
// synchronized 锁的对象是方法的调用者!
// static 静态方法
// 类一加载就有了!锁的是Class
public static synchronized void sendMessage() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "sendMessage");
}
public static synchronized void call() {
System.out.println(Thread.currentThread().getName() + "call");
}
}
/**
* 7.一个静态同步方法,一个普通同步方法 ,一个对象,先输出哪一个 //打电话
*
* 8. 一个静态同步方法,一个普通同步方法, 两个对象,先打印哪一个 //B打电话
*/
public class Test4 {
public static void main(String[] args) {
// 两个对象的Class类模板只有一个,static,锁的是Class
Phone3 phone3 = new Phone3();
Phone3 phone4 = new Phone3();
//锁的存在
new Thread(() -> {
phone3.sendMessage();
}, "A").start();
// 捕获
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone4.call();
}, "B").start();
}
}
// Phone3唯一的一个 Class 对象
class Phone3 {
// 静态的同步方法 锁的是 Class 类模板
public static synchronized void sendMessage() {
try {
TimeUnit.SECONDS.sleep(4); //延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "sendMessage");
}
// 普通的同步方法 锁的调用者
public synchronized void call() {
System.out.println(Thread.currentThread().getName() + "call");
}
}
小结:
- synchronized 方法锁的是调用者 new this 具体的一个手机
- static 方法锁的是模板 static Class 唯一的一个模板