多线程中的锁

120 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第21天,点击查看活动详情

大家好,我是尚影嫣🌷,一名Java后端程序媛。如果您喜欢我的文章,欢迎点赞➕关注❤️,让我们一起成为更好的我们~

多线程中有显式锁与隐式锁,区别如下:

  • synchronized是隐式锁,是Java中的关键字,由JVM维护,属于JVM层面的锁;lock是显式锁,是JDK5之后才出现的类,使用Lock是调用对应的API,是API层面的锁。
  • 使用显式锁时,需要手动获取和释放锁,使用隐式锁则不需要。

一、显式锁

  1. lock是显式锁(需手动获取和释放);
  2. lock只有代码块锁;
  3. 使用lock锁,Jvm将花费较少的时间来调度线程,性能更好,且具有更好的扩展性;
  4. 优先使用顺序:lock>同步代码块>同步方法;

使用lock时,需要使用者手动去获取锁和释放锁。如果没有释放锁,就可能出现死锁的现象。手动获取锁的方法:lock.lock(); 释放锁的方法:lock.unlock()。

package yanf.ticket;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 显式锁
 *
 */
public class SaleTicketsLock {
    public static void main(String[] args) {
        // 多态
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable {
        // 票数
        private int count = 10;
        // 显式锁lock,fair参数为true则为公平锁,先到先得,排队
        private Lock l = new ReentrantLock(true);
        @Override
        public void run() {
            while (true) {
                // 获取锁
                l.lock();
                if (count > 0) {
                    //卖票
                    System.out.println("正在准备卖票");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count--;
                    System.out.println(Thread.currentThread().getName() + "出票成功,余票:" + count);
                } else {
                    break;
                }
                // 释放锁
                l.unlock();
            }
        }
    }
}

二、同步锁

使用synchronized关键字时,无需写其他代码,程序就能自动获取锁和释放锁。synchronized是由系统维护的,系统会自动地让程序释放占用的锁。

package yanf.ticket;

public class SaleTicketsSyncBlock {
    public static void main(String[] args) {
        // 多态
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable {
        // 票数
        private int count = 10;
        // 加锁对象
        private Object o = new Object();

        @Override
        public void run() {
            while (true) {
                synchronized (o) {
                    if (count > 0) {
                        //卖票
                        System.out.println("正在准备卖票");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName() + "出票成功,余票:" + count);
                    } else {
                        break;
                    }
                }
            }
        }
    }
}

三、同步方法

同步代码块可以直接定义在某个方法中,使方法的部分操作进行同步处理。但是如果现在某一个方法中的全部操作都需要进行同步处理进行同步处理,则可以采用同步方法的形式进行定义,即在方法声明上使用synchronized关键字即可。

package yanf.ticket;

public class SaleTicketsSyncMethod {
    public static void main(String[] args) {
        // 多态
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable {
        // 票数
        private int count = 10;

        @Override
        public void run() {
            while (true) {
                boolean flag = sale();
                if (!flag) {
                    break;
                }
            }
        }

        /**
         * 同步方法
         * 在方法加上synchronize关键字,锁对象是this(如果第7-9行改成new Thread(new Ticket()).start();则锁无效);
         * 如果加在静态方法上,锁对象是对象.class-字节码文件对象。
         *
         * @return
         */
        public synchronized boolean sale() {
            if (count > 0) {
                //卖票
                System.out.println("正在准备卖票");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println(Thread.currentThread().getName() + "出票成功,余票:" + count);
                return true;
            }
            return false;
        }
    }
}