【Java】面试代码题 多线程

1. 实现一个死锁 快手一面8.11

思路:两个线程,两个Object A 和 B,线程一先获得 A 再获得 B,线程二先获得 B 再获得 A。为防止效果不明显,可以在 线程一获得 A 之后 sleep 一段时间,线程二获得 B 之后 sleep 一段时间。

实现一

写在一个 run() 方法里,用线程名判断先获取 A 还是 B。

public class TwoThreadDeadLock {
    public static void main(String[] args) {
        DeadLock deadLock = new DeadLock();
        new Thread(deadLock, "1").start();
        new Thread(deadLock, "2").start();
    }
}

class DeadLock implements Runnable {

    private final Object A = new Object();
    private final Object B = new Object();

    @Override
    public void run() {
        String currentThreadName = Thread.currentThread().getName();
        if (currentThreadName.equals("1")) {
            synchronized (A) {
                System.out.println(currentThreadName + " get A");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (B) {
                    System.out.println(currentThreadName + " get B");
                }
            }
        } else {
            synchronized (B) {
                System.out.println(currentThreadName + " get B");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (A) {
                    System.out.println(currentThreadName + " get A");
                }
            }
        }
    }
}

实现二(提取相同逻辑更简洁)

把获取锁的代码写进一个方法里,因为逻辑都是一样,先获取一个锁再获取另一个。

public class Main {
    public static void main(String[] args) {
        Object A = new Object();
        Object B = new Object();
        new Thread(() -> {
            task(A, B);
        }).start();
        new Thread(() -> {
            task(B, A);
        }).start();
    }

    static void task(Object first, Object second) {
        String currentThreadName = Thread.currentThread().getName();
        synchronized (first) {
            System.out.println(currentThreadName + " get " + first);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (second) {
                System.out.println(currentThreadName + " get " + second);
            }
        }
    }
}

2. 两个线程交替打印 0-100

思路:把打印部分的代码用 synchronized 包起来,打印之后先 notifyAll() 唤醒另一个线程,然后 wait() 自己并释放锁,让另一个线程打印下一个数字,如此往复,直到打印到 100,notifyAll() 另一个线程结束循环。

public class TwoThread0to100 {
    public static void main(String[] args) {
        Print p = new Print();
        new Thread(p).start();
        new Thread(p).start();
    }
}
class Print implements Runnable {
    private int number = 0;
    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                if (number >= 100) {
                    notifyAll();
                    break;
                }
                System.out.println(Thread.currentThread().getName() + " : " + (++number));
                notifyAll();
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

3. 三个线程交替打印,线程0打印零,线程1打印奇数,线程0打印0,线程2打印偶数 01020304 … 拼多多一面

思路:判断是否轮到当前线程打印,轮到就打印并 notifyAll() 其他线程尝试打印下一个数字;没轮到就 wait(),没轮到有四种情况:

总的来说分为两种情形,索引是奇数时线程0打印零,索引是偶数时那么线程1打印奇数或者线程2打印偶数。

  1. 因此如果当前进来的是线程0并且当前索引是偶数,那当前没轮到线程0打印,wait()
  2. 如果不是线程0,是线程1或线程2,并且当前索引是奇数,那当前轮到线程0打印,wait()

对于索引是偶数的情况,还要区分当前是打印奇数还是偶数,如果索引除2之后结果是奇数,那当前该打印奇数,除2之后结果是偶数,那当前该打印偶数。

  1. 所以如果当前是线程1,并且当前索引除2不是奇数,wait()
  2. 如果当前是线程2,并且当前索引除2是奇数,wait()
public class TriThread {
    public static void main(String[] args) {
        Alternate alternate = new Alternate();
        new Thread(alternate, "0").start();
        new Thread(alternate, "1").start();
        new Thread(alternate, "2").start();
    }
}
class Alternate implements Runnable {

    private int number = 1;

    @Override
    public void run() {
        while (number <= 200) {
            synchronized(this) {
                String currentThreadName = Thread.currentThread().getName();
                if (currentThreadName.equals("0") && (number&1)!=1 ||
                        !currentThreadName.equals("0") && (number&1)==1 ||
                        currentThreadName.equals("1") && ((number/2)&1)!=1 ||
                        currentThreadName.equals("2") && ((number/2)&1)==1) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    System.out.println(currentThreadName + " : " + (currentThreadName.equals("0") ? 0 : number/2));
                    number++;
                    notifyAll();
                }
            }
        }
    }
}

4. 四个线程交替打印ABCD,打印10次 美团一面8.17

public class Main {
    public static void main(String[] args) {
        new Thread(new SwapPrint(0)).start();
        new Thread(new SwapPrint(1)).start();
        new Thread(new SwapPrint(2)).start();
        new Thread(new SwapPrint(3)).start();
    }
}

class SwapPrint implements Runnable{
    private static final Object lock = new Object();
    private static int count = 0;
    private final int no;

    public SwapPrint(int no){
        this.no = no;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (lock) {
                if (count >= 40) {
                    break;
                }
                if (count % 4 == this.no) {
                    System.out.println("count: " + (count+1) + " thread" + (this.no+1) + "--->" + (char)('A'+(count%4)));
                    count++;
                } else {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                lock.notifyAll();
            }
        }

    }
}

Reference

  1. 三线程打印
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值