卖票服务
引入问题:
有一百张票,分为三个从窗口
测试类
public static void main(String[] args) {
RunnableImpl run=new RunnableImpl();
Thread t0=new Thread(run);
Thread t1=new Thread(run);
Thread t2=new Thread(run);
//调用start,开启多线程
t0.start();
t1.start();
t2.start();
}
第一次代码:
只要有票就售出,三个窗口为三个线程,同时执行
public class RunnableImpl implements Runnable {
private int ticket=100;
@Override
public void run() {
while (true)
{
if(ticket>0)
{
System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票!");
ticket--;
}
}
}
}
效果:
Thread-2正在卖第100张票!
Thread-2正在卖第99张票!
Thread-2正在卖第98张票!
Thread-1正在卖第100张票!
Thread-1正在卖第96张票!
Thread-2正在卖第97张票!
Thread-2正在卖第94张票!
Thread-2正在卖第93张票!
Thread-0正在卖第100张票!
原因:线程争夺资源,同时进入cpu中,系统没有调休实现,所以出现票重复 卖出的情况。
改进算法:
卖票的时间每个改为10ms,意味着线程休眠10ms。使得资源得以缓冲
static void sleep(long millis)
使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。
public class RunnableImpl implements Runnable {
private int ticket=100;
@Override
public void run() {
while (true)
{
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(ticket>0)
{
System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票!");
ticket--;
}
}
}
}
效果:
Thread-0正在卖第100张票!
Thread-1正在卖第100张票!
Thread-2正在卖第100张票!
Thread-0正在卖第97张票!
Thread-1正在卖第97张票!
Thread-2正在卖第95张票!
原因:还是因为争夺cpu,同时进入,改进并没有找到根本原因!
使用synchronized(锁对象)同步代码块
synchronized(锁对象)
{可能会出现线程安全问题的代码(访问了共享数据的代码)}
注意:
-
1.通过代码块中的锁对象,可以使用任意的对象
-
2.但是必须保证多个线程使用的锁对象是同一个
-
3.锁对象作用:
-
把同步代码块锁住,只让一个线程在同步代码块中执行
public class RunnableImpl implements Runnable { private int ticket=100; //创建一个锁对象 Object obj=new Object(); @Override public void run() { while (true) {//同步代码块 synchronized (obj)//也可以synchronized(this) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } if(ticket>0) { System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票!"); ticket--; } } } } }
效果:
Thread-0正在卖第100张票!
Thread-0正在卖第99张票!
Thread-0正在卖第98张票!
Thread-0正在卖第97张票!
Thread-0正在卖第96张票!
Thread-0正在卖第95张票!
Thread-0正在卖第94张票!
Thread-0正在卖第93张票!
原理:
使用同步方法
锁对象是this
使用步骤:
1.把访问了共享数据代码抽取出来,放入到一个方法中
2.在方法上添加synchronized
代码:
public class RunnableImpl implements Runnable {
private int ticket=100;
//创建一个锁对象
@Override
public void run() {
while (true)
{//同步代码块
payTicket();
}
}
public synchronized void payTicket()
{
if(ticket>0)
{
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票!");
ticket--;
}
}
}
效果:
Thread-0正在卖第100张票!
Thread-0正在卖第99张票!
Thread-0正在卖第98张票!
Thread-0正在卖第97张票!
Thread-0正在卖第96张票!
Thread-0正在卖第95张票!
Thread-0正在卖第94张票!
Thread-0正在卖第93张票!
设置票为私有静态,则同步方法也是静态方法
静态的同步方法
锁对象是静态方法的锁对象是本类的class属性–>class文件对象(反射)
代码:
public class RunnableImpl implements Runnable {
private static int ticket=100;
//创建一个锁对象
@Override
public void run() {
while (true)
{//同步代码块
payTicket();
}
}
/*使用静态方法解决同步问题*/
public static synchronized void payTicket()
{
//去掉synchronized 关键字换上
//synchronized(RunnableImpl.class){}
if(ticket>0)
{
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票!");
ticket--;
}
}
}