相信很多同行小伙伴会因为许多原因想跳槽,不论是干得不开心还是想跳槽涨薪,在如此内卷的行业,我们都面临着“面试造火箭,上班拧螺丝”的局面,鉴于当前形势博主呕心沥血整理的干货满满的造火箭的技巧来了,本博主花费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的sizevolatile 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的sizevolatile 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数组返回。*/@Testvoid 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).*/@Testvoid 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 {@Overridepublic void test() {System.out.println("test");}
}/*** 动态代理算法* 不修改原始代码的情况下,执行自定义代码*/
public class TargetProxy {/*** invoke() 代表的是执行代理对象的方法* method:代表目标对象的方法字节码对象* args:代表目标对象的响应的方法的参数*/@Testvoid 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();}@Testvoid 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相关联的lockcondition2.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;}});
}