Synchronized 和 ReentrantLock 区别

代码

package com.auth.Sercurity.TreadDemo;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @author hujian
 * @version 1.0
 * @description: 线程模式售票
 * @date 2022/2/22 15:29
 */
public  class TicketDemo {

    protected static List<Integer> list=new ArrayList<>();

    static class  TicketThread extends Thread{
        private static Integer tickets=100;
        private static final ReentrantLock lock=new ReentrantLock();
        private  Object objectLock=new Object();
        @Override
        public void run(){
            while (tickets>0){
                selaTicketLock();
//                或者选择Synchornized方法
//                selaTicketSychronyzed();
            }
            System.out.println("==========");
            System.out.println(Thread.currentThread().getName()+"售票结束");
        }

        private void selaTicketLock() {
            try {
//                代码加锁
                lock.lock();
                if (tickets>0){
                    System.out.println(Thread.currentThread().getName() + "-" + "卖出最后第" + tickets + "张票");
                    list.add(tickets);
                    tickets--;
                }
            }catch (Exception e){
                System.out.println(e.getMessage());
            }finally {
//                必须手动解锁
                lock.unlock();
            }
        }


        /**
         * 如果是实例方法,那么默认的同步监听对象就是this,
         * 如果是静态方法,默认的同步监听对象是类的字节码对象;
         * 1.实例方法 private synchronized void sellTicketSyn(),默认的同步监听对象就是this,只能锁住同一个对象,在当前使用会出现重复售票;
         * 2.静态方法 private static synchronized void sellTicketSyn(),同步监听对象是类的字节码对象,相当于全局锁; 
         */
        private void selaTicketSychronyzed() {
            /**
             * 1.synchronized锁住的是括号里得对象,而不是代码
             * 2.常用的锁对象(this,字节码)
             *      a.当synchronized (TicketThread.class),程序正常执行,只有一份字节码,谁拥有字节码就有执行权
             *      b.当chnchronized(objectLock)或chnchronized(this),this代表当前对象,启动线程的时候看的实例了几次						TicketThread 类,如果
             *        TicketThread ticketThread = new TicketThread();
             *         Thread thread1=new Thread(ticketThread,"窗口1");
             *         Thread thread2=new Thread(ticketThread,"窗口2");
             *         那么只会有一个objectLock对象或者TicketThread对象。如果
             *          Thread  thread1= new TicketThread();
             *         Thread  thread2= new TicketThread();
             *         那么就会多个objectLock对象或者TicketThread对象,也会重复卖票
             *      c.synchronized(tickets),因为数量是变化的,所以当存在数量不相等时会同时进行。
             *
             */
            synchronized (this){
                if (tickets>0){
                    System.out.println(Thread.currentThread().getName() + "-" + "卖出最后第" + tickets + "张票");
                    list.add(tickets);
                    tickets--;
                }
            }
        }
    }


    public static void main(String[] args) {
        TicketThread ticketThread = new TicketThread();
        Thread thread1=new Thread(ticketThread,"窗口1");
        Thread thread2=new Thread(ticketThread,"窗口2");
        Thread thread3=new Thread(ticketThread,"窗口3");

//        Thread  thread1= new TicketThread();
//        Thread  thread2= new TicketThread();
//        Thread  thread3= new TicketThread();
//        thread1.setName("窗口1");
//        thread2.setName("窗口2");
//        thread3.setName("窗口3");
        System.out.println("开始售票");
        System.out.println("=================");
        thread1.start();
        thread2.start();
        thread3.start();
        // 提取出重复的票数,供自己参考
        List<Integer> result=new ArrayList<>();
        Map<Integer, Long> collect = list.stream().collect(Collectors.groupingBy(e -> e, Collectors.counting()));
        collect.forEach((a,b)->{
            if (b>1){
                result.add(a);
            }
        });
        System.out.println(result.toString());
        System.out.println(list.toString());
    }
}

特性

1.锁的对象
  1. synchorized锁的是对象,锁是保存在对象头里面的,根据对象头的数据来标识是否有线程获得锁和争抢锁。
  2. ReentrantLock锁的是线程,根据进入的线程和int类型的state标识锁的状态。
2.底层实现
  1. synchronized是JVM层面的锁,是Java的关键字,通过monitor的对象完成。对象只有在同步代码块或同步方法中才能调用wait/notify方法。synchronized的实现涉及锁的升级,具体为无锁,偏向锁,自旋锁,向OS申请重量级锁。
  2. ReentrantLock实现则是通过利用CAS(CompareAndSwap)自旋机制保证线程操作的原子性和volatile保证数据可见性以实现锁的功能。
3.是否可手动释放
  1. synchroized不需要用户自己去释放锁,Synchronized锁的代码执行完后系统会自动让线程释放对锁的占用;
  2. ReentrantLocak则需要用户手动加锁和释放锁,不手动使用unlock()可能会导致死锁现象。
4.是否可绑定条件

synchronized不能绑定,ReentrantLock通过绑定Condition结合await()/singal()方法实现线程的精准唤醒,而不是像synchronized通过Object类wait()/notify()/notifyAll()那样要么随即唤醒要么全部唤醒。

package com.auth.Sercurity.TreadDemo;

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

/**
 * @author hujian
 * @version 1.0
 * @description: ReentrantLock绑定条件
 *     依次打印 A/B/C
 * @date 2022/2/23 14:38
 */
public class ReentrantLockCondition {
    int number=1;
    private ReentrantLock lock=new ReentrantLock();
    private Condition condition1=lock.newCondition();
    private Condition condition2=lock.newCondition();
    private Condition condition3=lock.newCondition();

    void print1(){
        lock.lock();
        try {
            while (number!=1){
//                等待
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"---"+number);
            number=2;
//            唤醒
            condition2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    void print2(){
        lock.lock();
        try {
            while (number!=2){
//                等待
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"---"+number);
            number=3;
//            唤醒
            condition3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    void print3(){
        lock.lock();
        try {
            while (number!=3){
//                等待
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+"---"+number);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

// 
    public static void main(String[] args) {
        ReentrantLockCondition reentrantLockCondition=new ReentrantLockCondition();
        new Thread(()-> reentrantLockCondition.print1(),"A:").start();
        new Thread(()->reentrantLockCondition.print2(),"B:").start();
        new Thread(()->reentrantLockCondition.print3(),"C:").start();
    }
}
5.是否公平

synchronized为非公平锁 ReentrantLock则即可以选公平锁也可以选非公平锁,通过构造方法new ReentrantLock时传入boolean值进行选择,为空默认false非公平锁,true为公平锁。

    private ReentrantLock lock=new ReentrantLock(true);
    private ReentrantLock lock=new ReentrantLock(false);
6是否可以中断
  1. synchronized是不可中断类型的锁,除非加锁的代码中出现异常或正常执行完成;
  2. ReentrantLock则可以中断,可通过trylock(long timeout,TimeUnit unit)设置超时方法或者将lockInterruptibly()放到代码块中,调用interrupt方法进行中断。
           try {
//                代码加锁
                lock.lock();
                lock.tryLock(2, TimeUnit.SECONDS);
                lock.lockInterruptibly();
                if (tickets>0){
                    System.out.println(Thread.currentThread().getName() + "-" + "卖出最后第" + tickets + "张票");
                    list.add(tickets);
                    tickets--;
                }
            }catch (Exception e){
                System.out.println(e.getMessage());
            }finally {
//                必须手动解锁
                lock.unlock();
            }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值