前言
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)
公平锁,阻塞时间越长,优先执行,注意:性能慢