JUC学习笔记一:锁,生产者和消费者问题

笔记来源狂神说java

1.进程和线程

  • 进程:就是一个程序。
  • 进程包含多个线程,至少包含一个。
  • 线程:开了一个程序的进程中有用于不同功能的线程。
  • java默认开启了2个线程:main,GC
  • 对于Java而言有几种开启线程的方式:Thread,Runnable,Callable
  • Java不能开启线程:Java是运行在虚拟机上的,不可以操作硬件
public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

    private native void start0();

2.并发和并行

  • 并发:多个线程操作同一个资源(单核CPU,模拟出多个线程,快速交替占用CPU)
  • 并行:多个线程同时运行(多核CPU,可用线程池)

获取cpu核数:
在这里插入图片描述
并发编程的本质:充分利用cpu的资源。

3.线程有几个状态?

public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW, //新建  刚开始认识

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE, //运行  甜蜜期

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED, //阻塞 他没有回来。

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called {@code Object.wait()}
         * on an object is waiting for another thread to call
         * {@code Object.notify()} or {@code Object.notifyAll()} on
         * that object. A thread that has called {@code Thread.join()}
         * is waiting for a specified thread to terminate.
         */
        WAITING, //等待,死死的等。。。

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING, //超时等待 等了好久还是没来

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;//终止,等不到了。。。
    }

4.wait和sleep的区别

1.来自不同的类

  • wait–> Object
  • sleep–>Thred

2.关于锁的释放

wait会释放锁
sleep不会释放锁,抱着锁睡了就不会懂得释放了。。。

3.使用的范围不同

  • wait必须在同步代码块中
  • sleep在什么地方都可以睡。。。

4.是否需要捕获异常

  • wait不需要捕获异常
  • sleep需要捕获异常

5.Lock锁(重点)

1.传统的synchronized

public class Test1 {

    public static void main(String[] args) {
        //并发:多线程操作同一个资源类
        Ticket ticket = new Ticket(); //线程资源类
        //()->{}

        //将线程资源类丢进线程中
        new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        }, "C").start();
    }
}

//线程应该是一个独立的资源类,没有其他附属的操作,降低耦合性
class Ticket {
    private int num = 50;

    public synchronized void sale() {
        if (num > 0) {
            System.out.println(Thread.currentThread().getName() + "还剩" + (num--) + "张票");
        }
    }
}

2.lock接口

查看文档说明
在这里插入图片描述

在这里插入图片描述
构造方法:
在这里插入图片描述
java默认的是非公平锁

  1. 非公平锁:进程之间可以互相抢占
  2. 公平锁:进程之间不可以互相抢占
public class Test2 {

    public static void main(String[] args) {
        Ticket2 ticket = new Ticket2();
//        //()->{}
        new Thread(() -> {
            for (int i = 0; i < 30; i++) ticket.sale();
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 30; i++) ticket.sale();
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 30; i++) ticket.sale();
        }, "C").start();
    }
}

class Ticket2 {
    private int num = 50;
    //锁对象
    Lock lock = new ReentrantLock();

    public synchronized void sale() {
        //加锁
        lock.lock();
        try {
            //业务
            if (num > 0) {
                System.out.println(Thread.currentThread().getName() + "还剩" + (num--) + "张票");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //释放锁
            lock.unlock();
        }

    }
}

3.synchronized 和lock的区别

  1. synchronized 是java内置的关键字,lock是一个类
  2. synchronized 无法判断获取锁的状态,lock可以判断是否获取到了锁
  3. Synchronized 会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁
  4. Synchronized 线程 1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下去;
  5. Synchronized 可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以 判断锁,非公平(可以自己设置);
  6. Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码!

6.生产者和消费者问题

synchronized版本

/**
 * 线程之间的通信问题:生产者和消费者的问题! 等待唤醒<---->通知唤醒
 * 线程交替执行 A  B 操作同一个变量   直到num==0
 *
 * A:num+1
 * B:num-1
 */
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;

    //+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();
    }
}

问题存在:虚假唤醒

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();
                }
            }
        },"D").start();
        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 四个线程同时去操作,那么就会出现虚假唤醒的情况。
原因是方法中只有一次的判断num==0的操作,而此时有多个线程在操作num。
在这里插入图片描述
解决办法:可以将if改为while
在这里插入图片描述

JUC版本

在这里插入图片描述
通过lock找到Condition
在这里插入图片描述
代码

public class B {
    public static void main(String[] args) {
        Data2 data = new Data2();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        },"D").start();
    }

}

class Data2 {
    private int number = 10;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    //+1
    public void increment() {
        lock.lock();
        try {
            while (number == 0) {
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            // 通知其他线程,我+1完毕了
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    //-1
    public void decrement()  {
        lock.lock();
        try{
            while (number == 0) { // 1
                // 等待
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            // 通知其他线程,我-1完毕了
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

Condition实现精准唤醒:


/**
 * Condition 实现精准唤醒
 */
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 {
    Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();

    private int number = 1; // 1A 2B 3C

    //+1
    public void printB() {
        lock.lock();
        try {
            while (number !=1) {
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName() + "=>" +"bbbbbbbbbbb");
            number=2;
            // 通知其他线程,我+1完毕了
            condition2.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    //-1
    public void printA()  {
        lock.lock();
        try{
            while (number !=2) { // 1
                // 等待
                condition2.await();
            }
            number=3;
            System.out.println(Thread.currentThread().getName() + "=>" +"AAAAAAAAA");
            // 通知其他线程,我-1完毕了
            condition3.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void printC()  {
        lock.lock();
        try{
            while (number !=3) { // 1
                // 等待
                condition3.await();
            }
            number=1;
            System.out.println(Thread.currentThread().getName() + "=>" +"CCCCCCC");
            // 通知其他线程,我-1完毕了
            condition1.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

7.锁现象

  1. 一个对象,synchronized在方法中,锁的是调用者,几个方法用的是同一个锁,那么谁先占用锁,谁就先运行。
  2. 两个对象,synchronized在方法中,就有两个同步方法,谁先调用谁就先执行。
  3. 一个对象,方法都为静态方法,synchronized在方法中,这时候锁的是类模板,只有一个锁,谁先占用谁就先执行
  4. 两个对象,方法为静态方法,synchronized在方法中,也是有两个同步方法,谁先调用谁就先执行
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值