news 2026/6/23 19:19:44

回顾Java知识点,面试题汇总Day9(持续更新)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
回顾Java知识点,面试题汇总Day9(持续更新)

1.sleep()和wait()的区别?

sleep和wait的功能类似,都是让线程暂停执行任务。

  • sleep是Thread类提供的方法,wait是Object类提供的方法
  • sleep是直接作用于线程对象本身,wait作用于线程正在访问的资源。

调用A对象的wait:让当前正在访问的A对象的线程休眠,同时它拥有一个前提,当前线程必须拥有A对象,所以wait方法只能在同步方法或同步代码块中调用,否则会抛出异常。

  • wait会释放锁,sleep不会释放锁。
package com; import java.util.concurrent.TimeUnit; public class Test3 { public static void main(String[] args) { A a = new A(); new Thread(()->{ for (int i = 0; i < 10; i++) { a.test(i); } }).start(); } } class A{ public synchronized void test(int i){ if(i == 5){ //wait没有时间,会一直休眠下去 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i + "-----------------"); } }

i == 5时,调用A的wait方法,会让正在访问A的线程休眠,并且永久不会解除阻塞。

2.如何让线程解除阻塞?

  • 指定wait的时间,wait(long millis)之后会自动解决阻塞,类似sleep

wait->Object,A继承了Object,A中自带wait方法。

sleep->Thread

class A{ public synchronized void test(int i){ if(i == 5){ //wait没有时间,会一直休眠下去 try { this.wait(5000);//指定时间后,阻塞5秒继续输出(this也可以去掉) } catch (InterruptedException e) { e.printStackTrace(); } } try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i + "-----------------"); } }
  • 调用notify方法唤醒线程
package com; import java.util.concurrent.TimeUnit; public class Test3 { public static void main(String[] args) { A a = new A(); new Thread(()->{ for (int i = 0; i < 10; i++) { a.test(i); } }).start(); new Thread(()->{ try { TimeUnit.SECONDS.sleep(7); //输出到5时,等待两秒,再继续输出 } catch (InterruptedException e) { throw new RuntimeException(e); } a.test1(); }).start(); } } class A{ public synchronized void test(int i){ if(i == 5){ //wait没有时间,会一直休眠下去 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } try { TimeUnit.SECONDS.sleep(1); //表示1秒输出一个值 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i + "-----------------"); } public synchronized void test1(){ //必须使用synchronized修饰 this.notify(); } }

wait和notify方法必须放到同步方法或者同步代码中,否则会抛出异常。

3.synchronized锁定的是谁?

  • 如果synchronized修饰非静态方法,则锁定的是方法的调用者
import java.util.concurrent.TimeUnit; public class SyDemo { public static void main(String[] args) { Data data = new Data(); new Thread(()->{ data.func1(); },"A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { throw new RuntimeException(e); } new Thread(()->{ data.func2(); },"B").start(); } } class Data{ public synchronized void func1(){ try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("1======="); } public synchronized void func2(){ System.out.println("2++++"); } }

  • 如果synchronized修饰的是静态方法,则锁定的是类,无论有多少个对象,都会同步。

4.ConcurrentModificationException并发修改异常

并发修改:多个线程对同一个对象进行修改。

import java.util.ArrayList; import java.util.List; public class Bfxg { public static void main(String[] args) { List<String>list = new ArrayList<>(); for (int i = 0; i < 10; i++) { new Thread(()->{ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } list.add("a"); System.out.println(list); }).start(); } } }

由于同时读,同时写,产生了异常。

ArrayList是线程不安全的集合,当多个线程同时操作集合时,会出现数据不准确的情况。

5.ConcurrentModificationException如何解决?

  • 将ArrayList替换成线程安全的集合Vector

Vector中add方法是synchronized修饰

public synchronized boolean add(E e) { modCount++; add(e, elementData, elementCount); return true; }

ArrayList中的add方法

public boolean add(E e) { modCount++; add(e, elementData, size); return true; }
  • 使用Collections.synchronizedList

import java.util.*; public class Bfxg { public static void main(String[] args) { // List<String> list = new ArrayList<>(); // List<String>list = new Vector<>(); List<String> list = Collections.synchronizedList(new ArrayList<>()); for (int i = 0; i < 10; i++) { new Thread(()->{ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } list.add("a"); System.out.println(list); }).start(); } } }
  • JUC工具类CopyOnWriteArrayList

java.util.concurrent JDK的一个包,存放的都是关于并发的工具类。

写时复制。

import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; public class Bfxg { public static void main(String[] args) { List<String> list = new CopyOnWriteArrayList<>(); for (int i = 0; i < 10; i++) { new Thread(()->{ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } list.add("a"); System.out.println(list); }).start(); } } }

copyOnWrite写时复制,当我们往一个容器中添加元素的时候,不直接往当前容器中添加,而是将当前容器复制,向新容器中添加元素,添加完成之后,再将原容器的引用指向新的容器。

public boolean add(E e) { synchronized (lock) { Object[] es = getArray(); int len = es.length; es = Arrays.copyOf(es, len + 1); es[len] = e; setArray(es); return true; } }

可以对CopyOnWrite容器进行并发的读,因为当前容器不会添加任何元素,添加元素都是针对复制出来的新集合进行操作,所以CopyOnWrite容器也是一种读写分明的思想,读和写操作的是不同容器。

6.JUC常见并发编程工具包

  • CountDownLatch

减法计数器,JUC的工具类,可以用来倒计时,当两个线程同时执行时,如果我们要确保一个线程优先执行,设置一个计数器,当计数器清零的时候,再执行另一个线程。

import java.util.concurrent.CountDownLatch; public class CountDownLatchTest { public static void main(String[] args) { CountDownLatch countDownLatch = new CountDownLatch(100);//因为需要循环100次 new Thread(()->{ for (int i = 0; i < 100; i++) { System.out.println("+++++++++++Thread"); countDownLatch.countDown(); } }).start(); //判断是否清零 try { countDownLatch.await();//注意:这里是await,不是wait,如果使用wait,会报异常,且不会执行main------ } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 100; i++) { System.out.println("main-----------"); } } }
  • CyclicBarrier

加法计数器

import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; public class CyclicBarrierTest { public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(10,()->{ System.out.println("放行"); }); for (int i = 0; i < 30; i++) { final int temp = i; new Thread(()->{ System.out.println("-->" + temp); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }).start(); } } }
  • Semaphore

计数信号量,实际开发中主要用来完成限流操作,即限制可以访问某些资源的线程数量。

  • 初始化
  • 获得许可
  • 释放
import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; public class SemaphoreTest { public static void main(String[] args) { Semaphore semaphore = new Semaphore(5); for (int i = 0; i < 15; i++) { new Thread(()->{ try { semaphore.acquire();//获取许可 System.out.println(Thread.currentThread().getName() + "进店购买"); TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName() + "出店"); } catch (InterruptedException e) { e.printStackTrace(); }finally { semaphore.release();//释放 } },String.valueOf(i)).start(); } } }
  • 读写锁

接口ReadWriteLock,实现类ReentrantReadWriteLock,可以多线程同时读,但是同一时间只能有一个线程进行写入操作。

读写锁功能也是为了实现线程同步,只不过粒度更细,可以分别给读和写操作设置不同的锁机制。

import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWriteLockTest { public static void main(String[] args) { Cache cache = new Cache(); for (int i = 0; i < 5; i++) { final int temp = i; new Thread(()->{ cache.write(temp,String.valueOf(temp)); }).start(); } for (int i = 0; i < 5; i++) { final int temp = i; new Thread(()->{ cache.read(temp); }).start(); } } } class Cache{ private Map<Integer,String> map = new HashMap<>(); ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); //写操作 public void write(Integer key,String value){ readWriteLock.writeLock().lock(); System.out.println(key + "开始写入"); map.put(key, value); System.out.println(key + "写入完毕"); readWriteLock.writeLock().unlock(); } //读操作 public void read(Integer key){ readWriteLock.readLock().lock(); System.out.println(key + "开始读取"); map.get(key); System.out.println(key + "读取完毕"); readWriteLock.readLock().unlock(); } }

7.线程池

预先创建好一定数量的线程对象,存入缓冲池,需要用的时候直接从缓冲池中取出,用完后还回到缓冲池中,供下一次任务使用。

优点

  • 提高线程的利用率
  • 提高响应速度
  • 便于统一管理线程对象
  • 可控制最大并发量

工作流程

  • 初始化线程池,创建默认数量的线程对象
  • 当任务过多的时候,额外补充线程数量
  • 当任务趋于正常时,额外补充的线程会自动销毁

初始化线程数量

最大线程数量

线程池的实现(3种)

import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Pool { public static void main(String[] args) { // //单例线程池,只有一个线程对象 // ExecutorService executorService = Executors.newSingleThreadExecutor(); // ExecutorService executorService = Executors.newFixedThreadPool(5); //缓存线程池,不同性能的电脑分配的不一样 ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { final int temp = i; executorService.execute(()->{ System.out.println(Thread.currentThread().getName() + ":" + temp); }); } executorService.shutdown(); } }
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }

ThreadPoolExecutor 线程池的原生类

核心参数

  • corePoolSize:核心池大小
  • maximumPoolSize:线程池最大线程数
  • keepAliveTime:空闲线程的存活时间
  • unit:时间单位
  • workQueue:阻塞队列
  • threadFactory:线程工厂
  • handler:拒绝策略
import java.util.concurrent.*; public class Pool { public static void main(String[] args) { ExecutorService executorService = new ThreadPoolExecutor(2,5,1L, TimeUnit.SECONDS,new ArrayBlockingQueue<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy()); for (int i = 0; i < 2; i++) { executorService.execute(()->{ try { TimeUnit.SECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在办理业务"); }); } executorService.shutdown(); } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/23 19:20:02

Linux内核消息观测生产排障流程

Linux内核消息观测生产排障流程这是一篇面向中级 Linux 使用者的技术文章&#xff0c;主题聚焦在内核消息观测&#xff0c;重点讨论内核日志、驱动事件和硬件异常。在真实生产环境中&#xff0c;内核消息观测相关问题往往不会以单一错误形式出现&#xff0c;而是混杂在日志、权…

作者头像 李华
网站建设 2026/6/23 19:41:57

大模型微调实战:用LoRA技术微调LLaMA 2模型

在人工智能技术飞速发展的当下&#xff0c;大语言模型&#xff08;LLM&#xff09;在自然语言处理领域展现出了强大的能力。LLaMA 2作为Meta推出的开源大模型&#xff0c;凭借其出色的性能和广泛的适用性&#xff0c;成为了众多开发者和研究人员的首选。对于软件测试从业者而言…

作者头像 李华
网站建设 2026/6/23 19:20:01

机器学习中的特征工程:如何提取有效的特征

一、特征工程&#xff1a;连接数据与模型的核心桥梁在机器学习的完整流程中&#xff0c;特征工程是承上启下的关键环节。它是指对原始数据进行一系列处理、转换和筛选&#xff0c;提取出能有效反映数据本质规律、与预测目标高度相关特征的过程。对于软件测试从业者而言&#xf…

作者头像 李华
网站建设 2026/6/23 19:19:59

别再写if-else了!MySQL的ON DUPLICATE KEY UPDATE,一个SQL搞定新增和修改

别再写if-else了&#xff01;MySQL的ON DUPLICATE KEY UPDATE&#xff0c;一个SQL搞定新增和修改 每次处理数据入库时&#xff0c;你是否还在重复这样的代码逻辑&#xff1f;先查询数据库判断记录是否存在&#xff0c;如果存在则执行更新&#xff0c;不存在则执行插入。这种模式…

作者头像 李华