java多线程练习题

java多线程练习题

1. 车站售票

要求: 代码实现火车站3个窗口同时卖25张车票的逻辑,同一个窗口不能卖同一张票

//多线程售票例子
public class TicketDemo {
​
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
   //窗口1
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
               ticket.sales();
            }
        },"窗口1").start();
 //窗口2
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                ticket.sales();
            }
        },"窗口2").start();
 //窗口3
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                ticket.sales();
            }
        },"窗口3").start();
    }
​
}
​
/**
*真正的多线程开发记住一句话“线程就是一个单独的资源类,没有任何附属的操作,只有属性和方法”
*/
class  Ticket{
    private int ticketNumber = 25;
​
    public synchronized void sales ()
    {
        if(ticketNumber > 0)
        {
            System.out.println(Thread.currentThread().getName()+"售卖票后,还剩"+ (--ticketNumber) +"张票");
        }else
        {
            System.out.println("票已经销售完了");
        }
    }
​
​
}

 

image-20210623162453702

在<<阿里巴巴java开发手册>>中关于并发控制明确提出必须通过线程池创建,不允许自行显式创建线程和不允许线程池使用Excutors去创建,因此以上的代码是不符合规范的.

image-20210623175623124

因此代码修改成创建线程池,然后从线程池中去获取线程,并且同时自定义线程工厂,可以自定义线程的名称\组以及优先级等信息,甚至可以任性的将所有的线程设置为守护线程.总之,使用自定义线程池可以让我们更加自由的设置池子中所有线程的状态.

 public class Demo02 {
     
public static void main(String[] args) {
      
        Ticket ticket = new Ticket();
​
        //创建线程池
        ThreadPoolExecutor TicketThreadPool = new ThreadPoolExecutor(
                3, 5, 3, TimeUnit.SECONDS,
                new LinkedBlockingDeque<Runnable>(3),
                new NamedThreadFactory("售票ThreadPool"),//默认使用 Executors.defaultThreadFactory()
                new ThreadPoolExecutor.AbortPolicy());
​
        //创建3个线程,每个线程卖10次票
        for (int i = 0; i < 3; i++) {
​
            TicketThreadPool.execute(() -> {
                for (int i1 = 0; i1 < 10; i1++) {
                    ticket.sales();
                }
            });
        }
        //关闭线程池
        TicketThreadPool.shutdown();
    }
}
​
/**
 * 自定义线程工厂
 */
class NamedThreadFactory implements ThreadFactory{
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;
​
    NamedThreadFactory(String name) {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
        if (null == name || name.isEmpty()) {
            name = "pool";
        }
        namePrefix = name + "-窗口-";
    }
    @Override
    public Thread newThread(Runnable r) {
        Thread thread = new Thread(r, namePrefix + threadNumber.getAndIncrement());
        return thread;
    }
​
}
​
/**
 * 资源类
 */
 class  Ticket  {
    private int ticketNumber = 25;
​
    public synchronized void sales ()
    {
        if(ticketNumber > 0)
        {
            System.out.println(Thread.currentThread().getName()+"售卖票后,还剩"+ (--ticketNumber) +"张票");
        }else
        {
            System.out.println("票已经销售完了");
        }
    }
}

image-20210623182912599

2. 银行存取款

要求:

小明打算去提款机上取钱,发现卡上没有钱,这时候他告知妈妈去存钱,妈妈存了钱了,告知小明存好了可以取钱了。

  • 小明分多次取钱,每次取100,当发现钱不够100,就等妈妈存钱

  • 妈妈每次存钱2000,当发现钱小于100就存钱,并且通知小明取取钱,当大于100就等小明钱不够再存

public class MoneyDemo {
/**
 *要求:
 * (线程通信)小明打算去提款机上取钱,发现卡上没有钱,这时候他告知妈妈去存钱,妈妈存了钱了,告知小明存好了可以取钱了。
 * 小明分多次取钱,每次取100,当发现钱不够100,就等妈妈存钱
 * 妈妈每次存钱2000,当发现钱小于100就存钱,并且通知小明取取钱,当大于100就等小明钱不够再存
 */
    public static void main(String[] args) {
        Money money = new Money();
        //小明取钱
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                money.drawMoney();
            }
            },"小明").start();
​
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                money.storeMoney();
            }
        },"妈妈").start();
    }
}
/*
*资源类
*/
class Money{
​
    private volatile int money = 2000;
​
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
​
    /**
     * 妈妈存钱
     */
    public void storeMoney()
    {
        lock.lock();
        try {
            while (money > 0)
            {
                condition.await();
            }
            this.money = 2000;
            System.out.println("小兔崽子节约点用,老妈又给你打了"+ this.money + "块钱过来");
            condition.signalAll();
​
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
​
    /**
     * 小明取钱
     */
    public void drawMoney()
    {
        lock.lock();
        try {
            while(money > 0)
            {
                money = money - 100;
                System.out.println("小明取款后还剩" + money + "元");
                condition.signalAll();
            }
            condition.await();
        }
          catch (Exception e) {
                e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
​
​
}

image-20210623185330412

这里使用过来JUC里面的Lock锁和Condition来实现线程间的互相通信

3. (线程同步)设计四个线程对象对同一个数据进行操作,两个线程执行减操作,两个线程执行加操作。

public class IncreaseNumber {
​
    public static void main(String[] args) {
        Number number = new Number();
//线程A 做+1操作
        new Thread(()->{
            number.IncrementNumber();
        },"ThreadA").start();
//线程B 做+1操作
        new Thread(()->{
            number.IncrementNumber();
        },"ThreadB").start();
//线程C 做-1操作
        new Thread(()->{
            number.DecrementNumber();
        },"ThreadC").start();
//线程D 做-1操作
        new Thread(()->{
            number.DecrementNumber();
        },"ThreadD").start();
​
    }
}
​
/**
*资源类
*/
class  Number{
    private int number=10;
    /**
    *加1的操作方法
    */
    public synchronized void  IncrementNumber()
    {
        number++;
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "--> +1,后变成了"+number);
    }
    /**
    *减1的操作方法
    */
    public synchronized void DecrementNumber()
    {
        number--;
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "--> -1,后变成了"+number);
    }
}

image-20210623194827307

未完待续
4. (线程通信)制作两个线程对象,要求用同步块的方式使第一个线程运行2次,然后将自己
阻塞起来,唤醒第二个线程,第二个线程再运行2次,然后将自己阻塞起来,唤醒第一个线
程……两个线程交替执行。


5. (线程同步)设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。

 

6. (线程通信)子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着
再回到主线程又循环100,如此循环50次。

原文链接:https://blog.csdn.net/wenzhi20102321/article/details/52524545

 

posted @ 2021-06-23 18:50  肖恩雷  阅读(1167)  评论(0编辑  收藏  举报