带你深刻认识一下synchronized和Lock

一、认识线程

1.1线程如何启动

    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();  // 调用本地C++代码 Java没有权限

Java无法直接操作硬件,要通过native调用本地方法,由底层的C++开启线程。

1.2并发和并行

并发(多个线程操作同一个资源)

  • CPU一核 ,模拟出来多条线程,快速交替各个线程产生假象

并行(多个线程一起运行)

  • CUP多核,同一个时间,多个线程同时执行。
public class Dome01 {
    public static void main(String[] args) {
        // 获取CPU的核数
        // CPU 密集型,IO密集型
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}

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

1.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 <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * 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; // 终止
    }

wait/sleep 区别:

  • 来自不同的类
    • wait =》 Object
    • sleep =》 Thread
  • 关于锁的释放
    • wait会释放锁
    • sleep 不会释放锁
  • 使用范围
    • wait必须在同步代码块中使用
    • sleep可以再任何地方使用

二、synchronized锁与Lock锁

2.1 synchronized

接下来我们来看一个售票的小栗子:
/**
 *  售票
 */
public class SaleTicketDome01 {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();

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

class Ticket {

    private int number = 50;
	// synchronized 锁
    public synchronized void sale(){
        if (number > 0){
            System.out.println(Thread.currentThread().getName()
                    +"卖出了"+(number--)+"张票,剩余"+number);
        }
    }
}
	这是一个售票的小程序,sale()售票方法被synchronized 修饰。防止出现两个人抢到同一张票的问题。

2.2 Lock锁

在这里插入图片描述
ReentrantLock()

    /**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
    public ReentrantLock() {
        sync = new NonfairSync(); // 如果是一个无参构造的话就是一个非公平锁
    }

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        // 如果传入的是一个布尔类型
        // true:公平锁
        // false:非公平锁
        sync = fair ? new FairSync() : new NonfairSync();
    }

公平锁:先来后到

非公平锁:可以插队(默认)
在这里插入图片描述
接下来我们看一看使用Lock锁的售票系统

/**
 *  售票
 *  公司中的开发
 *	不会继承Thread 或 实现Runable接口
 */
public class SaleTicketDome01 {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        new Thread(()->{ for (int i = 0; i < 40; i++) ticket.sale(); },"一号口").start();
        new Thread(()->{ for (int i = 0; i < 40; i++) ticket.sale(); },"二号口").start();
        new Thread(()->{ for (int i = 0; i < 40; i++) ticket.sale(); },"三号口").start();
    }
}

/**
 * Lock 三部曲
 * new ReentrantLock();
 * lock.lock(); 加锁
 * lock.unlock(); 解锁
 */
class Ticket {

    private int number = 50;
    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 线程1(获得锁,阻塞),线程2(等待,一直等),Lock锁就不一定会等待
  • Synchronized 可重入锁,不可以中断的,非公平,Lock 可重入锁,可以判断锁,可以设置公平非公平锁
  • Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码

2.3 synchronized线程通讯

2.3.1 synchronized线程通讯方法

Thread.wait():使当前线程进入等待状态。

在这里插入图片描述

Thread notify():通知wait的线程唤醒。

在这里插入图片描述

2.3.2生产者消费者问题
public class ProducerConsumer {
    public static void main(String[] args) {
        Product product = new Product();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    product.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    product.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}

/**
 * 等待
 * 业务
 * 通知
 */
class Product{
    private int number = 0;

    // +1
    public synchronized void increment() throws InterruptedException {
        if (number != 0){ // 等待-1
        this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"===》"+number);
        this.notify();
        // 通知其他线程 number 已经增加
    }

    // -1
    public synchronized void decrement() throws InterruptedException {
        if (number == 0){  // 等待+1
        this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"===》"+number);
        this.notify();
        // 通知其他线程 number 已经减少
    }
}

如果有A,B,C,D四个线程;此程序就不再安全;存在虚假唤醒情况

在这里插入图片描述

解决方法:if 循环换为 while循环

class Product{
    private int number = 0;

    // +1
    public synchronized void increment() throws InterruptedException {
        while(number != 0){ // 等待-1
        this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"===》"+number);
        this.notify();
        // 通知其他线程 number 已经增加
    }

    // -1
    public synchronized void decrement() throws InterruptedException {
        while(number == 0){  // 等待+1
        this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"===》"+number);
        this.notify();
        // 通知其他线程 number 已经减少
    }
}

2.4Lock线程通讯

2.4.1 Lock线程通讯方法

newCondition() // 可以获得一个 Condition对象

在这里插入图片描述
Conditition作用就是替换Object类中wait notify 方法

在这里插入图片描述

Condition.await() // 让当前线程进入等待

在这里插入图片描述

Condition.signal() // 唤醒一个等待的线程
在这里插入图片描述

2.4.2 生产者消费者问题
public class ProducerConsumer {
    public static void main(String[] args) {
        Product product = new Product();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    product.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    product.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    product.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    product.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}


/**
 * 等待
 * 业务
 * 通知
 */
class Product{
    private int number = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    // +1
    public void increment() throws InterruptedException {
        lock.lock();
        try {
            while (number != 0) { // 等待-1
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "===》" + number);
            condition.signalAll();
            // 通知其他线程 number 已经增加
        }catch (Exception 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);
            // 通知其他线程 number 已经减少
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
2.4.3 Condition的优势

Condition可以精准的通知和唤醒线程,就以上程序来说,可以让 A、B 、C、D有序的执行

/**
 *  Condition 顺序执行
 *  A->B->C->A
 */
public class ConditionDominant {
    public static void main(String[] args) {
        Dome_01 dome = new Dome_01();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                dome.printA();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                dome.printB();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                dome.printC();
            }
        },"C").start();
    }
}
class Dome_01{
    Lock lock = new ReentrantLock();
    Condition conditionA = lock.newCondition();
    Condition conditionB = lock.newCondition();
    Condition conditionC = lock.newCondition();
    private int number = 1;

    public void printA(){
    lock.lock();
        try {
            // 业务 判断 执行 通知
            while (number != 1){
                conditionA.await();
            }
            System.out.println(Thread.currentThread().getName()+"====>A在执行");
            // 唤醒指定的人
                // 唤醒conditionB
                number = 2;
                conditionB.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printB(){
        lock.lock();
        try {
            while (number != 2){
                conditionB.await();
            }
            System.out.println(Thread.currentThread().getName()+"====>B在执行");
            number = 3;
            conditionC.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
        lock.unlock();
        }
    }
    public void printC(){
    lock.lock();
        try {
            while (number != 3){
                conditionC.await();
            }
            System.out.println(Thread.currentThread().getName()+"====>C在执行");
            number = 1;
            conditionA.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

													感谢阅读
													欢迎留言
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值