为什么需要线程锁
假设现在有三个线程,同时去销售100张车票,我们知道一张车票只能销售一次,如果不使用线程锁就可能出现一张车票同时被三个线程销售三次的情况。
接下来用程序演示一下
package com.test.lock;
public class SellTicket3 implements Runnable {
private static int tickets=100;
public void run()
{
while(true)
{
//隐士锁 内置锁 方式1 锁住代码块
if(tickets>0)
{
System.out.println(Thread.currentThread().getName()+"售出第..."+tickets+"...张票");
tickets--;
try {
Thread.sleep(10); //休眠 10毫秒
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
package com.test.lock;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test2 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建线程池对象
ExecutorService pool= Executors.newFixedThreadPool(3);
//创建售票任务 对象
SellTicket3 sellTicket=new SellTicket3();
pool.submit(sellTicket);
pool.submit(sellTicket);
pool.submit(sellTicket);
}
}
观察控制台的输入,发现第100张票被售出了三次,这是有问题的。
使用线程锁解决这个问题
创建线程锁有两种方式,隐式锁和显式锁。
隐式锁
package com.test.lock;
public class SellTicket implements Runnable {
private static int tickets=100;
public void run()
{
while(true)
{
//隐士锁 内置锁 方式1 锁住代码块
synchronized (this) {
if(tickets>0)
{
System.out.println(Thread.currentThread().getName()+"售出第..."+tickets+"...张票");
tickets--;
try {
Thread.sleep(10); //休眠 10毫秒
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
package com.test.lock;
public class SellTicket implements Runnable {
private static int tickets=100;
public void run()
{
while(true)
{
//方式 2
sellTicket();
}
}
//方式2 锁住方法(成员方法或静态方法)
public synchronized void sellTicket()
{
if(tickets>0)
{
System.out.println(Thread.currentThread().getName()+"售出第..."+tickets+"...张票");
tickets--;
try {
Thread.sleep(10); //休眠 10毫秒
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
显示锁
package com.test.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SellTicket2 implements Runnable {
private static int tickets=100;
//显示锁
private Lock lock=new ReentrantLock();
@Override
public void run() {
// TODO Auto-generated method stub
while(true)
{
lock.lock(); //上锁
//lock.tryLock(arg0, arg1)//设置等待的时间的
if(tickets>0)
{
System.out.println(Thread.currentThread().getName()+"售出第.."+tickets+"..张票");
tickets--;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//开锁
lock.unlock();
}
}
}
运行测试
package com.test.lock;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建线程池对象
ExecutorService pool= Executors.newFixedThreadPool(3);
//创建售票任务 对象
SellTicket2 sellTicket=new SellTicket2();
pool.submit(sellTicket);
pool.submit(sellTicket);
pool.submit(sellTicket);
}
}
没有出现,同一张票被多次销售的情况
如何选择隐式锁和显式锁
利用显式锁可以设置更多的参数,比如等待时间。
如果使用的是隐式锁的话,假如线程中的代码出现异常,会导致死锁,这时候使用显式锁设置等待时间,可以避免死锁的情况。