案例:火车站目前正在出售车票,共有100张票,而它有3个售票窗口售票,
请设计一个程序模拟该火车站售票。
解答:
- 分析:三个窗口共享同一个份代码,同一份资源,但是都是独立执行,可利用实现Runnable接口的方法,但存在线程安全问题,所以主要应该解决线程安全问题
PS:
(1.)实现接口方式的好处:
1.可以避免由于Java单继承带来的局限性。
2.适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离
(2.)产生线程安全的因素
在多线程环境下,至少有两条以上的原子性语句操作了共享数据,并且这个操作是写操作,肯定会出现线程安全问题
下面利用三种方式解决这一问题:
- 同步代码块:
格式:
synchronized(锁对象){需要同步的代码;}
- 注意:
a.锁对象是任意对象
b.不同线程共享同一把锁
c.如果方法是静态方法,锁对象是字节码文件对象 - 同步的好处
解决了多线程的安全问题。 - 同步的弊端
当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,
降低程序的运行效率。如果出现了同步嵌套,就容易产生死锁问题
public class Test4 {
public static void main(String[] args) {
SaleTicketsThread st = new SaleTicketsThread();
Thread stt = new Thread(st,"窗口一");
Thread stt2 = new Thread(st,"窗口二");
Thread stt3 = new Thread(st,"窗口三");
stt.start();
stt2.start();
stt3.start();
}
}
class SaleTicketsThread implements Runnable{
public int tickets = 100;
@Override
public void run() {
while (true) {
synchronized (MyLock.LOCK) {
if (tickets>0) {
try {
Thread.sleep(50);//设置线程休眠时间
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在售第"+tickets--+"张票");
Thread.yield();
}
}
}
}
//自定义锁对象
class MyLock {
public static final Object LOCK = new Object();
}
2.同步方法
格式:
public synchronized 返回值 方法名(参数列表) {
//需要同步的代码块
}
注意:
a.同步方法的锁对象是 this
public class Test4 {
public static void main(String[] args) {
SaleTicketsThread st = new SaleTicketsThread();
Thread stt = new Thread(st,"窗口一");
Thread stt2 = new Thread(st,"窗口二");
Thread stt3 = new Thread(st,"窗口三");
stt.start();
stt2.start();
stt3.start();
}
}
class SaleTicketsThread implements Runnable{
public int tickets = 100;
@Override
public void run() {
while (true) {
sellTicket();
}
}
public synchronized void sellTicket() {
if (tickets>0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在售第"+tickets--+"张票");
}
}
}
3.Lock锁 (系统提供)
public class Test4 {
public static void main(String[] args) {
SaleTicketsThread st = new SaleTicketsThread();
Thread stt = new Thread(st,"窗口一");
Thread stt2 = new Thread(st,"窗口二");
Thread stt3 = new Thread(st,"窗口三");
stt.start();
stt2.start();
stt3.start();
}
}
class SaleTicketsThread implements Runnable{
public int tickets = 100;
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets --) + "张票");
}
lock.unlock();
}
}