多线程使用同步代码块的方式实现两个窗口卖票(不重复)
使用两个线程实现两个窗口卖票,不能卖出重复的票,两个线程使用同一个数据
结果图
1.先建立一个共享的数据存储,这里用ArrayList来实现
package com.leiyustudy.thread.safe;
import java.util.ArrayList;
/** * * */
public class TicketService {
private ArrayList<String> all;
public TicketService() {
all =new ArrayList<>();
all.add("01车01A");
all.add("01车01B");
all.add("01车01C");
all.add("01车01D");
all.add("01车01F");
all.add("02车02A");
all.add("02车02B");
all.add("02车02C");
all.add("02车02D");
all.add("02车02F");
}
//查询是否还有票
public boolean hasTicket() {
return all.size()>0;
}
//卖票
public String sale() {
return all.remove(0);
}
/** * @param args */
}
2.创建一个类来继承Thread类实现多线程卖票
/** * */
package com.leiyustudy.thread.safe;
/** * @author 雷雨 *同时两个人卖票 *两个线程使用了共享的同一数据,就可能有线程安全问题. *如何判断 *是否有多个线程共享一个数据 *(2)是否有多个线程共同操作和访问同一个数据 * *如何解决? 加锁 *同步: *(1)同步的锁对象可以是任意类型的对象(意思就是不限制它的类型,只有共享的人都默认承认这个锁的存在就可以 *(2)使用共享数据的多个线程承认这个锁 * *两种形式; *(1)同步代码块 *语法格式: *synchronized(需要同步的锁对象){ * 需要锁着的代码:一个线程在运行代码期间不想别的线程加入进来, *} * *(2)同步方法 */
public class TestTicket {
/** * @param args */
public static void main(String[] args) {
// TODO 自动生成的方法存根
Ticketsaler t1 = new Ticketsaler("窗口一");
Ticketsaler t2 = new Ticketsaler("窗口二");
t1.start();
t2.start();
}
}
class Ticketsaler extends Thread{
private static TicketService ts = new TicketService();
public Ticketsaler (String name) {
super(name);
}
public void run() {
//这里锁的的ts对象时两个线程公用的对象,因为他是static的对象,但是同时也导致了锁着的ts对象(票库中的票全是窗口一线程卖出的)
while(true) {
synchronized (ts) {
if(ts.hasTicket()) {
try {
Thread.sleep(1000);
//这里用synchronized来锁,在sleep的过程中不释放锁对象
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println(getName()+"卖出了"+ts.sale());
}
else {
break;
}
}
}
}
}
3.创建一个类来实现Runnable接口来实现多线程卖票
/** * */
package com.leiyustudy.thread.safe;
import org.junit.runner.notification.RunListener.ThreadSafe;
/** * @author 雷雨 * * *继承Thread和实现Runnable的区别: *共享对象 *Thread继承来做的话必须用静态的对象 *而Runnable来做只需要得到共同的Runnable的对象,而不一定要静态对象,静态对象的生命周期很长,不建议大量使用 *(2)选择锁对象时比较方便 *因为在Runnable的run方法中的对象就是Runnable的同一个对象,所以可以用this来锁对象 *而继承Thread的方法就不能用this来锁 *(3)继承有单继承限制,实现没有限制 *在java程序开发中尽量面向接口编程 * */
public class TestTicketRunnable {
public static void main(String[] args) {
MyRunnable r1 = new MyRunnable();
Thread t1 = new Thread(r1,"窗口一");
Thread t2 = new Thread(r1,"窗口二");
t1.start();
t2.start();
}
}
class MyRunnable implements Runnable{
private TicketService ts = new TicketService();
@Override
public void run() {
while(true) {
synchronized (ts) {
if(ts.hasTicket()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖出了"+ts.sale());
}
else {
break;
}
}
}
}
}
继承Thread类和实现Runnable接口来实现多线程的方式不同,对于实现Runnable接口的方式来说,对于选择锁的对象比较简单,但是线程的启动得依靠Thread对象来启动.总的来说,java尽量面向接口编程.