多线程笔试题

1、用5个线程去卖火车票,一共5000张,卖完即止。

用线程池的写法:向线程池提交5000个任务

public class Test {
    public static void main(String[] args) throws InterruptedException {
        int total = 5000;
        int threadSize = 5;
        TicketHolder ticketHolder = new TicketHolder(total);
        ExecutorService executorService =
                Executors.newFixedThreadPool(threadSize, new ThreadFactoryBuilder().setNameFormat("线程%d").build());
        for (int i = 1; i <= total; i++) {
            executorService.submit(ticketHolder::decrease);
        }
        executorService.awaitTermination(3, TimeUnit.SECONDS);
        executorService.shutdown();
    }
}

@Data
@AllArgsConstructor
class TicketHolder {

    private int total;

    public synchronized void decrease() {
        total = total - 1;
        System.out.println(Thread.currentThread().getName() + ",还剩" + total + "张票");
    }

}

以上代码可以优化下,去掉实体类,而是用静态变量在多个线程间传值:

用线程池的写法:向线程池提交5个任务

public class Test {
    public static void main(String[] args) throws InterruptedException {
        int total = 5000;
        int threadSize = 5;
        TicketHolder ticketHolder = new TicketHolder(total);
        ExecutorService executorService =
                Executors.newFixedThreadPool(threadSize, new ThreadFactoryBuilder().setNameFormat("线程%d").build());
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println(1);
                while (true) {
                    synchronized (this) {
                        if (ticketHolder.getTotal() >= 1) {
                            ticketHolder.decrease();
                        } else {
                            break;
                        }
                    }
                }
            }
        };

        for (int i = 1; i <= threadSize; i++) {
            executorService.submit(runnable);
        }
        executorService.awaitTermination(3, TimeUnit.SECONDS);
        executorService.shutdown();
    }
}

@Data
@AllArgsConstructor
class TicketHolder {

    private int total;

    public void decrease() {
        total = total - 1;
        System.out.println(Thread.currentThread().getName() + ",还剩" + total + "张票");
    }

}

不用线程池的写法:

public class Test {
    public static void main(String[] args) {
        int total = 5000;
        TicketHolder ticketHolder = new TicketHolder(total);
        new Thread(new SellTicketThread(ticketHolder), "线程1").start();
        new Thread(new SellTicketThread(ticketHolder), "线程2").start();
        new Thread(new SellTicketThread(ticketHolder), "线程3").start();
        new Thread(new SellTicketThread(ticketHolder), "线程4").start();
        new Thread(new SellTicketThread(ticketHolder), "线程5").start();
    }
}

@Data
@AllArgsConstructor
class TicketHolder {

    private int total;

    public void decrease() {
        if (total >= 1) {
            total = total - 1;
            System.out.println(Thread.currentThread() + ",还剩" + total + "张票");
        }
    }
}

class SellTicketThread implements Runnable {

    private final TicketHolder ticketHolder;

    @Override
    public void run() {
        while (ticketHolder.getTotal() >= 1) {
            synchronized (ticketHolder) {
                ticketHolder.decrease();
            }
        }
    }

    public SellTicketThread(TicketHolder ticketHolder) {
        this.ticketHolder = ticketHolder;
    }
}

2、三个线程,依次打印A、B、C,打印十次,各线程run方法只能执行一次,最后依次输出ABCABCABC...。

用线程池写法:因为各线程run()方法只能执行一次,所以只能往线程池中提交三个任务。如果提交三十个任务的话,run()方法会执行三十次。

public class Test {
    public static void main(String[] args) throws InterruptedException {

        PrintTime printTime = new PrintTime(0);

        ExecutorService executorService =
                Executors.newFixedThreadPool(3, new ThreadFactoryBuilder().setNameFormat("线程%d").build());


        Runnable runnableA = () -> {
            for (int i = 1; i <= 10; i++) {
                synchronized (printTime) {
                    if (printTime.getTime() % 3 == 0) {
                        System.out.print("A");
                        printTime.setTime(printTime.getTime() + 1);
                    } else {
                        i = i - 1;
                    }
                }
            }
        };

        Runnable runnableB = () -> {
            for (int i = 1; i <= 10; i++) {
                synchronized (printTime) {
                    if (printTime.getTime() % 3 == 1) {
                        System.out.print("B");
                        printTime.setTime(printTime.getTime() + 1);
                    } else {
                        i = i - 1;
                    }
                }
            }
        };

        Runnable runnableC = () -> {
            for (int i = 1; i <= 10; i++) {
                synchronized (printTime) {
                    if (printTime.getTime() % 3 == 2) {
                        System.out.print("C");
                        printTime.setTime(printTime.getTime() + 1);
                    } else {
                        i = i - 1;
                    }
                }
            }
        };

        executorService.submit(runnableA);
        executorService.submit(runnableB);
        executorService.submit(runnableC);

        executorService.awaitTermination(3, TimeUnit.SECONDS);
        executorService.shutdown();
    }
}

@Data
@AllArgsConstructor
class PrintTime {

    int time;

}

不用线程池写法:

public class Test {
    public static void main(String[] args) {
        PrintTime object = new PrintTime(0);
        new Thread(new PrintThread(object, "A"), "线程1").start();
        new Thread(new PrintThread(object, "B"), "线程2").start();
        new Thread(new PrintThread(object, "C"), "线程3").start();
    }
}

@Data
@AllArgsConstructor
class PrintTime {
    int time;
}

class PrintThread implements Runnable {

    private final PrintTime printTime;

    private String printStr;

    public PrintThread(PrintTime printTime, String printStr) {
        this.printTime = printTime;
        this.printStr = printStr;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (printTime) {
                if (printTime.getTime() >= 30) {
                    break;
                }
                if (("A".equals(printStr) && printTime.getTime() % 3 == 0)
                        || "B".equals(printStr) && printTime.getTime() % 3 == 1
                        || "C".equals(printStr) && printTime.getTime() % 3 == 2
                        ) {
                    System.out.print(printStr);
                    printTime.setTime(printTime.getTime() + 1);
                }
            }
        }

    }
}

不用synchronized,用Lock写法:

public class Test {
    public static void main(String[] args) {

        Lock lock = new ReentrantLock();

        PrintTime printTime = new PrintTime(0);

        new Thread(new ThreadA(lock, printTime), "线程A").start();
        new Thread(new ThreadB(lock, printTime), "线程B").start();
        new Thread(new ThreadC(lock, printTime), "线程C").start();
    }
}

class ThreadA implements Runnable {

    private Lock lock;

    private PrintTime printTime;

    public ThreadA(Lock lock, PrintTime printTime) {
        this.lock = lock;
        this.printTime = printTime;
    }

    @Override
    public void run() {
        int size = 0;
        while (size < 10) {
            if (lock.tryLock()) {
                try {
                    if (printTime.getTime() % 3 == 0) {
                        size = size + 1;
                        printTime.setTime(printTime.getTime() + 1);
                        System.out.println("A");
                    }
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}

class ThreadB implements Runnable {

    private Lock lock;
    private PrintTime printTime;

    public ThreadB(Lock lock, PrintTime printTime) {
        this.lock = lock;
        this.printTime = printTime;
    }

    @Override
    public void run() {
        int size = 0;
        while (size < 10) {
            if (lock.tryLock()) {
                try {
                    if (printTime.getTime() % 3 == 1) {
                        size = size + 1;
                        printTime.setTime(printTime.getTime() + 1);
                        System.out.println("B");
                    }
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}

class ThreadC implements Runnable {

    private Lock lock;
    private PrintTime printTime;

    public ThreadC(Lock lock, PrintTime printTime) {
        this.lock = lock;
        this.printTime = printTime;
    }

    @Override
    public void run() {
        int size = 0;
        while (size < 10) {
            if (lock.tryLock()) {
                try {
                    if (printTime.getTime() % 3 == 2) {
                        size = size + 1;
                        printTime.setTime(printTime.getTime() + 1);
                        System.out.println("C");
                    }
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}


@Data
@AllArgsConstructor
class PrintTime {

    int time;

}

注意,unlock()方法一定要写在finally块中。

用wait/notify机制:

用Condition的await/signal通信机制:

A线程打印完,唤醒B,且本身await,B线程打印完,唤醒C,且本身await,C线程打印完,唤醒A,且本身await。。。依次

public class AwaitSignalTest {

    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition conditionA = lock.newCondition();
        Condition conditionB = lock.newCondition();
        Condition conditionC = lock.newCondition();

        int threadSize = 3;
        int printTimePerThread = 10;
        int maxPrintTime = threadSize * printTimePerThread;

        new Thread(new Print(lock, conditionA, conditionB, "A", threadSize, maxPrintTime, 0)).start();
        new Thread(new Print(lock, conditionB, conditionC, "B", threadSize, maxPrintTime, 1)).start();
        new Thread(new Print(lock, conditionC, conditionA, "C", threadSize, maxPrintTime, 2)).start();

    }

}

class Print implements Runnable {

    private static int printTimeInt = 0;

    private Lock lock;
    private final Condition conditionToWait;
    private final Condition conditionNotify;

    private String printStr;
    private int threadSize;
    private int maxPrintTime;
    private int index;

    public Print(Lock lock, Condition conditionToWait, Condition conditionNotify, String printStr,
            int threadSize, int maxPrintTime, int index) {
        this.lock = lock;
        this.conditionToWait = conditionToWait;
        this.conditionNotify = conditionNotify;
        this.printStr = printStr;
        this.threadSize = threadSize;
        this.maxPrintTime = maxPrintTime;
        this.index = index;
    }

    @Override
    public void run() {
        while (true) {
            lock.lock();
            if (printTimeInt >= maxPrintTime) {
                System.exit(1);
            }
            if (printTimeInt % threadSize == index) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(printStr);
                printTimeInt++;
                conditionNotify.signal();
            }
            try {
                conditionToWait.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

上面的代码不是一次写好的,是先把三个线程的实现分别写出来,调通,然后一步步调优得到的。代码中的System.exit(1);是让jvm退出,自己习惯是lock.unlock();break;,但是这样不行,原因还没定位出来。

一个Lock实例可以生成多个Condition实例,而调用任意一个Condition实例的await()方法都会使调用线程释放掉这个Lock实例,如果在await之前先调用另一个Condition实例的signal()方法,则相当于先唤醒另一个线程,然后让本线程等待唤醒。这样的特性非常适合于多线程中各线程按照自定义顺序执行的场景,但是编程难度也是很大的,要debug好久。

原文地址:https://www.cnblogs.com/koushr/p/5873423.html