目录名称

字节面试题1:

使用锁实现两个线程打印1~100,其中第一个线程打印奇数,第二个而线程打印偶数。

解法1:使用Object的wait、notify和notifyAll方法

public class WaitAndNotify {
    public static void main(String[] args) throws InterruptedException {
        PrintNumber  number = new PrintNumber();
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new Thread(number));
        exec.execute(new Thread(number));
        TimeUnit.SECONDS.sleep(5);
        exec.shutdownNow();
    }
}
class PrintNumber implements  Runnable{
    private int counter = 1;
    @Override
    public void run() {
        synchronized (this){//this是PrintNumber实例化对象
            while(counter <= 100){
                try {
                    System.out.println(Thread.currentThread().getName()+","+counter);
                    counter++;
                    notify();//通知其他阻塞线程变成同步等待状态
                    wait();//阻塞当前线程
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

方法2:使用lock和condition

contion使用await()、singal、signalAll依次代替了Object中的wait、notify、notifyAll字段,要求condition的三个方法必须在lock()和unlock()包围。await方法也是首先释放对象锁,然后将当前线程阻塞,放入到

class LockConditionPrintNumber {
    public static void main(String[] args) {
        PrintNumber2 number2 = new PrintNumber2();
        new Thread(number2,"线程A").start();
        new Thread(number2,"线程B").start();

    }
    static class PrintNumber2 implements  Runnable{
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        private int counter =1;
        @Override
        public void run() {
            try{
                lock.lock();
                while(counter <= 100){
                    System.out.println(Thread.currentThread().getName()+","+counter++);
                    condition.signal();
                    condition.await();
                }
            }catch (Exception e){

            }finally {
                lock.unlock();
            }
        }
    }
}

方法3:使用volatile关键字

public class VolatilePrintNumber {
    public static void main(String[] args) {
        PrintNumber1 number1  = new PrintNumber1();
        new Thread(()->{//打印1~100之间50个奇数
            for(int i = 0;i < 50;i++){
                number1.printOdd();
            }
        },"线程A").start();
        new Thread(()->{//打印1~100之间50个偶数
            for(int i = 0;i < 50;i++){
                number1.printEven();
            }
        },"线程B").start();
    }
}
class PrintNumber1{
    private volatile int flag = 1;
    private int counter = 1;
    public void printOdd(){
        while(flag!=1){//不能使用if
            Thread.yield();//让出CPU控制权
        }
        System.out.println(Thread.currentThread().getName()+","+counter++);
        flag= 2;
    }
    public   void printEven(){
        while(flag!=2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+","+counter++);
        flag = 1;
    }
}

Thread.yield是交出当前CPU的控制权,但无法控制下次CPU分配给谁,会出现空循环等待情况。

方法4:使用Semphore

public class SemphorePrintNumber {
    public static void main(String[] args) {
        Semaphore s1 = new Semaphore(1);
        Semaphore s2 = new Semaphore(0);
        PrintNumber4 number4 = new PrintNumber4();
        new Thread(()->{
            try {
                number4.printNumber(s1,s2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"线程A").start();
        new Thread(()->{
            try {
                number4.printNumber(s2,s1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"线程B").start();
    }
}
class PrintNumber4{
    private int counter =1;
    public void printNumber(Semaphore s1,Semaphore s2) throws InterruptedException {
        while(counter < 100){
            s1.acquire();
            System.out.println(Thread.currentThread().getName()+","+counter++);
            s2.release();
        }
    }
}

小结:基于加锁的方式,实现两个线程通过通信协作的方式交替打印,基于volatile的方式,虽然实现了两个线程交替打印数字,但每个线程执行完成之后,只是主动让出了线程,并没有通知另外一个线程执行,会出现很多无效的线程上限文之间切换和循环判断,严格来说,不属于线程之间协作,效率也比较低下。

错误解法:基于while循环

public class WhilePrintNumber {
    public static void main(String[] args) {
        int counter  = 1;
        PrintNumber3  number3 = new PrintNumber3();
        new Thread(()->{
            number3.printOdd();
        },"线程A").start();
        new Thread(()->{
            number3.printEven();
        },"线程B").start();
    }
}
class PrintNumber3{
    private int counter = 1;
    public void printOdd(){
        while(counter<=1000){
            if(counter%2!=0){
                System.out.println(Thread.currentThread().getName()+","+counter);
                counter++;
            }
        }
    }
    public void printEven(){
        while(counter<=1000){
            if(counter%2==0){
                System.out.println(Thread.currentThread().getName()+","+counter);
                counter++;
            }
        }
    }
}

alt 自己猜测: 每次程序有可能结束,有可能不能成功结束。截图情况不能继续执行原因,在打印完303时,counter++还没有执行,就切换到线程B,线程B在执行while循环条件303%2=1,显然,不成立,一直在执行while循环判断,线程A一值得不到CPU的控制权,counter的值一值无法改变。

字节面试题2:

实现缓存替换,可以选择通过配置实现LFU和LRU方式。

力扣460:https://leetcode-cn.com/problems/lfu-cache/

Lazada面试题2:

public MyMap implements Map{
   public void put(String key);
   public void remove(String key);
}

自己思考主要让描述hashMap的一个插入过程和是否阅读过源码,写出大体整个过程就行。

携程面试题1:

使用多线程实现一个生产者和消费者。 alt 该模型实现了生产者和消费者之间的解耦、异步和平衡生产者和消费者之间的速度,队列相当于存储产品的缓冲区。

import java.util.ArrayDeque;
import java.util.Queue;
import java.util.Random;

public class ProducerAndConsumer {
    public static void main(String[] args) {
        Queue<Product> queue = new ArrayDeque<>();
        int maxCapacity = 10;//容器大小为10
        for(int i = 0;i < 3;i++){//创建3个生产者和3个消费者,
            new Thread(new Producer(queue,maxCapacity),""+i).start();
            new Thread(new Consumer(queue,maxCapacity),""+i).start();
        }
    }
}
class Producer implements Runnable{
    private Queue<Product> container;
    private int maxCapacity;
    public Producer(Queue queue,int maxCapacity){
        this.container = queue;
        this.maxCapacity = maxCapacity;
    }

    @Override
    public void run() {
        while(true){
                synchronized (container){//保证在一个时间只能一个线程访问容器
                Random random = new Random();
                Product newProduct = new Product(String.valueOf(random.nextInt()));
                while(container.size()==maxCapacity){
                    System.out.println("容器已经满了,生成的产品放不下了");
                    container.notifyAll();//通知消费者去消费
                    try {
                        container.wait();//当前线程先阻塞
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                try {
                    Thread.sleep(200);//表示生产产品所需时间
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("生产者:"+Thread.currentThread().getName()+"生产了一个产品:"+newProduct.toString());
                container.offer(newProduct);
                container.notifyAll();//新生产了产品,通知其她消费者去消费
            }
        }
    }
}
class Consumer implements Runnable{
    private Queue<Product> container;
    private int maxCapacity;
    public Consumer(Queue queue,int maxCapacity){
        this.container = queue;
        this.maxCapacity = maxCapacity;
    }

    @Override
    public void run() {

        while(true){
            synchronized (container){//保证在一个时间只能一个线程访问容器
                while(container.size()==0){
                    System.out.println(Thread.currentThread().getName()+"容器内没有产品,无法消费,等待生产品..");
                    container.notifyAll();//通知消费者去消费
                    try {
                        container.wait();//当前线程先阻塞
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Product  newProduct = container.poll();
                try {
                    Thread.sleep(200);//表示消耗产品所需时间
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("消费者:"+Thread.currentThread().getName()+"消费了一个产品:"+newProduct.toString());
                container.notifyAll();//有空间可以用来盛产品,通知其生产者去生产
            }
        }
    }
}
class Product{
    private  String name;
    public Product(){}
    public Product(String name){
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Product{" +
                "name='" + name + '\'' +
                '}';
    }
}

注意点:在使用Object的wait/notify/notifyAll,需要对对象加锁和释放锁,所以要想直接使用wait/notify/notifyAll,则使用synchronized(this),this表示当前类的对象,当对synchronized(container)加锁,要使用container.wait/container.notify/container.notifyAll。 alt

携程面试题2:

求能被3整除的最大64位整数,要求该被数只能由1组成

public static void main(String[] args) {
        long cur = 1,res = 0;//long占8个字节,64位
        long jinwei = 1;
        while(cur>0){//curr溢出将变成负数
            jinwei = jinwei*10;
            if(cur%3==0){//保存当前最大能被3整除的数
                res = cur;
            }
            cur = cur+jinwei;
        }
        System.out.println(res);
        System.out.println(cur);
    }

携程面试题3:

使用一下三个函数实现生成1~100之间任意随机数:

func2();//生成1~2之间的随机数

func3();//生成1~3之间的随机数

func5();// 生成1~ 5之间的随机数首先使用者三个函数生成一个1~ 10之间的函数。

思路:

func100=func10+(func10-1)*10 ---公式(1)

func10-1可以范围在1~ 9,(func10-1)*10范围在【0,10,20,30,40,50,60,70,80,90】,func10+(func10-1)*10范围在1~100,并且每个数生成概率相等0.01。

同理:func10 = func5+(func2-1)*2;---公式(2)

将公式(2)带入公式(1),可得:

func100=func10+(func10-1)*10

   = func5+(func2-1)*2+(func5+(func2-1)*2-1)*10
   =11func5+22func2-32 公式(3)