说明
当程序中出现多个进程对同一资源进行操作时,因为对数据的操作非常密集,可能会对资源过度操作,这时就需要用到线程的同步技术。
以一个抢红包程序为例,红包数量为3个,开启5个线程来模拟抢红包行为,红包数量抢完了,就不能再抢了。代码如下:
/**
 * 抢红包
 */
class GetBonus implements Runnable {
    /**
     * 创建一个红包数量
     */
    private static int bonusQuantity = 3;
    @Override
    public void run() {
        // 如果红包数量大于0
        if (bonusQuantity > 0) {
            System.out.println("恭喜" + Thread.currentThread().getName() + "您成功抢到一个20元的红包");
            // 红包数量-1
            bonusQuantity--;
        } else {
            System.out.println(Thread.currentThread().getName() + "抱歉,红包已经被抢完了");
        }
        System.out.println(bonusQuantity);
    }
}
public class EssayTest {
    private static void main(String[] args) {
        // 创建红包线程
        GetBonus getBonus = new GetBonus();
        // 创建5个Thread线程并命名、启动
         new Thread(getBonus, "黄蓉").start();
         new Thread(getBonus, "郭靖").start();
         new Thread(getBonus, "杨过").start();
         new Thread(getBonus, "小龙女").start();
         new Thread(getBonus, "张无忌").start();
    }
}

线程同步方式
为了解决上面对线程对统一资源操作不同步的问题,有以下几种方式
一、syschronized代码块
syschronized是java的关键字,被syschronized代码块包含的程序,同时只能有一个线程在执行。可以将程序中对资源操作的核心代码包起来,实现线程同步。修改后的代码如下:
/**
 * 抢红包
 */
class GetBonus implements Runnable {
    /**
     * 创建一个红包数量
     */
    private static int bonusQuantity = 3;
    @Override
    public void run() {
        // 用synchronized代码块将核心代码包含起来,()内的必须是一个对象且唯一,通常用this,即GetBonus类的地址,在内存中是唯一的。
        synchronized (this) {
            // 如果红包数量大于0
            if (bonusQuantity > 0) {
                System.out.println("恭喜" + Thread.currentThread().getName() + "您成功抢到一个20元的红包");
                // 红包数量-1
                bonusQuantity--;
            } else {
                System.out.println(Thread.currentThread().getName() + "抱歉,红包已经被抢完了");
            }
            System.out.println("红包剩余:" + bonusQuantity);
            System.out.println("---------------------------------");
        }
    }
}

二、锁方法
即将方法用syschronized修饰,表明该方法是一个同步方法,同时只能有一个线程在执行,修改后的代码如下:
/**
 * 抢红包
 */
class GetBonus implements Runnable {
    /**
     * 创建一个红包数量
     */
    private static int bonusQuantity = 3;
    @Override
    public void run() {
        get();
    }
    /**
     * 将抢红包的代码抽取成一个方法,并用synchronized修饰
     */
    private synchronized void get() {
        // 如果红包数量大于0
        if (bonusQuantity > 0) {
            System.out.println("恭喜" + Thread.currentThread().getName() + "您成功抢到一个20元的红包");
            // 红包数量-1
            bonusQuantity--;
        } else {
            System.out.println(Thread.currentThread().getName() + "抱歉,红包已经被抢完了");
        }
        System.out.println("红包剩余:" + bonusQuantity);
        System.out.println("---------------------------------");
    }
}

 值得思考的是,这里可不可以将run()方法用synchronized修饰,设置成一个同步方法,一步到位呢?
三、ReentrantLock
使用java的ReentrantLock类,自定义设置上锁和解锁的代码区间,修改后的代码如下:
import java.util.concurrent.locks.ReentrantLock;
/**
 * 抢红包
 */
class GetBonus implements Runnable {
    /**
     * 创建一个红包数量
     */
    private static int bonusQuantity = 3;
    /**
     * 创建一个锁对象
     */
    ReentrantLock lock = new ReentrantLock();
    
    @Override
    public void run() {
        // 加锁
        lock.lock();
        // 如果红包数量大于0
        if (bonusQuantity > 0) {
            System.out.println("恭喜" + Thread.currentThread().getName() + "您成功抢到一个20元的红包");
            // 红包数量-1
            bonusQuantity--;
        } else {
            System.out.println(Thread.currentThread().getName() + "抱歉,红包已经被抢完了");
        }
        // 解锁
        lock.unlock();
        System.out.println("红包剩余:" + bonusQuantity);
        System.out.println("---------------------------------");
    }
}

 值得一提的是,阿里巴巴代码规范提示,lock.lock()必须紧跟try代码块,且unlock要放到finally第一行。应该是考虑到代码被上锁后,解锁的lock.unlock()代码有可能一直没被执行的情况。
总结
如果涉及到多线程对同一资源的操作,为了避免出现资源溢出的情况,可以用synchronized代码块、synchronized方法和ReentrantLock类对象上锁、解锁等方法,在实际使用时,还应该考虑到,被锁住的代码越多,程序执行效率越低,所以应该尽量只锁住“核心代码”。
![[MAUI]在.NET MAUI中复刻苹果Cover Flow](https://img-blog.csdnimg.cn/427db839c6b7434aa54ecba60c5325f8.gif)
















![[论文分享] Function Representations for Binary Similarity](https://img-blog.csdnimg.cn/845aff2ff3ca4295a9e5d4a83bd630cc.png)

