Lock锁重点

回顾一下sleep和wait区别

  • sleep来自Thread类,wait来自Object类
  • sleep睡觉抱着锁不会释放锁,wait会释放锁
  • sleep可以在任何地方使用,而wait必须在同步代码块中使用
  • sleep需要捕获异常,而wait不需要

并发编程本质:充分利用CPU资源 

并发:多线程操作同一资源(CPU单核,模拟出来多条线程,天下武功,唯快不破,快速交替)

并行:多个人一起行走 (CPU多核,多个线程可以同时执行,线程池)

线程有几个状态 

public enum State {
        // 新生 
        NEW,
        // 运行
        RUNNABLE,
        // 阻塞
        BLOCKED,
        // 等待,死死地等
        WAITING,
        // 超时等待 (过时不等)    
        TIMED_WAITING,
        // 终止
        TERMINATED;
}

传统的synchronized锁

package com.kuang.demo01;

// 基本的卖票例子

/**
 * 真正的多线程开发,公司中的开发
 *  线程就是一个单独的资源类,没有任何附属的操作
 */
public class SaleTicketDemo01 {
    public static void main(String[] args) {
        // 并发 多线程操作同一个资源类
        final Ticket ticket=new Ticket();
        // 函数式接口
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 40; 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锁接口(常用ReentrantLock类可重入锁)

// 资源类 OOP
// Lock三部曲
// 1、new ReentrantLock()
// 2、lock.lock() 加锁
// 3、lock.unlock() 解锁
class Ticket2{
    private int number=30;
    Lock lock=new ReentrantLock();//可重入锁
    // 卖票的方式
    public void sale(){
        lock.lock();
        try{
            // 业务代码
            if (number>0){
                System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"票,剩余:"+number);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

Synchronized和Lock区别

  • Synchronized是java关键字,Lock是Java类
  • Synchronized是自动释放锁的,而Lock必须手动释放锁,否则就会死锁
  • Synchronized适合锁少量的代码同步问题,而Lock适合锁大量的代码同步
  • Synchronized是无法判断获取锁的状态,而Lock可以判断是否获取到了锁
  • Synchronized是可重入锁,不可以中断的,非公平锁,而Lock是可重入锁,可判断,非公平锁(可设置)
  • Synchronized 线程1(获得锁,阻塞)线程2(阻塞,傻傻的等)而Lock不一定会等待下去

面试点:单例模式、排序算法、生产者和消费者、死锁 

传统的生产者和消费者

package com.kuang.pc;

// 线程之间的通信问题:生产者和消费者问题
// 线程交替执行 线程A B操作同一个变量
public class A {
    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;
    public synchronized void increment() throws InterruptedException {
        while (number!=0){
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
        while (number==0){
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        this.notifyAll();
    }
}

注意点:当超过2个线程则判断等待时必须使用while,不能用if,因为会出现虚假唤醒情况(防止),即线程被唤醒,但不会通知、中断或者超时 

Lock版的生产者和消费者 

package com.kuang.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class B {
    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();

    public void increment() throws InterruptedException {
        lock.lock();
        try{
            // 业务
            while (number!=0){
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void decrement() throws InterruptedException {
        lock.lock();
        try{
            // 业务
            while (number==0){
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

Condition精准的通知和唤醒线程

package com.kuang.pc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
// 先执行线程A 再执行B 后执行C 一直保持这个顺序
public class C {
    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{
    private Lock lock=new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();
    private int number=1;
    public void printA(){
        lock.lock();
        try {
            while (number!=1){
                condition1.await();
            }
            number=2;
            System.out.println(Thread.currentThread().getName()+"---->AAA");
            condition2.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void printB(){
        lock.lock();
        try {
            while (number!=2){
                condition2.await();
            }
            number=3;
            System.out.println(Thread.currentThread().getName()+"---->BBB");
            condition3.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void printC(){
        lock.lock();
        try {
            while (number!=3){
                condition3.await();
            }
            number=1;
            System.out.println(Thread.currentThread().getName()+"---->CCC");
            condition1.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

八锁现象

package com.kuang.lock8;

import java.util.concurrent.TimeUnit;
// 结果都是先发短信后打电话
public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(()->{phone.sendMsg();},"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{phone.call();},"B").start();
    }
}

class Phone{
    // synchronized锁的对象是方法的调用者
    // 两个方法使用的是同一把锁,谁先获取到谁先执行
    public synchronized void sendMsg(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call(){
        System.out.println("打电话");
    }
}

synchronized锁的对象是方法的调用者 两个方法使用的是同一把锁,谁先获取到谁先执行 

package com.kuang.lock8;

import java.util.concurrent.TimeUnit;
// 3、增加了1个普通方法后,1s后先打印hello,等到了4s打印发短信
// 4、两个对象,两个同步方法,先打电话再发短信
public class Test2 {
    public static void main(String[] args) {
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();
        new Thread(()->{phone1.sendMsg();},"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //new Thread(()->{phone.hello();},"B").start();
        new Thread(()->{phone2.call();},"B").start();
    }
}

class Phone2{
    // synchronized锁的对象是方法的调用者
    // 两个方法使用的是同一把锁,谁先获取到谁先执行
    public synchronized void sendMsg(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call(){
        System.out.println("打电话");
    }

    //
    public void hello(){
        System.out.println("hello");
    }
}

这里两个对象调用即有2把锁,这时根据等待时间长短决定谁先执行 结果即先打电话后发短信

package com.kuang.lock8;

import java.util.concurrent.TimeUnit;
// 5、增加2个静态的同步方法 一个对象调用
// 6、增加2个静态的同步方法  两个对象调用
// 结果:先发短信再打电话
public class Test3 {
    public static void main(String[] args) {
        // 两个对象都来自Class,记住锁的是Class(因为同步方法加了static)
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();
        new Thread(()->{phone1.sendMsg();},"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{phone2.call();},"B").start();
    }
}
class Phone3{
    // 类一加载就有了,锁的是Class模板
    public static synchronized void sendMsg(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call(){
        System.out.println("打电话");
    }

}

加了static的同步方法,锁的是Class,所以不管是几个对象调用,谁先获取锁谁就先执行

package com.kuang.lock8;

import java.util.concurrent.TimeUnit;

// 7、1个静态同步方法 1个同步方法 1个对象
// 8、1个静态同步方法 1个同步方法 2个对象
public class Test4 {
    public static void main(String[] args) {
        // 两个对象都来自Class,记住锁的是Class(因为同步方法加了static)
        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();
        new Thread(()->{phone1.sendMsg();},"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{phone2.call();},"B").start();
    }
}

class Phone4{
    // 类一加载就有了,锁的是Class模板
    public static synchronized void sendMsg(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    // synchronized锁的对象是方法的调用者
    public synchronized void call(){
        System.out.println("打电话");
    }
}

 先打电话再发短信因为静态同步方法锁的是Class,而普通同步方法锁的方法的调用者

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在C++中,unique_lock是一个用于管理互斥的RAII(资源获取即初始化)类。它提供了一种更灵活的方式来管理互斥的加和解操作。 unique_lock的解操作非常简单,只需要调用其成员函数unlock()即可。例如: ```cpp #include <iostream> #include <mutex> std::mutex mtx; void foo() { std::unique_lock<std::mutex> lock(mtx); // 互斥已经在构造unique_lock对象时被加 // 执行一些需要保护的操作 // 解互斥 lock.unlock(); // 在解后可以执行一些不需要互斥保护的操作 // 再次加互斥 lock.lock(); // 执行一些需要保护的操作 // 解互斥 lock.unlock(); } int main() { foo(); return 0; } ``` 在上面的示例中,我们首先创建了一个std::mutex对象mtx,然后在函数foo()中创建了一个unique_lock对象lock,并将mtx作为参数传递给它。在unique_lock对象的构造函数中,互斥会被自动加。然后我们可以执行一些需要保护的操作。当我们调用lock.unlock()时,互斥会被解,这样我们就可以执行一些不需要互斥保护的操作。最后,我们可以再次调用lock.lock()来重新加互斥,并执行一些需要保护的操作。最后,当unique_lock对象超出作用域时,析构函数会自动解互斥。 需要注意的是,unique_lock对象的unlock()和lock()成员函数可以在任何时候调用,而不仅仅是在构造函数和析构函数中。这使得我们可以更灵活地控制互斥的加和解操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值