Java面试必备--经典代码回顾(二)

      相信很多同行小伙伴会因为许多原因想跳槽,不论是干得不开心还是想跳槽涨薪,在如此内卷的行业,我们都面临着“面试造火箭,上班拧螺丝”的局面,鉴于当前形势博主呕心沥血整理的干货满满的造火箭的技巧来了,本博主花费2个月时间,整理归纳java全生态知识体系常见面试题!总字数高达百万! 干货满满,每天更新,关注我,不迷路,用强大的归纳总结,全新全细致的讲解来留住各位猿友的关注,希望能够帮助各位猿友在应付面试笔试上!当然如有归纳总结错误之处请各位指出修正!如有侵权请联系博主QQ1062141499!


目录

1 统计某字符串在文件中出现的次数

2  二分查找算法

3  实现一个容器,提供两个方法add,size。写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数为5的时候,线程2给出提示并退出,线程1继续执行。

4 两个数组求交集

5 动态代理算法

6  10个线程执行完成后才能执行下一步操作的编码

7  两个线程交替打印 1a2b3c4d5e

  方法1-synchronized关键字实现

  方法2 -LockSupport

  方法3- BlockingDeque阻塞队列实现

  方法4- ReentrantLock可重入锁实现

  方法5-TransferQueue实现

  方法6-自旋方式实现


1 统计某字符串在文件中出现的次数

public static void main(String[] args) {
    String filePath = "/Users/handsome/Desktop/a.txt";
    String word = "Lee";
    System.out.println(countWordAppearTimes(filePath, word));
}
/**
 * 统计每行的出现单词的出现次数之后
 * @param filePath
 * @param word
 * @return
 */
public static int countWordAppearTimes(String filePath, String word) {
    int times = 0;
    FileReader fr = null;
    BufferedReader br = null;
    try {
        fr = new FileReader(filePath);
        br = new BufferedReader(fr);
        String line;
        while ((line = br.readLine()) != null) {//读文件每行字符串
            //按照单词正则查找出现次数
            Pattern p = Pattern.compile(word);
            Matcher m = p.matcher(line);
            while (m.find()) {
                times++;
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (fr != null) {
            try {
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (br != null) {
            try {
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return times;
}

2  二分查找算法

/**
 * @description :  二分查询(非递归方式) {1,3,8,10,11,67,100},编程实现二分查找,要求使用非递归方式完成。
 * @date: 2020-06-30 17:25
 */
public class BinarySearchNonRecursive {
    public static void main(String[] args) {
        int[] arr = {1, 3, 8, 10, 11, 67, 100};
        int index = binarySearch(arr, 67);
        if (index != -1) {
            System.out.println("找到了,下标为:" + index);
        } else {
            System.out.println("没有找到--");
        }
    }
    private static int binarySearch(int[] arr, int target) {
        int left = 0;
        int right = arr.length - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (arr[mid] == target) {
                return mid;
            } else if (arr[mid] > target) {
                right = mid - 1; // 向左找
            } else {
                left = mid + 1; // 向右找
            }
        }
        return -1;
    }
}

3  实现一个容器,提供两个方法add,size。写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数为5的时候,线程2给出提示并退出,线程1继续执行。

    关键:容器使用volatile关键字修饰。

    方法一:

    使用wait(),notify()实现。

package com.junli;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class Container {
    //volatile关键字,使t2得到通知,否则t2不能判断container的size
    volatile List<Object> list = new ArrayList<>();
    public void add(Object o){
        list.add(o);
    }
    public int getSize(){
        return list.size();
    }
    public static void main(String[] args) {
        Container container = new Container();
        final Object lock = new Object();
        new Thread(()->{
            synchronized (lock){
                System.out.println("t2启动");
                if(container.getSize() != 5){
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("t2结束");
                //通知t1继续执行
                lock.notify();
            }
        },"t2").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            synchronized (lock){
                System.out.println("t1启动");
                for(int i =0;i < 10;i++){
                    container.add(new Object());
                    System.out.println("add"+i);
                    if(container.getSize() == 5){
                        lock.notify();
                        try {
                            //释放锁,让t2可以获得锁得以执行
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },"t1").start();
    }
}

方法二:

      使用Latch(门闩)代替wait,notify来进行通知

      好处通信方式简单,同时也可以指定等待时间

      使用await和countdown方法替代wait和notify

      CountDownLatch不涉及锁定,当count的值为零时线程继续执行

      当不涉及同步,只是涉及线程通信的时候,用synchronized + wait/notify就显得太重了

package com.junli;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class Container {
    //volatile关键字,使t2得到通知,否则t2不能判断container的size
    volatile List<Object> list = new ArrayList<>();
    public void add(Object o) {
        list.add(o);
    }
    public int getSize() {
        return list.size();
    }
    public static void main(String[] args) {
        Container container = new Container();
        //调用countDown方法后-1,门就开了
        CountDownLatch latch = new CountDownLatch(1);
        new Thread(() -> {
            System.out.println("t2启动");
            if (container.getSize() != 5) {
                try {
                    latch.await();//阻塞当前线程,直到计数器的值为0
                    //也可以指定等待时间
                    //latch.await(5,TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("t2结束");
        }, "t2").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            System.out.println("t1启动");
            for (int i = 0; i < 10; i++) {
                container.add(new Object());
                System.out.println("add" + i);
                if (container.getSize() == 5) {
                    //打开门闩,让t2得以执行
                    latch.countDown();
                }
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t1").start();
    }
}

4 两个数组求交集

public class ArrayJoinDemo {

    private static final int[] ARR_1 = { 51, 32, 35, 24, 15 };
    private static final int[] ARR_2 = { 11, 24, 32, 14, 5 };

    /**
     * 使用Set的特性(不允许重复),把数组1保存在HashSet中,然后遍历数组2的值,如果当前值已存在Set中,则该值为交集。同理,我们将交集保存在Set中去重,最后转换成int数组返回。
     */
    @Test
    void test1() {

        HashSet<Integer> hashSet = new HashSet<>();
        HashSet<Integer> hashSet2 = new HashSet<>();
        for (int i : ARR_1) {
            hashSet.add(i);
        }
        for (int i : ARR_2) {
            if (hashSet.contains(i)) {
                hashSet2.add(i);
            }
        }
        int[] arr = new int[hashSet2.size()];
        int i = 0;
        for (Integer integer : hashSet2) {
            arr[i] = integer;
            i++;
        }
        Console.log(arr);
    }

    /**
     * 暴力破解法,遍历两个数组,比较值。借用第三个数组来装载相同的值。时间复杂度为O(n).
     */
    @Test
    void test2() {

        int N = 0;
        if (ARR_1.length > ARR_2.length) {
            N = ARR_2.length;
        } else {
            N = ARR_1.length;
        }
        int[] n = new int[N];
        int k = 0;
        for (int i = 0; i < ARR_1.length; i++) {
            for (int j = 0; j < ARR_2.length; j++) {
                if (ARR_1[i] == ARR_2[j]) {
                    n[k++] = ARR_1[i];
                }
            }
        }
        n = Arrays.copyOf(n, k);
        Console.log(n);
    }

}

5 动态代理算法

interface Target {

    void test();
}

class TargetImp implements Target {

    @Override
    public void test() {

        System.out.println("test");
    }
}

/**
 * 动态代理算法
 * 不修改原始代码的情况下,执行自定义代码
 */
public class TargetProxy {

    /**
     * invoke() 代表的是执行代理对象的方法
     * method:代表目标对象的方法字节码对象
     * args:代表目标对象的响应的方法的参数
     */
    @Test
    void test1() {

        Target target = (Target) Proxy.newProxyInstance(
                Target.class.getClassLoader(),
                new Class[] { Target.class },
            (proxy, method, args) -> {
                Console.log("before");
                // 反射执行
                Object invoke = method.invoke(new TargetImp(), args);
                Console.log("after");
                return invoke;
            });
        target.test();
    }

    @Test
    void test2() {

        TargetImp targetImp = new TargetImp();
        Target target = (Target) Proxy.newProxyInstance(
            targetImp.getClass().getClassLoader(),
            targetImp.getClass().getInterfaces(),
            (proxy, method, args) -> {
                Console.log("test2 before");
                Object invoke = method.invoke(new TargetImp(), args);
                Console.log("test2 after");
                return invoke;
            });
        target.test();
    }
}

6  10个线程执行完成后才能执行下一步操作的编码

       方法1,利用Future

// 构造方法参数列表解释:
// corePoolSize -  池中所保存的线程数,包括空闲线程。
// maximumPoolSize -  池中允许的最大线程数。
// keepAliveTime -  当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
// unit - keepAliveTime  参数的时间单位。
// workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable  任务。
private static ExecutorService executor =
        new ThreadPoolExecutor(5, 10, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
/**
 * 10个线程执行完成后才能执行下一步操作
 */
@Test
public void testThreadOver() {
    List<Future> futureList = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        Future<?> future = executor.submit(() -> {
            try {
                Thread.sleep(1000l);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
        });
        futureList.add(future);
    }
    poolExceptionHanding(executor, ArrayUtil.toArray(futureList,Future.class));
    log.info("所有线程执行完毕,执行下一步操作");
}
/**
 * @param pool    线程池
 * @param futures Future对象
 * @description: 处理线程池的异常
 * @return: void
 * @author: lijun
 * @date: 2020-06-30 15:11:06
 */
private void poolExceptionHanding(ExecutorService pool, Future... futures) {
    try {
        for (Future future : futures) {
            future.get();
        }
        log.info("所有线程无异常");
    } catch (Exception e) {
        pool.shutdownNow();
        log.error("异常: " + e.getMessage());
        throw new RuntimeException(e.getCause());
    } finally {
        // 不再接受新的线程,所有线程完成则结束
        pool.shutdown();
        log.info("关闭线程池");
    }
}

         方法2,利用CountDownLatch

/**
 * 方法2,利用CountDownLatch
 * 10个线程执行完成后才能执行下一步操作,
 */
@Test
public void testCountDownLatch() {
    long start = System.currentTimeMillis();
    final ExecutorService service = Executors.newFixedThreadPool(10);
    final CountDownLatch countDownLatch = new CountDownLatch(10);
    for (int i = 0; i < 10; i++) {
        service.execute(() -> {
            try {
                Thread.sleep(1000l);
                countDownLatch.countDown();//当前线程调用此方法,则计数减一
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
        });
    }
    try {
        countDownLatch.await();//阻塞当前线程,直到计数器的值为0
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    log.info("所有线程执行完毕,耗时{}毫秒,执行下一步操作", System.currentTimeMillis() - start);
}

7  两个线程交替打印 1a2b3c4d5e

  方法1-synchronized关键字实现

public class AlternateThread {
    private static volatile Object lock = new Object();
    /**
     * 两个线程交替打印 1a2b3c4d5e
     *
     * @param args
     */
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                synchronized (lock) {
                    try {
                        System.out.println(i);
                        lock.notify();
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    lock.notify();
                }
            }
        });
        Thread t2 = new Thread(() -> {
            for (char i = 'a'; i <= 'e'; i++) {
                synchronized (lock) {
                    try {
                        System.out.println(i);
                        lock.notify();
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    lock.notify();
                }
            }
        });
        t1.start();
        t2.start();
    }
}

  方法2 -LockSupport

private static Thread t1, t2;
    public static void main(String[] args) {
        waistDemo();
    t1.start();
        t2.start();
    }
    public static void waistDemo() {
        t1 = new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                System.out.println(i);
                // 唤醒处于阻塞状态的指定线程
                LockSupport.unpark(t2);
                // 阻塞当前线程,如果调用unpark方法或者当前线程被中断,从能从park()方法中返回
                LockSupport.park();
            }
        });
        t2 = new Thread(() -> {
            for (char i = 'a'; i <= 'e'; i++) {
                LockSupport.park();
                System.out.println(i);
                // 唤醒处于阻塞状态的指定线程
                LockSupport.unpark(t1);
            }
        });
    }

  方法3- BlockingDeque阻塞队列实现

private static Thread t1, t2; 
    private static BlockingDeque<Object> bq1 = new LinkedBlockingDeque<>(1);
    private static BlockingDeque<Object> bq2 = new LinkedBlockingDeque<>(1);
    public static void main(String[] args) {
        blockingDequeDemo();
        t1.start();
        t2.start();
    }
/**
 * 使用BlockingDeque两个线程交替打印1a2b3c4d5e
 */
public static void blockingDequeDemo() {
    t1 = new Thread(() -> {
        for (int i = 1; i <= 5; i++) {
            try {
                //put()方法向队列中生产数据,当队列满时,线程阻塞
                bq1.put(i);
                //take()方法从队列中消费数据,当队列为空是,线程阻塞
                System.out.println(bq2.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    t2 = new Thread(() -> {
        for (char i = 'a'; i <= 'e'; i++) {
            try {
                System.out.println(bq1.take());
                bq2.put(i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
}

  方法4- ReentrantLock可重入锁实现

 private static Thread t1, t2; 
    private static ReentrantLock reentrantLock = new ReentrantLock(true);
    private static Condition condition1 = reentrantLock.newCondition();
    private static Condition condition2 = reentrantLock.newCondition();
    private static volatile Boolean ifT2Start = false;
    public static void main(String[] args) {
        reentrantLockDemo();
        t1.start();
        t2.start();
    }
    public static void reentrantLockDemo() {
        t1 = new Thread(() -> {
            try {
                reentrantLock.lock();
                while (!ifT2Start) {
                    for (int i = 1; i <= 5; i++) {
                        System.out.println(i);
                        //调用condition的signal或者signalAll方法可以将等待队列中等待时间最长的节点移动到同步队列中
                        condition1.signal();
                        //当调用condition.await()方法后会使得当前获取lock的线程进入到等待队列,如果该线程能够从await()方法返回的话一定是该线程获取了与condition相关联的lock
                        condition2.await();
                    }
                }
                condition1.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        });
        t2 = new Thread(() -> {
            try {
                reentrantLock.lock();
                ifT2Start = true;
                for (char i = 'a'; i <= 'e'; i++) {
                    System.out.println(i);
                    condition2.signal();
                    condition1.await();
                }
                condition2.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        });
    }

  方法5-TransferQueue实现

 private static Thread t1, t2;
    public static void main(String[] args) {
        transferQueueDemo();
        t1.start();
        t2.start();
    }
    private static TransferQueue<Object> tq = new LinkedTransferQueue<>();
    public static void transferQueueDemo() {
        t1 = new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                try {
                    tq.transfer(i);
                    System.out.println(tq.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t2 = new Thread(() -> {
            for (char i = 'a'; i <= 'e'; i++) {
                try {
                    System.out.println(tq.take());
                    tq.transfer(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }

  方法6-自旋方式实现

 private static Thread t1, t2;
    public static void main(String[] args) {
        selfDemo();
        t1.start();
        t2.start();
    }
    static volatile int x = 0;
    /**
     * 使用自旋方式两个线程交替打印1a2b3c4d5e
     */
    public static void selfDemo() {
        t1 = new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                while (x != 0) {
                }
                System.out.println(i);
                x = 1;
            }
        });
        t2 = new Thread(() -> {
            for (char i = 'a'; i <= 'e'; i++) {
                while (x != 1) {
                }
                System.out.println(i);
                x = 0;
            }
        });
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值