前言

Java 同步线程的方法有三种,分别是使用java.util.concurrent 框架,synchronized 关键字,volatile 关键字,直接使用java.util.concurrent 框架并不常用,但是我们在理解java.util.concurrent 框架的锁与条件锁后,对理想synchronized 就更加方便。

线程框架介绍

java.util.concurrent 框架是一个线程同步框架,它包含Lock, ReentrantLock, Condition
等出来线程的工具类,通过该工具列可以实现线程的同步与条件同步,我们写一个买票的例子来说明这些类的使用。

买票代码示例

假设我们要买火车票回家,我们为火车站写一个买票代码,我们为车站提供了一个出票方法与查看剩余票数的方法,代码如下:

public class Dome03 
{
    /** 火车票*/
    private int ticketNumble ;

    public Dome03(int inifTicketNumble)
    {
        ticketNumble = inifTicketNumble;
    }
    /** 查看车票数量
     * @return
     */
    public int queryTicketNumble()
    {
        return ticketNumble;
    }

    /**
     * 出票
     */
    public void goTicket()
    {

        if(ticketNumble > 0)
        {           
            threadSleep(1000);// 出票延迟 <- 
            --ticketNumble;
            System.out.println(Thread.currentThread().getName() + "窗口出票了,当前剩余票数是:" + queryTicketNumble() );
        }
    }

    /**
     * 延迟
     */
    private void threadSleep(long time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

我们来编写一个两个窗口买票的场景实例,假设有两个窗口买票,每个窗口都排队买票100人,两个窗口并发的进行买票。代码如下:

/**
     * 出现了负数
     */
    public void sceneOne()
    {
        // 买票窗口01
        Thread ticketWindow01 = new Thread(new Runnable() {

            @Override
            public void run() {
                for(int lineUpPeople = 100; lineUpPeople >0; lineUpPeople--)
                    goTicket();
            }
        }, "ticketWindow01");
        // 买票窗口02
        Thread ticketWindow02 = new Thread(new Runnable() {

            @Override
            public void run() {
                for(int lineUpPeople = 100; lineUpPeople >0; lineUpPeople--)
                    goTicket();
            }
        }, "ticketWindow02");

        ticketWindow01.start();
        ticketWindow02.start();
    }
    public static void main(String[] args) {
        int ticketNumble = 100;
        new Dome03(ticketNumble).sceneOne();

    }

运行有如下结果:
正常情况:
ticketWindow01窗口出票了,当前剩余票数是:3
ticketWindow02窗口出票了,当前剩余票数是:2
异常情况:
ticketWindow02窗口出票了,当前剩余票数是:1
ticketWindow01窗口出票了,当前剩余票数是:1

线程框架使用

/** 车票锁*/
    private Lock ticketLock = new ReentrantLock();
/**
     * 出票
     */
    public void goTicket()
    {
        ticketLock.lock();
        try {
            if(ticketNumble > 0)
            {           
                threadSleep(1000);// 出票延迟 <- ticketWindow01 与 ticketWindow02 
                --ticketNumble;
                System.out.println(Thread.currentThread().getName() + "窗口出票了,当前剩余票数是:" + queryTicketNumble() );
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            ticketLock.unlock();
            threadSleep(100);
        }
    }

运行有如下结果:
ticketWindow01窗口出票了,当前剩余票数是:9
ticketWindow02窗口出票了,当前剩余票数是:8
ticketWindow01窗口出票了,当前剩余票数是:7
ticketWindow02窗口出票了,当前剩余票数是:6
ticketWindow01窗口出票了,当前剩余票数是:5
ticketWindow02窗口出票了,当前剩余票数是:4
ticketWindow01窗口出票了,当前剩余票数是:3
ticketWindow02窗口出票了,当前剩余票数是:2
ticketWindow01窗口出票了,当前剩余票数是:1
ticketWindow02窗口出票了,当前剩余票数是:0

相关 API 介绍

void lock()
获得对象锁,如果锁同时被另一个线程拥有,该线程被阻塞。
void unlock()
释放锁
ReentranLock()
可重入锁
ReentranLock(boolean fair)
公平锁,阻塞时间越长,优先执行,注意:性能慢