商品秒杀
假如某网上商城推出活动新上架10部新手机免费送客户体验,要求所有参与活动的人员在规定的时间同时参与秒杀挣抢,假如有30人同时参与了该活动,请使用线程池模拟这个场景,保证前10人秒杀成功,后20人秒杀失败;
要求:
- 使用线程池创建线程
- 解决线程安全问题
1:既然商品总数量是10个,那么我们可以在创建线程池的时候初始化线程数是10个及以下,设计线程池最大数量为10个;
2:当某个线程执行完任务之后,可以让其他秒杀的人继续使用该线程参与秒杀;
3:使用synchronized控制线程安全,防止出现错误数据;
- 任务类:
包含了商品数量,客户名称,秒杀手机的行为
public class MyTask implements Runnable{
private static int id =10;
private String username;
public MyTask(String username) {
this.username = username;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(username+"正在使用"+name+"线程,参与华为P30秒杀活动");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (MyTask.class){
if (id>0){
System.out.println(username+"使用"+name+"线程,成功秒杀华为P30!!");
id--;
if (id>0){
System.out.println("商品还剩下"+id+"个");
}else {
System.out.println("商品已售完");
}
}else{
System.out.println(username+"秒杀失败啦!");
}
}
}
}
- 主程序类,测试任务类
public class MyTest {
public static void main(String[] args) {
//创建一个线程池对象(3个核心,最大线程5个,时间1,单位分钟,任务队列长度15)
ThreadPoolExecutor pool=new ThreadPoolExecutor(5,10, 1, TimeUnit.MINUTES,new LinkedBlockingQueue<>(20));
//循环创建任务对象
for (int i=1;i<=30;i++) {
MyTask myTask = new MyTask("客户"+i);
pool.submit(myTask);
}
//关闭线程池
pool.shutdown();
}
}
取款业务
设计一个程序,使用两个线程模拟在两个地点同时从一个账号中取钱,假如卡中一共有1000元,每个线程取800元,要求演示结果一个线程取款成功,剩余200元,另一个线程取款失败,余额不足;
要求:
1. 使用线程池创建线程
2. 解决线程安全问题
提示:
1. 线程池可以利用Executors工厂类的静态方法,创建线程池对象;
2. 解决线程安全问题可以使用synchronized方法控制取钱的操作
3. 在取款前,先判断余额是否足够,且保证余额判断和取钱行为的原子性;
- 任务类
public class MyTask implements Runnable{
//用户姓名
private String username;
//取款金额
private double money;
//总金额
private static double total =1000;
public MyTask(String username, double money) {
this.username = username;
this.money = money;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(username+"准备取款"+money+"元");
synchronized (MyTask.class){
if ((total-money>0)) {
System.out.println(username + "使用" + name + "线程,取款:" + money + "元");
total=total-money;
}else {
System.out.println(username + "使用" + name + "线程,取款:" + money + "元失败!!!");
}
}
}
}
- 主程序类,测试任务类
public class MyTest {
public static void main(String[] args) {
//创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2, new ThreadFactory() {
int id =1;
@Override
public Thread newThread(Runnable r) {
return new Thread(r,"ATM"+id++);
}
});
//创建两个任务提交
for(int i=1;i<=2;i++){
MyTask myTask =new MyTask("客户"+i, 600);
pool.submit(myTask);
}
//关闭线程池
pool.shutdown();
}
}
总结
线程池的使用步骤可以归纳总结为五步:
1:利用Executors工厂类的静态方法,创建线程池对象;
2:编写Runnable或Callable实现类的实例对象;
3:利用ExecutorService的submit方法或ScheduledExecutorService的schedule方
法提交并执行线程任务
4:如果有执行结果,则处理异步执行结果(Future)
5:调用shutdown()方法,关闭线程池