多线程练习题,来自《疯狂java》

题目

假设又三个车位(可以用boolean[]数组来表示车库)可以停车,写一个程序模拟多个用户开车离开,停车入库的效果。注意车位有车的时候不能停车。

分析

开始时,车位为空,只准进行倒车入库操作(一共有三个车位,所以最多同时有三辆车进行倒库),超过三辆车以后,不允许车进入;同时,当某个车位有车的时候,新的车辆不能进入该车位;汽车出库完成,允许新的车进入其中。

用户(User)提供车位信息,倒车入库方法和出库离开方法,检查剩余车位并且返回空余车位的位置

代码

第一版

/**
 # 题目
 假设又三个车位(可以用boolean[]数组来表示车库)可以停车,
 写一个程序模拟多个用户开车离开,停车入库的效果。注意车位有车的时候不能停车。
 */

/**
 * 用户类,封装车位信息,提供入库和出库方法
 */
class User{

    private boolean[] parking ={false,false,false};

    public synchronized int findSpareParking(){

        for(int i=0;i<parking.length; i++){
            if(!parking[i]){
                return i;
            }
        }
        return -1;
    }

    /**
     * 倒车入库,出库离开
     */
    public synchronized void move(){
        int p;
        if((p=findSpareParking())>=0){

            //停车进入
            parking[p]=true;
            System.out.println(Thread.currentThread().getName()+"在车位"+p+"倒车入库");
            //模拟实际汽车在车位停留一定思维时间
            //唤醒其他线程
            notifyAll();
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //出库
            parking[p] = false;
            System.out.println(Thread.currentThread().getName() +"在车位"+p+ "出库离开");
        }
    }

}
class CarThread extends Thread {

    private String name;
    private User user;

    public CarThread(String name, User user) {
        super(name);
        this.user = user;

    }

    @Override
    public void run() {
        user.move();
    }
}
public class Problem2 {

    public static void main(String[] args) {

        User user = new User();
        //创建多个线程和用户
        for (int i=0;i<12;i++){
            new CarThread("car"+i,user).start();
        }

    }
}

问题

car0在车位0倒车入库
car0在车位0出库离开
car11在车位0倒车入库
car11在车位0出库离开
car10在车位0倒车入库

只有一个车位被启用,分析原因是因为move方法里面虽然有休眠和notifyAll()方法,但是程序实际上还是顺序执行

第二版

每位用户创建两个线程:倒车入库线程和出库离开线程;但是这个时候出现了新的问题,怎样知道某辆汽车停在哪个车位上呢?只有认识到这一点,才能完成汽车出库的操作。因此需要额外定义一个数组cars,同来表示汽车入库以后的车位信息。汽车在p位置入库,parking[p]=true,cars[p]=“car_name”;出库的时候,首先需要确保当前线程所代表的汽车的确是进入到车位当中,假设其位置是p,出库操作为parking[p]=false,cars[p]=“null”

/**
 * # 题目
 * 假设又三个车位(可以用boolean[]数组来表示车库)可以停车,
 * 写一个程序模拟多个用户开车离开,停车入库的效果。注意车位有车的时候不能停车。
 */

/**
 * 用户类,封装车位信息,提供入库和出库方法
 */
class User {

    private boolean[] parking = {false, false, false};
    private String[] cars = new String[3];

    public synchronized int findSpareParking() {

        for (int i = 0; i < parking.length; i++) {
            if (!parking[i]) {
                return i;
            }
        }
        return -1;
    }

    public synchronized int findParkingWithCar() {
        for (int i = 0; i < parking.length; i++) {
            if (parking[i] && cars[i] != null && cars[i].equals(Thread.currentThread().getName())) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 倒车入库
     */
    public synchronized void move() {
        int p;
        String name = Thread.currentThread().getName();
        
        if((p= findSpareParking())>=0){
        	//停车进入
            parking[p] = true;
            cars[p] = name;
            System.out.println(name + "在车位" + p + "倒车入库");
            //模拟实际汽车在车位停留一定思维时间
            //唤醒其他线程
            notifyAll();
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 出库离开
     */
    public synchronized void leave() {

        //出库离开
        if((p = findParkingWithCar())>=0){
        	parking[p] = false;
            cars[p] = null;
            System.out.println(Thread.currentThread().getName() + "在车位" + p + "出库离开");
            notifyAll();
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class MoveThread extends Thread {

    private String name;
    private User user;

    public MoveThread(String name, User user) {
        super(name);
        this.user = user;

    }

    @Override
    public void run() {
        user.move();
    }
}

class LeaveThread extends Thread {

    private String name;
    private User user;

    public LeaveThread(String name, User user) {
        super(name);
        this.user = user;

    }

    @Override
    public void run() {
        user.leave();
    }
}

public class Problem2 {

    public static void main(String[] args) {

        User user = new User();
        //创建多个线程和用户
        for (int i = 0; i < 12; i++) {
            new MoveThread("car" + i, user).start();
            new LeaveThread("car" + i, user).start();
        }
    }
}

但是实际运行的结果发现每次只有少量的几次记录,如下所示

car0在车位0倒车入库
car11在车位1倒车入库
car10在车位2倒车入库
car0在车位0出库离开

问题

明明在main方法里面创建了24个线程,属于12辆汽车(包括入库和出库),为什么最终每次只有4个线程运行?

因为在main方法里面的确创建了24个线程,但是其中很多线程在执行run方法时,当不能满足里面的if判断的时候,线程将直接正常结束,这是我们不希望看见的,我们应该让其阻塞,直到满足if判断条件,因此在每个同步方法前面应该加入while判断,如果不满足,阻塞线程。

最终版

/**
 * # 题目
 * 假设又三个车位(可以用boolean[]数组来表示车库)可以停车,
 * 写一个程序模拟多个用户开车离开,停车入库的效果。注意车位有车的时候不能停车。
 */

/**
 * 用户类,封装车位信息,提供入库和出库方法
 */
class User {

    private boolean[] parking = {false, false, false};//车位信息
    private String[] cars = new String[3];//表示每个车位所停的汽车的名称,没有停车时为null

    /**
     * 从左往右找到空闲的车位
     * @return
     */
    public synchronized int findSpareParking() {

        for (int i = 0; i < parking.length; i++) {
            if (!parking[i]) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 出库操作判断,找到能够出库的汽车
     * @return
     */
    public synchronized int findParkingWithCar() {
        for (int i = 0; i < parking.length; i++) {
            //确保当前线程所代表的汽车的确是进入到车位当中
            if (parking[i] && cars[i] != null && cars[i].equals(Thread.currentThread().getName())) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 倒车入库
     */
    public synchronized void move() {
        int p;
        String name = Thread.currentThread().getName();
        //不满足倒车入库条件阻塞当前线程
        while (findSpareParking() < 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        p= findSpareParking();
        //停车进入
        parking[p] = true;
        cars[p] = name;
        System.out.println(name + "在车位" + p + "倒车入库");
        //模拟实际汽车在车位停留一定思维时间
        //唤醒其他线程
        notifyAll();
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 出库离开
     */
    public synchronized void leave() {

        int p;
        //不满足出库离开条件,阻塞线程
        while (findParkingWithCar() < 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //出库离开
        p = findParkingWithCar();
        parking[p] = false;
        cars[p] = null;
        System.out.println(Thread.currentThread().getName() + "在车位" + p + "出库离开");
        notifyAll();
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class MoveThread extends Thread {

    private String name;
    private User user;

    public MoveThread(String name, User user) {
        super(name);
        this.user = user;

    }

    @Override
    public void run() {
        user.move();
    }
}

class LeaveThread extends Thread {

    private String name;
    private User user;

    public LeaveThread(String name, User user) {
        super(name);
        this.user = user;

    }

    @Override
    public void run() {
        user.leave();
    }
}

public class Problem2 {

    public static void main(String[] args) {

        User user = new User();
        //创建多个线程和用户
        for (int i = 0; i < 12; i++) {
            new MoveThread("car" + i, user).start();
            new LeaveThread("car" + i, user).start();
        }

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值