阿里面试,面试官问我代理模式,我给他讲了几个小故事给他整的明明白白

简介: 阿里面试,面试官问我代理模式,我通过几个小故事给他整的明明白白,面试官满意的对我点了点头。

什么是代理

代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。

​代理其实不仅仅是在软件开发领域,在我们的日常生活中也是时常可见。比如某p2p老板突然携款带着小姨子跑路了,可怜了下面一堆的程序员背负一身房贷,上有老下有小,程序员只能被迫去申请劳动仲裁,劳动局就会为其指派一位代理律师全权负责程序员的仲裁事宜(PS:p2p跑路仲裁拿回工资的可能性非常低,没让你把工资退回就算好的了)。那这里面就是使用了代理模式,因为在劳动仲裁这个活动中,代理律师会全权代理程序员。比如:房东要将房子出售,于是到房地产中介公司找一个中介(代理),由他来帮房东完成销售房屋,签订合同、网签、贷款过户等等事宜。

代理模式

在这里插入图片描述
这是常见代理模式常见的 UML 示意图。
需要注意的有下面几点:

  1. 用户只关心接口功能,而不在乎谁提供了功能。上图中接口是 Subject
  2. 接口真正实现者是上图的 RealSubject,但是它不与用户直接接触,而是通过代理。
  3. 代理就是上图中的 Proxy,由于它实现了 Subject 接口,所以它能够直接与用户接触。
  4. 用户调用 Proxy 的时候,Proxy 内部调用了 RealSubject。所以,Proxy 是中介者,它可以增强 RealSubject 操作。
  • 代理又可以分为静态代理和动态代理两种。我们先来看下静态代理。

静态代理

电影是电影公司委托给影院进行播放的,但是影院可以在播放电影的时候,产生一些自己的经济收益,比如提供按摩椅,娃娃机(这个每次去电影院都会尝试下,基本上是夹不起来,有木有大神可以传授下诀窍),卖爆米花、饮料(贵的要死,反正吃不起)等。我们平常去电影院看电影的时候,在电影开始的阶段是不是经常会放广告呢?然后在影片开始结束时播放一些广告。
下面我们通过代码来模拟下电影院这一系列的赚钱操作。
首先得有一个接口,通用的接口是代理模式实现的基础。这个接口我们命名为 Movie,代表电影播放的能力。

package com.workit.demo.proxy;

public interface Movie {

void play();

}

  • 接下来我们要创建一个真正的实现这个 Movie 接口的类,和一个实现该接口的代理类。
    真正的类《美国队长》电影:

package com.workit.demo.proxy;

public class CaptainAmericaMovie implements Movie {

@Override
public void play() {
    System.out.println("普通影厅正在播放的电影是《美国队长》");
}

}

代理类:

package com.workit.demo.proxy;

public class MovieStaticProxy implements Movie {

Movie movie;

public MovieStaticProxy(Movie movie) {
    this.movie = movie;
}

@Override
public void play() {
    playStart();
    movie.play();
    playEnd();
}

public void playStart() {
    System.out.println("电影开始前正在播放广告");
}
public void playEnd() {
    System.out.println("电影结束了,接续播放广告");
}

}

测试类:

package com.workit.demo.proxy;

package com.workit.demo.proxy;

public class StaticProxyTest {

public static void main(String[] args) {
    Movie captainAmericaMovie = new CaptainAmericaMovie();
    Movie movieStaticProxy = new MovieStaticProxy(captainAmericaMovie);
    movieStaticProxy.play();

}

}

运行结果:

电影开始前正在播放广告
正在播放的电影是《美国队长》
电影结束了,接续播放广告

现在可以看到,代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。这个就是是静态代理的内容,为什么叫做静态呢?因为它的类型是事先预定好的,比如上面代码中的 MovieStaticProxy 这个类。

优点
  • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用
  • 代理对象可以扩展目标对象的功能
  • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度。
缺点
  • 代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护。

jdk动态代理

与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。

  • 接着上面的例子,刚看完《美国队长》不过瘾,还想继续去看一场《钢铁侠》。一直在普通影厅看电影觉得没啥意思,那就赶紧去VIP影厅(至今不知道长啥样子)体验一把。既然 实体店没体验过那就用代码来体验一次吧。创建一个VIPMovie电影接口

package com.workit.demo.proxy;
public interface VIPMovie {

void vipPlay();

}

紧接着创建一个VIP影厅的播放实现类

package com.workit.demo.proxy;

public class IronManVIPMovie implements VIPMovie {

@Override
public void vipPlay() {
    System.out.println("VI影厅正在播放的电影是《钢铁侠》");
}

}

如果按照静态代理我们是不是又要创建一个VIP影厅播放的代理实现类,这种方式我们就不演示了。下面我们来看看通过动态代理怎么来实现吧。

package com.workit.demo.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {

private Object object;

public MyInvocationHandler(Object object) {
    this.object = object;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    playStart();
    Object invoke = method.invoke(object, args);
    playEnd();
    return invoke;
}

public void playStart() {
    System.out.println("电影开始前正在播放广告");
}
public void playEnd() {
    System.out.println("电影结束了,接续播放广告");
}

}

MyInvocationHandler实现了 InvocationHandler 这个类,这个类是什么意思呢?大家不要慌张,下面我会解释。然后,我们就可以在VIP影厅看电影了。

package com.workit.demo.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class DynamicProxyTest {

public static void main(String[] args) {
    IronManVIPMovie ironManVIPMovie = new IronManVIPMovie();
    InvocationHandler invocationHandler = new MyInvocationHandler(ironManVIPMovie);
    VIPMovie dynamicProxy = (VIPMovie) Proxy.newProxyInstance(IronManVIPMovie.class.getClassLoader(),
            IronManVIPMovie.class.getInterfaces(), invocationHandler);
    dynamicProxy.vipPlay();
}

}

输出结果:

电影开始前正在播放广告
VI影厅正在播放的电影是《钢铁侠》
电影结束了,接续播放广告

看到没有,我们并没有像静态代理那样为 VIPMovie接口实现一个代理类,但最终它仍然实现了相同的功能,这其中的差别,就是之前讨论的动态代理所谓“动态”的原因。
我们顺带把《美国队长》也用动态代理实现下吧。

package com.workit.demo.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class DynamicProxyTest {

public static void main(String[] args) {
    // VIP 影厅《钢铁侠》
    IronManVIPMovie ironManVIPMovie = new IronManVIPMovie();
    InvocationHandler invocationHandler = new MyInvocationHandler(ironManVIPMovie);
    VIPMovie dynamicProxy = (VIPMovie) Proxy.newProxyInstance(IronManVIPMovie.class.getClassLoader(),
            IronManVIPMovie.class.getInterfaces(), invocationHandler);
    dynamicProxy.vipPlay();
    
    // 普通影厅《美国队长》
    CaptainAmericaMovie captainAmericaMovie = new CaptainAmericaMovie();
    InvocationHandler invocationHandler1 = new MyInvocationHandler(captainAmericaMovie);
    Movie dynamicProxy1 = (Movie) Proxy.newProxyInstance(CaptainAmericaMovie.class.getClassLoader(),
            CaptainAmericaMovie.class.getInterfaces(), invocationHandler1);
    dynamicProxy1.play();
}

}

输出结果:

电影开始前正在播放广告
VI影厅正在播放的电影是《钢铁侠》
电影结束了,接续播放广告
电影开始前正在播放广告
正在播放的电影是《美国队长》
电影结束了,接续播放广告

我们通过 Proxy.newProxyInstance() 方法,却产生了 MovieVIPMovie两种接口的实现类代理,这就是动态代理的魔力。

JDK动态代理到底是怎么实现的呢

动态代码涉及了一个非常重要的类 Proxy。正是通过 Proxy 的静态方法 newProxyInstance 才会动态创建代理。具体怎么去创建代理类就不分析了,感兴趣的可以去看下源码。我们直接看下生成的代理类。
如何查看生成的代理类?
在生成代理类之前加上以下代码(我用的jdk1.8):

//新版本 jdk产生代理类
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

如果上述代码加上不生效可以考虑加下下面的代码:

// 老版本jdk
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 该设置用于输出cglib动态代理产生的类
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\class");

代码如下:

public static void main(String[] args) {

    //新版本 jdk产生代理类
   System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
    // VIP 影厅《钢铁侠》
    IronManVIPMovie ironManVIPMovie = new IronManVIPMovie();
    InvocationHandler invocationHandler = new MyInvocationHandler(ironManVIPMovie);
    VIPMovie dynamicProxy = (VIPMovie) Proxy.newProxyInstance(IronManVIPMovie.class.getClassLoader(),
            IronManVIPMovie.class.getInterfaces(), invocationHandler);
    dynamicProxy.vipPlay();

    // 普通影厅《美国队长》
    CaptainAmericaMovie captainAmericaMovie = new CaptainAmericaMovie();
    InvocationHandler invocationHandler1 = new MyInvocationHandler(captainAmericaMovie);
    Movie dynamicProxy1 = (Movie) Proxy.newProxyInstance(CaptainAmericaMovie.class.getClassLoader(),
            CaptainAmericaMovie.class.getInterfaces(), invocationHandler1);
    dynamicProxy1.play();
    System.out.println("VIP 影厅《钢铁侠》代理类:"+dynamicProxy.getClass());
    System.out.println("普通影厅《美国队长》:"+dynamicProxy1.getClass());
}

我们可以看到结果

电影开始前正在播放广告
VI影厅正在播放的电影是《钢铁侠》
电影结束了,接续播放广告
电影开始前正在播放广告
正在播放的电影是《美国队长》
电影结束了,接续播放广告
VIP 影厅《钢铁侠》代理类:class com.sun.proxy.$Proxy0
普通影厅《美国队长》:class com.sun.proxy.$Proxy1

产生了两个代理类分别是$Proxy0$Proxy1
下面们来看下"钢铁侠"的代理类$Proxy0

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import com.workit.demo.proxy.VIPMovie;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements VIPMovie {

private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;

public $Proxy0(InvocationHandler var1) throws  {
    super(var1);
}

public final boolean equals(Object var1) throws  {
    try {
        return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
    } catch (RuntimeException | Error var3) {
        throw var3;
    } catch (Throwable var4) {
        throw new UndeclaredThrowableException(var4);
    }
}

public final void vipPlay() throws  {
    try {
        super.h.invoke(this, m3, (Object[])null);
    } catch (RuntimeException | Error var2) {
        throw var2;
    } catch (Throwable var3) {
        throw new UndeclaredThrowableException(var3);
    }
}

public final String toString() throws  {
    try {
        return (String)super.h.invoke(this, m2, (Object[])null);
    } catch (RuntimeException | Error var2) {
        throw var2;
    } catch (Throwable var3) {
        throw new UndeclaredThrowableException(var3);
    }
}

public final int hashCode() throws  {
    try {
        return (Integer)super.h.invoke(this, m0, (Object[])null);
    } catch (RuntimeException | Error var2) {
        throw var2;
    } catch (Throwable var3) {
        throw new UndeclaredThrowableException(var3);
    }
}

static {
    try {
        m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
        m3 = Class.forName("com.workit.demo.proxy.VIPMovie").getMethod("vipPlay");
        m2 = Class.forName("java.lang.Object").getMethod("toString");
        m0 = Class.forName("java.lang.Object").getMethod("hashCode");
    } catch (NoSuchMethodException var2) {
        throw new NoSuchMethodError(var2.getMessage());
    } catch (ClassNotFoundException var3) {
        throw new NoClassDefFoundError(var3.getMessage());
    }
}

},

通过上述代码我们可以看到 $Proxy0 extends Proxy implements VIPMovie继承了Proxy 且实现了VIPMovie接口,这也就是为什么jdk动态代理必须基于接口,java 是单继承的。
然后再看下代理类实现的方法:

public final void vipPlay() throws {

    try {
        super.h.invoke(this, m3, (Object[])null);
    } catch (RuntimeException | Error var2) {
        throw var2;
    } catch (Throwable var3) {
        throw new UndeclaredThrowableException(var3);
    }
}

这个supper.h.invoke Proxy中的h的invoke方法,即InvocationHandler.invoke也就是上面 MyInvocationHandler.invoke方法,至此整个流程就清晰了。这就是jdk的动态代理。

cglib动态代理

上面说jdk动态代理只能基于接口,那么如果是类要动态代理怎么办呢?cglib动态代理就可解决关于类的动态代理。
下面我们来创建一个“《美国队长2》”

package com.workit.demo.proxy;

public class CaptainAmerica2MovieImpl {

public void play(){
    System.out.println("正在播放的电影是《美国队长2》");
}

}

引入cglib pom依赖


<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>

创建一个自定义MethodInterceptor。

package com.workit.demo.proxy;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxyInterceptor implements MethodInterceptor {

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    playStart();
    Object object = methodProxy.invokeSuper(o, objects);
    playEnd();
    return object;
}

public void playStart() {
    System.out.println("电影开始前正在播放广告");
}

public void playEnd() {
    System.out.println("电影结束了,接续播放广告");
}

}

测试类

package com.workit.demo.proxy;

import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;

public class CglibProxyTest {

public static void main(String[] args) {
    // //在指定目录下生成动态代理类
    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\class");
    //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
    Enhancer enhancer = new Enhancer();
    //设置目标类的字节码文件
    enhancer.setSuperclass(CaptainAmerica2MovieImpl.class);
    //设置回调函数
    enhancer.setCallback(new CglibProxyInterceptor());
    //这里的creat方法就是正式创建代理类
    CaptainAmerica2MovieImpl captainAmerica2Movie = (CaptainAmerica2MovieImpl)enhancer.create();
    //调用代理类的play方法
    captainAmerica2Movie.play();
    System.out.println("cglib动态代理《美国队长2》:"+captainAmerica2Movie.getClass());
}

}

输出结果:

电影开始前正在播放广告
正在播放的电影是《美国队长2》
电影结束了,接续播放广告
cglib动态代理《美国队长2》:class com.workit.demo.proxy.CaptainAmerica2MovieImpl$$EnhancerByCGLIB$$5c3ddcfe

我们看下最终创建的代理类生成的play方法

public class CaptainAmerica2MovieImpl$$EnhancerByCGLIB$$5c3ddcfe extends CaptainAmerica2MovieImpl implements Factory {
public final void play() {

    MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
    if (var10000 == null) {
        CGLIB$BIND_CALLBACKS(this);
        var10000 = this.CGLIB$CALLBACK_0;
    }

    if (var10000 != null) {
        var10000.intercept(this, CGLIB$play$0$Method, CGLIB$emptyArgs, CGLIB$play$0$Proxy);
    } else {
        super.play();
    }
}

从代理对象反编译源码可以知道,代理对象继承于CaptainAmerica2MovieImpl ,拦截器调用intercept()方法,
intercept()方法由自定义CglibProxyInterceptor实现,所以,最后调用CglibProxyInterceptor中的intercept()方法,从而完成了由代理对象访问到目标对象的动态代理实现。

  • CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
  • 用CGlib生成代理类是目标类的子类。
  • 用CGlib生成 代理类不需要接口。
  • 用CGLib生成的代理类重写了父类的各个方法。
  • 拦截器中的intercept方法内容正好就是代理类中的方法体。

总结

  • 代理分为静态代理和动态代理两种。
  • 静态代理,代理类需要自己编写代码写成。
  • 动态代理有jdk和cglib,代理类通过 Proxy.newInstance()或者ASM 生成。
  • 静态代理和动态代理的区别是在于要不要开发者自己定义 Proxy 类。
    动态代理通过 Proxy 动态生成 proxy class,但是它也指定了一个 InvocationHandler 或者 MethodInterceptor的实现类。
  • 代理模式本质上的目的是为了增强现有代码的功能。

结束

  • 由于自己才疏学浅,难免会有纰漏,假如你发现了错误的地方,还望留言给我指出来,我会对其加以修正。
  • 如果你觉得文章还不错,你的转发、分享、赞赏、点赞、留言就是对我最大的鼓励。
  • 感谢您的阅读,十分欢迎并感谢您的关注。
    在这里插入图片描述

参考
https://blog.csdn.net/m0_37314675/article/details/77850967
https://www.cnblogs.com/cC-Zhou/p/9525638.html
https://www.jianshu.com/p/4539e6d9f337

目录
相关文章
|
2月前
|
存储 关系型数据库 MySQL
阿里面试:为什么要索引?什么是MySQL索引?底层结构是什么?
尼恩是一位资深架构师,他在自己的读者交流群中分享了关于MySQL索引的重要知识点。索引是帮助MySQL高效获取数据的数据结构,主要作用包括显著提升查询速度、降低磁盘I/O次数、优化排序与分组操作以及提升复杂查询的性能。MySQL支持多种索引类型,如主键索引、唯一索引、普通索引、全文索引和空间数据索引。索引的底层数据结构主要是B+树,它能够有效支持范围查询和顺序遍历,同时保持高效的插入、删除和查找性能。尼恩还强调了索引的优缺点,并提供了多个面试题及其解答,帮助读者在面试中脱颖而出。相关资料可在公众号【技术自由圈】获取。
|
9天前
|
存储 NoSQL 架构师
阿里面试:聊聊 CAP 定理?哪些中间件是AP?为什么?
本文深入探讨了分布式系统中的“不可能三角”——CAP定理,即一致性(C)、可用性(A)和分区容错性(P)三者无法兼得。通过实例分析了不同场景下如何权衡CAP,并介绍了几种典型分布式中间件的CAP策略,强调了理解CAP定理对于架构设计的重要性。
36 4
|
27天前
|
存储 NoSQL 算法
阿里面试:亿级 redis 排行榜,如何设计?
本文由40岁老架构师尼恩撰写,针对近期读者在一线互联网企业面试中遇到的高频面试题进行系统化梳理,如使用ZSET排序统计、亿级用户排行榜设计等。文章详细介绍了Redis的四大统计(基数统计、二值统计、排序统计、聚合统计)原理和应用场景,重点讲解了Redis有序集合(Sorted Set)的使用方法和命令,以及如何设计社交点赞系统和游戏玩家排行榜。此外,还探讨了超高并发下Redis热key分治原理、亿级用户排行榜的范围分片设计、Redis Cluster集群持久化方式等内容。文章最后提供了大量面试真题和解决方案,帮助读者提升技术实力,顺利通过面试。
|
1月前
|
SQL 关系型数据库 MySQL
阿里面试:1000万级大表, 如何 加索引?
45岁老架构师尼恩在其读者交流群中分享了如何在生产环境中给大表加索引的方法。文章详细介绍了两种索引构建方式:在线模式(Online DDL)和离线模式(Offline DDL),并深入探讨了 MySQL 5.6.7 之前的“影子策略”和 pt-online-schema-change 方案,以及 MySQL 5.6.7 之后的内部 Online DDL 特性。通过这些方法,可以有效地减少 DDL 操作对业务的影响,确保数据的一致性和完整性。尼恩还提供了大量面试题和解决方案,帮助读者在面试中充分展示技术实力。
|
2月前
|
消息中间件 架构师 Java
阿里面试:秒杀的分布式事务, 是如何设计的?
在40岁老架构师尼恩的读者交流群中,近期有小伙伴在面试阿里、滴滴、极兔等一线互联网企业时,遇到了许多关于分布式事务的重要面试题。为了帮助大家更好地应对这些面试题,尼恩进行了系统化的梳理,详细介绍了Seata和RocketMQ事务消息的结合,以及如何实现强弱结合型事务。文章还提供了分布式事务的标准面试答案,并推荐了《尼恩Java面试宝典PDF》等资源,帮助大家在面试中脱颖而出。
|
2月前
|
SQL 关系型数据库 MySQL
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
尼恩,一位40岁的资深架构师,通过其丰富的经验和深厚的技術功底,为众多读者提供了宝贵的面试指导和技术分享。在他的读者交流群中,许多小伙伴获得了来自一线互联网企业的面试机会,并成功应对了诸如事务ACID特性实现、MVCC等相关面试题。尼恩特别整理了这些常见面试题的系统化解答,形成了《MVCC 学习圣经:一次穿透MYSQL MVCC》PDF文档,旨在帮助大家在面试中展示出扎实的技术功底,提高面试成功率。此外,他还编写了《尼恩Java面试宝典》等资料,涵盖了大量面试题和答案,帮助读者全面提升技术面试的表现。这些资料不仅内容详实,而且持续更新,是求职者备战技术面试的宝贵资源。
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
|
2月前
|
Kubernetes 架构师 算法
阿里面试:全国14亿人,统计出重名最多的前100个姓名
文章介绍了如何解决“从全国14亿人的数据中统计出重名人数最多的前100位姓名”的面试题,详细分析了多种数据结构的优缺点,最终推荐使用前缀树(Trie)+小顶堆的组合。文章还提供了具体的Java代码实现,并讨论了在内存受限情况下的解决方案,强调了TOP N问题的典型解题思路。最后,鼓励读者通过系统化学习《尼恩Java面试宝典》提升面试技巧。
阿里面试:全国14亿人,统计出重名最多的前100个姓名
|
4月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
28天前
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
|
1月前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?