2018年Java面试题汇总(含答案)

 2024-01-15 00:01:53  阅读 0

和 之间的区别?

这已经在四种参考类型中进行了解释。 这里有一个简单的解释:

虽然两者都有利于提高GC和内存的效率,但是一旦最后一个强引用丢失,就会被GC回收。 虽然软引用不能阻止回收,但它们可以被延迟,直到 JVM 内存耗尽。

为什么有不同的参考类型?

与C语言不同的是,我们可以控制内存的申请和释放。 在Java中,有时我们需要适当控制对象被回收的时机,于是不同的引用类型就诞生了。 可以说,不同的引用类型实际上都是在为GC回收。 这是一种妥协,其时机是无法控制的。 下面的使用场景可以充分说明:

使用软引用和弱引用解决OOM问题:使用保存图像的路径和对应图像对象关联的软引用之间的映射关系。 当内存不足时,JVM会自动回收这些缓存的图像对象所占用的空间,从而有效避免OOM问题。

通过软引用实现Java对象的缓存:比如我们创建一个类,如果每次都需要查询一个人的信息,即使是几秒前才查询过,也必须重新构建一个实例,这会导致很多问题。 对象的消耗,而且由于这些对象的生命周期比较短,会导致多次GC影响性能。 此时可以通过软引用和缓存相结合的方式构建缓存来提高性能。

java中==和()的区别,()和`的区别

==是一个运算符,用于比较两个变量是否相等,而是一个类方法,用于比较两个对象是否相等。 默认类的方法是比较两个对象的地址,和==的结果是一样的。 换句话说:使用 == 来比较基本类型,并比较它们的值。 默认情况下,当使用 == 比较对象时,会比较内存地址。 如果需要比较对象内容,则需要重写 equal 方法。

()和()之间的联系

() 是返回哈希值的类的方法。 如果两个对象根据 equal() 方法比较相等,则对任一对象调用 () 方法必须产生相同的哈希值。

如果两个对象根据 equal() 方法不相等,则得到的哈希值不一定相等(在碰撞的情况下它们仍然会相等。)

a.()有什么用? 它与a.(b)有什么关系

()方法是对应对象的整型类型的哈希值。 常用于基于哈希的集合类,如,,等,与()方法关系特别密切。 根据Java规范,使用equal()方法判断相等的两个对象必须具有相同的值。

将对象放入集合时,首先判断集合中是否已经存在要放入的对象。 如果不存在,则直接放入集合中。 如果相等,则使用equal()方法判断要放入的对象是否与集合中的任意对象相等:如果equal()判断不相等,则直接将该元素放入集合中,否则未放置。

两个不相等的物体有可能有相同的吗

有可能,两个不相等的对象可能具有相同的值,这就是 . 如果两个对象相等,则它们必须具有相同的值,但反之则不然。

我可以在 中使用随机数吗?

不可以,因为同一个对象的值必须是相同的

a==b 和 a.(b) 有什么区别

如果a和b都是对象,则a==b比较两个对象的引用。 只有当a和b指向堆中的同一个对象,且a.(b)进行逻辑比较时,才会返回True。 ,因此通常需要重写此方法以提供逻辑上一致的比较。 例如,该类重写 () 方法,因此它可用于比较包含相同字母的两个不同对象。

3*0.1==0.3 返回值是多少?

false,因为某些浮点数无法完全准确地表示。

a=a+b 和 a+=b 有什么区别?

+= 运算符将执行隐式自动类型转换。 这里a+=b会隐式强制加法运算的结果类型为保存结果的类型,而a=a+b不会自动进行类型转换。 喜欢:

字节a=127;

字节 b = 127;

b = a + b; // 错误:从 int 到 byte

b+=a; // 好的

(译者注:这个地方应该是表述错误,实际上,无论a+b的值是什么,编译器都会报错,因为a+b操作会将a和b提升为int类型,所以int类型分配给byte会出现编译错误)

短s1=1; s1 = s1 + 1; 这段代码有错误吗? 如果是这样,我该如何纠正?

有错误。 Short类型在进行运算时会自动提升为int类型,也就是说s1+1的运算结果是int类型。

短s1=1; s1+=1; 这段代码有错误吗? 如果是这样,我该如何纠正?

+= 运算符会自动强制右侧的表达式结果与左侧的数据类型匹配,所以没错。

& 和 && 之间的区别

首先记住&是按位运算,而&&是逻辑运算符。 另请记住,逻辑运算符具有短路属性,而 & 则没有。

public class Test{
   static String name;
   public static void main(String[] args){
       if(name !=null & name.equals("")){
           System.out.println("ok");
       }else{
           System.out.println("erro");
       }
   }
}

上面的代码会抛出空指针异常。

java文件里面可以有类吗? (非内部类)

公共类只能有一个,但修饰类可以有多个。

如何正确退出多层嵌套循环?

使用标签和中断;

通过在外循环中添加标识符

public class BreaklFor {  
    public static void main(String args[]){  
        OK:                    //设置一个标记 使用带此标记的break语句跳出多重循环体  
        for(int i=1;i<100;i++){   //让i循环99次  
            for(int j=1;j<=i;j++){  
                if(i==10){  
                    break OK ;  
                }  
                System.out.print(i + "*" + j + "=" + i*j) ;  
                System.out.print(" ") ;  
            }  
            System.out.println() ;  
        }  
    }  
}  

内部类的作用

一个内部类可以有多个实例,每个实例都有自己的状态信息,并且独立于其他外围对象的信息。 在单个外围类中,多个内部类可以以不同的方式实现同​​一个接口,或者继承同一个类。 内部类对象创建的时刻并不依赖于外部类对象的创建。 内部类没有令人困惑的“is-a”系统,它们就像独立的实体。

内部类提供了更好的封装性,除了外部类之外,其他类无法访问内部类。

最终和最终的区别

Final是一个修饰符,可以修改变量、方法和类。 如果final修改了一个变量,则意味着该变量的值在初始化后就不能再改变了。 该方法是在对象被回收之前调用的方法,给对象本身最后一次复活的机会,但不能保证什么时候会被调用。 是与try和catch一起使用的关键字,用于异常处理。 无论try块中是否发生异常,该块都会被执行。

哪个类方法是clone()?

java.lang. 是一个标志性接口,不包含任何方法。 类中定义了clone方法。 并且你需要知道clone()方法是一个本地方法,这意味着它是由c或c++或其他本地语言实现的。

深拷贝和浅拷贝有什么区别?

浅复制:复制对象的所有变量都包含与原始对象相同的值,并且所有对其他对象的引用仍然指向原始对象。 换句话说,浅复制仅复制有问题的对象,而不复制它所引用的对象。

深拷贝:复制的对象的所有变量都包含与原始对象相同的值,并且那些引用其他对象的变量将指向复制的新对象,而不是原来引用的对象。 换句话说,深拷贝复制了要复制的对象所引用的所有对象。

有什么用途?

几乎每个人都知道关键字的两个基本用途:静态变量和静态方法。 也就是说,修改的变量/方法属于类的静态资源,被类实例共享。

除了静态变量和静态方法外,还用在静态块中,多用于初始化操作:

public calss PreCache{
   static{
       //执行相关操作
   }
}

另外,也常用于修饰内部类,称为静态内部类。

最后一个用法是静态导入包,这是JDK 1.5之后引入的新特性。 它可以用来指定导入到某个类中的静态资源,而无需使用类名。 资源名称,可以直接使用资源名称,如:

import static java.lang.Math.*;
public class Test{
   public static void main(String[] args){
       //System.out.println(Math.sin(20));传统做法
       System.out.println(sin(20));
   }
}

最终的用途是什么

Final也是很多面试喜欢问的地方。 如果您能回答以下三点就更好了:

1.被final修饰的类不能被继承

2.被final修饰的方法不能被重写

3.被final修饰的变量不能被改变。 如果修改引用,则意味着该引用是不可变的,而该引用指向的内容是可变的。

4、对于final修饰的方法,JVM会尝试内联它们以提高运行效率。

5、final修饰的常量在编译阶段会被存放到常量池中。

最好回答编译器对于最终字段必须遵循的两个重新排序规则:

1. 写入构造函数中的 Final 字段并随后将构造对象的引用分配给引用变量不能在这两个操作之间重新排序。

2. 对包含final 字段的对象的引用的第一次读取以及随后对final 字段的第一次读取不能在两个操作之间重新排序。

数据类型相关 Java中int char 和 long 占用多少字节? 类型字节数

短的

16

整数

32

长的

64

漂浮

32

64

字符

16

在64位JVM中,int的长度是多少?

在Java中,int类型变量的长度是固定值,与平台无关,并且始终为32位。 这意味着int类型的长度在32位和64位Java虚拟机中是相同的。

int 和 int 之间的区别

它是int的包装类型。 拆箱和装箱时,两者会自动转换。 Int是基本类型,直接存储数值。 它是一个对象,并且有一个引用指向这个对象。

int 和 int 哪个占用内存更多?

对象会占用更多内存。 是一个对象,需要存储该对象的元数据。 但int是一种原始数据类型,因此占用的空间较少。

, 和 差异

是字符串常量,final修饰:字符串变量(线程安全);

字符串变量(线程不安全)。

主要区别在于性能:它是一个不可变的对象。 对类型的每一次操作都相当于生成一个新的对象,然后指向这个新的对象。 因此,尽量不要对pair进行大量的拼接操作,否则会产生很多临时对象,导致GC开始工作,影响系统性能。

它对对象本身进行操作,而不是生成新的对象,因此我们建议在拼接较多的情况下使用它。

不过需要注意的是,JVM现在会对拼接进行一定的优化:

s=“This is only ”+””+”test”会被虚拟机直接优化成s=“This is only test”,此时不会有拼接过程。

是一个线程安全的可变字符串,其内部实现是一个可变数组。 这是jdk 1.5中新增的。 其功能与jdk 1.5类似,但不是线程安全的。 因此,如果不存在多线程问题,将会获得更好的性能。

什么是编译器常量? 使用它有什么风险?

公共静态不可变(最终)变量就是我们所说的编译时常量,它们在这里是可选的。 这些变量实际上是在编译时被替换的,因为编译器知道这些变量的值,并且知道这些变量在运行时不能改变。 这种方法的一个问题是,您在内部或第三方库中使用公共编译时常量,但该值后来被其他人更改,但您的客户端仍在使用旧值,即使您已经部署了一个新罐子。 为了避免这种情况,请确保在更新相关 JAR 文件时重新编译程序。

java中用什么类型来表达价格比较好?

如果内存和性能不是一个大问题,则使用,否则使用预定义的精度类型。

如何将字节转换为

您可以使用接收 byte[] 参数的构造函数来执行转换。 需要注意的是,必须使用正确的编码,否则将使用平台默认的编码。 该编码可以与原始编码相同,也可以不同。

int可以强制转换为byte类型吗? 会出现什么问题?

我们可以进行强制转换,但是Java中的int是32位,byte是8位。 因此,如果强制转换int类型的高24位,则byte类型的范围是-128到128。

关于垃圾回收你知道哪些垃圾回收算法?

垃圾收集在理论上很容易理解。 具体方法如下:

1. 清除标记

2. 标记-复制

3. 标记-组织

4. 世代回收

更多详情请参见深入理解垃圾收集算法:

如何判断一个对象是否应该被回收

这就是所谓的对象生存性判断。 常用的方法有两种: 1、引用计数法; 2. 对象可达性分析。 由于引用计数方式存在相互引用导致GC无法执行的问题,目前JVM虚拟机大多采用对象可达性分析算法。

简单解释一下垃圾收集

Java垃圾收集机制最基本的方法是分代收集。 内存中的区域被划分为不同的世代,对象根据其生存时间存储在相应世代的区域中。 一般实现分为3代:年轻代、老年代、永久代。 内存分配发生在年轻代。 当一个对象存活的时间足够长时,它就会被复制到老年代。 不同的垃圾收集算法可以用于不同的代。 世代划分的出发点是研究应用中对象的生存时间后得到的统计规律。 一般来说,应用程序中的大多数对象的生命周期都很短。 例如,局部变量的生存时间仅在方法执行期间。 基于此,年轻代的垃圾回收算法就可以非常具有针对性。

调用 .gc() 时会发生什么?

通知GC开始工作,但是GC真正开始的时间不确定。

下面说一下进程和线程相关的进程、线程和协程的区别。

简而言之,进程是程序运行和资源分配的基本单位。 一个程序至少有一个进程,一个进程至少有一个线程。 进程在执行时拥有独立的内存单元,多个线程共享内存资源,减少了切换次数,效率更高。 线程是进程的实体,是CPU调度和分派的基本单位,是比程序更小的、可以独立运行的基本单位。 同一进程中的多个线程可以并发执行。

你了解守护线程吗?它和非守护线程有什么区别?

程序运行完毕后,jvm会等待非守护线程完成然后关闭,但jvm不会等待守护线程。 守护线程最典型的例子是GC线程。

什么是多线程上下文切换

多线程上下文切换是指将CPU控制权从一个已经运行的线程切换到另一个准备好等待获取CPU执行权的线程的过程。

如何创建两个线程? 他们有什么区别?

通过实现java.lang。 或者通过扩展 java.lang。 班级。 实现一个接口可能比扩展它更好。 有两个原因:

Java不支持多重继承。 因此,扩展一个类意味着这个子类不能扩展其他类。 实现接口的类也可以扩展另一个类。

一个类可能只需要可执行,因此继承整个类的成本太高。

类中的 start() 和 run() 方法有什么区别?

start()方法用于启动一个新创建的线程,start()内部调用了run()方法,与直接调用run()方法有不同的效果。 当你调用run()方法时,它只会在原来的线程中被调用。 如果没有启动新线程,start()方法将启动一个新线程。

如何检测线程是否持有对象监视器

该类提供了 (obj) 方法,当且仅当对象 obj 的监视器由线程持有时,该方法才会返回 true。 注意,这是一个方法,也就是说“线程”指的是当前线程。 。

和...之间的不同

接口中run()方法的返回值为void,其作用纯粹是执行run()方法中的代码; 接口中的call()方法有返回值,并且是泛型类型,配合可以获取异步执行的结果。

这其实是一个非常有用的特性,因为多线程比单线程更困难、更复杂的一个重要原因就是因为多线程充满了未知数。 某个线程是否已执行? 一个线程执行了多长时间? 当线程执行的时候,我们期望的数据已经赋值了吗? 没有办法知道,我们所能做的就是等待这个多线程任务完成。 但+/可以轻松获取多线程操作的结果,如果等待时间过长,没有获取到需要的数据,可以取消线程的任务。

线程阻塞的原因是什么

阻塞是指暂停线程的执行以等待特定条件的发生(例如资源准备就绪)。 学过操作系统的同学一定很熟悉。 Java提供了大量的方法来支持阻塞。 我们来一一分析。

方法说明

睡觉()

sleep() 允许您指定一段以毫秒为单位的时间作为参数。 它导致线程在指定时间内进入阻塞状态,无法获得CPU时间。 一旦过了指定的时间,线程就重新进入可执行状态。通常,sleep()用于等待资源准备好时:测试发现条件不满足后,让线程阻塞一段时间时间后重新测试,直至满足条件。

() 和()

当两个方法一起使用时,()导致线程进入阻塞状态,并且不会自动恢复。 必须调用其相应的()才能使线程重新进入可执行状态。 通常,()和()用于等待另一个线程产生结果时:测试后发现结果还没有产生,则线程被阻塞,等另一个线程产生结果后,调用()恢复它。

屈服()

Yield() 使当前线程放弃当前已分配的CPU时间,但不会导致当前线程阻塞,即线程仍处于可执行状态,随时可能再次分配CPU时间。调用yield()的效果相当于调度程序认为该线程已经执行了足够的时间并切换到另一个线程。

等待()和()

这两种方法一起使用。 wait() 使线程进入阻塞状态。 它有两种形式。 一种允许指定以毫秒为单位的时间段作为参数,另一种则没有参数。 前者用于当调用相应的()或超过指定时间时,线程重新进入可执行状态,必须调用相应的()。

wait(),() 和 (),() 之间的区别

乍一看它们与 () 和 () 方法对没有什么不同,但实际上它们完全不同。 核心区别在于,上面描述的所有方法在阻塞时都不会释放占用的锁(如果被占用的话),而这个相反的规则则相反。 上述核心差异导致了一系列细节差异。

首先,上面介绍的所有方法都属于类,但这对是直接从属于类的,即所有对象都有这对方法。 这乍一看似乎不可思议,但实际上这是很自然的,因为当这对方法阻塞时,所占用的锁必须被释放,而该锁被任何对象占有。 调用任何对象的 wait() 方法都会导致线程阻塞。 并且对象上的锁被释放。 调用任何对象的wait()方法都会导致随机选择的一个线程通过调用该对象的wait()方法而解除阻塞(但直到获得锁后才会执行)。

其次,上面描述的所有方法都可以在任何位置调用,但这对方法必须在一个方法或块中调用。 原因也很简单。 只有在方法或者块中当前线程占用了锁,才可以释放锁。 。 同理,调用这对方法的对象上的锁也必须是当前线程拥有的,这样才能释放锁。 因此,这对方法调用必须放置在其锁定对象是调用这对方法的对象的方法或块中。 如果不满足这个条件,程序仍然可以编译,但运行时会出现异常。

wait()和()方法的上述特点决定了它们经常与关键字一起使用。 如果将它们与操作系统的进程间通信机制进行比较,就会发现它们的相似之处:方法或块提供了类似于操作系统原有语言的功能,它们的执行不会受到多线程的干扰机制,这对方法相当于块和原语(这对方法声明为)。 它们的结合使得我们可以在操作系统上实现一系列精巧的进程间通信算法(如信号量算法),并可以用来解决各种复杂的线程间通信问题。

关于 wait() 和 () 方法的最后两点:

第一:调用()方法解除阻塞的线程是从调用对象的wait()方法阻塞的线程中随机选择的。 我们无法预测哪个线程会被选择,所以编程时要特别小心。 避免因这种不确定性而产生的问题。

第二:除了()之外,还有一个方法()也可以起到类似的作用。 唯一的区别是,调用 () 方法会一次性解除所有通过调用对象的 wait() 方法而阻塞的线程。 。 当然,只有获得锁的线程才能进入可执行状态。

说到阻塞,就不得不说到死锁。 简单分析一下,调用()方法和wait()方法而不指定超时时间可能会导致死锁。 不幸的是,Java不支持语言层面的死锁避免,我们在编程中必须小心避免死锁。

上面我们分析了Java中线程阻塞的各种方法。 我们重点关注 wait() 和 () 方法,因为它们功能最强大,使用起来也最灵活,但这也导致它们效率较低且更容易出错。 在实际使用中,我们应该灵活运用各种方法,才能更好地达到我们的目的。

产生死锁的条件

1、互斥条件:一种资源一次只能被一个进程使用。

2.请求和保持条件:当一个进程因请求资源而被阻塞时,它将保持所获得的资源。

3、不剥夺条件:进程已经获得的资源在用完之前不能被强行剥夺。

4、循环等待情况:几个进程之间形成头尾循环等待资源关系。

为什么在同步块中调用 wait() 方法和 ()/() 方法?

这是 JDK 强制执行的。 wait()方法和()/()方法在调用之前都必须获得对象的锁。

wait()方法和()/()方法在放弃对象监视器时有什么区别?

wait()方法与()/()方法在放弃对象监视器时的区别在于,wait()方法立即释放对象监视器,而()/()方法则等待对象监视器的剩余代码线程在放弃对象之前完成执行。 监视器。

wait() 和 sleep() 之间的区别

这两个上面已经详细解释了,这里总结一下:

为什么wait,这些方法没有放在类中呢?

一个明显的原因是JAVA提供的锁是在对象级别而不是线程级别。 每个对象都有一个锁,它是通过线程获得的。 如果线程需要等待某个锁,那么调用对象中的 wait() 方法是有意义的。 如果类中定义了 wait() 方法,则线程正在等待哪个锁是不明显的。 简单来说,由于wait、and都是锁级别的操作,所以定义在类中是因为锁属于对象。

如何唤醒被阻塞的线程

如果线程由于调用wait()、sleep()或join()方法而被阻塞,可以中断线程并通过抛出来唤醒它; 如果线程遇到IO阻塞,你无能为力,因为IO是由操作系统实现的。 ,Java代码没有办法直接联系操作系统。

什么是多线程上下文切换

多线程上下文切换是指将CPU控制权从一个已经运行的线程切换到另一个准备好等待获取CPU执行权的线程的过程。

和...之间的不同

它与 if、else、for、while 是相同的关键字,并且它是一个类。 这是两者的本质区别。 由于它是一个类,因此它提供了比它所能提供的更多、更灵活的功能。 它可以被继承,可以有方法,并且可以有各种类变量。 其可扩展性体现在以下几点:

(1)可以设置获取锁的等待时间,避免死锁。

(2)可以获得各种锁的信息

(3)可灵活实现多种通知

另外,两者的加锁机制其实也不一样:底层调用park方法加锁,操作的应该是对象头中的mark字。

什么是

这个其实之前也提到过,代表的是一个异步操作任务。 可以传入具体的实现类,可以进行等待获取该异步操作任务的结果、判断是否已经完成、取消任务等操作。 当然,由于也是接口的实现类,所以也可以放入线程池中。

如果线程遇到运行时异常怎么办?

如果没有捕获该异常,线程将停止执行。 另外重要的一点是:如果这个线程持有某个对象的监视器,那么该对象监视器将立即被释放。

Java中有哪几种锁?

自旋锁:JDK1.6之后默认开启自旋锁。 根据之前的观察,共享数据的锁定状态只会持续很短的一段时间。 这么短的时间来挂起和恢复线程是一种浪费,所以这里做了一个处理,让后面请求锁的线程等待一段时间。 等待一段时间,但不要放弃处理器执行时间,看看持有锁的线程是否可以快速释放它。 为了让线程等待,线程需要执行繁忙循环,即自旋操作。 jdk6之后,引入了自适应自旋锁,即等待时间不再固定,而是由同一锁上最后一次自旋时间和锁拥有者状态决定。

偏向锁:JDK1之后引入的一种锁优化。 目的是在没有争用的情况下消除数据同步原语。 进一步提高程序的运行性能。 偏向锁是有偏向的,也就是说锁会偏向第一个获得它的线程。 如果后续执行过程中该锁没有被其他线程获取,则持有偏向锁的线程将永远不需要再次执行。 同步。 偏向锁可以提高同步但无竞争的程序的性能,这意味着它并不总是有利于程序运行。 如果程序中大部分锁被多个不同的线程访问,那么偏向模式就显得多余了。 是的,在具体问题具体分析的前提下,可以考虑是否使用偏向锁。

轻量级锁:为了减少获取和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”,因此Java SE1.6中有四种锁状态,无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态,随着竞争会逐渐升级。 锁可以升级但不能降级,也就是说偏向锁升级为轻量级锁后不能降级为偏向锁。

如何在两个线程之间共享数据

只要在线程之间共享对象,然后通过wait//、await//唤醒等待即可。 例如,阻塞队列旨在在线程之间共享数据。

如何正确使用wait()? 使用 if 或 while?

wait()方法应该循环调用,因为当线程获得CPU并开始执行时,其他条件可能还没有满足,所以最好在处理之前先循环检查条件是否满足。 以下是使用 wait 和方法的标准代码段:

synchronized (obj) {
   while (condition does not hold)
     obj.wait(); // (Releases lock, and reacquires on wakeup)
     ... // Perform action appropriate to condition
}

什么是抖音()

抖音()是一个仅限于线程的变量。 它属于线程本身,不在多个线程之间共享。 Java提供了类来支持抖音(),这是一种实现线程安全的方式。 但在托管环境(例如 Web 服务器)中使用抖音 () 时要特别小心。 在这种情况下,工人线的生命周期比任何应用变量的生命周期更长。 完成工作后未发布任何()后,Java应用程序就有内存泄漏的风险。

什么功能?

简而言之,这是一种交换时间并在每种方面维持一个空间的方法。 通过隔离数据而不共享数据,自然不会存在线程安全问题。

生产者消费者模型的作用是什么?

(1)通过平衡生产商的生产能力和消费者的消费能力来提高整个系统的运行效率,这是生产者消费者模型的最重要作用。

(2)脱钩,这是生产者消费者模型的偶然函数。 解耦意味着生产者和消费者之间的联系更少。 连接越少,他们可以独立发展而不会彼此限制。

写一个制片人队列

它可以通过阻止队列或等待来实现。

使用阻止队列实施

//生产者
public class Producer implements Runnable{
   private final BlockingQueue queue;
   public Producer(BlockingQueue q){
       this.queue=q;
   }
   @Override
   public void run() {
       try {
           while (true){
               Thread.sleep(1000);//模拟耗时
               queue.put(produce());
           }
       }catch (InterruptedException e){
       }
   }
   private int produce() {
       int n=new Random().nextInt(10000);
       System.out.println("Thread:" + Thread.currentThread().getId() + " produce:" + n);
       return n;
   }
}
//消费者
public class Consumer implements Runnable {
   private final BlockingQueue queue;
   public Consumer(BlockingQueue q){
       this.queue=q;
   }
   @Override
   public void run() {
       while (true){
           try {
               Thread.sleep(2000);//模拟耗时
               consume(queue.take());
           }catch (InterruptedException e){
           }
       }
   }
   private void consume(Integer n) {
       System.out.println("Thread:" + Thread.currentThread().getId() + " consume:" + n);
   }
}
//测试
public class Main {
   public static void main(String[] args) {
       BlockingQueue queue=new ArrayBlockingQueue(100);
       Producer p=new Producer(queue);
       Consumer c1=new Consumer(queue);
       Consumer c2=new Consumer(queue);
       new Thread(p).start();
       new Thread(c1).start();
       new Thread(c2).start();
   }
}

使用等待 - 实现

该方法应该是最经典的,并且不会在此处解释。

提交任务时,如果线程池队列已满,会发生什么?

如果您使用的是无限的队列,那没关系。 您可以继续将任务添加到阻塞队列并等待执行,因为它几乎可以将其视为无限的队列,可以无限存储任务; 例如,如果您使用的是有限的队列,则将首先添加任务。 如果已满,则拒绝策略DLER将用于处理完整的任务。 默认值为。

为什么使用线程池

避免经常创建和破坏线程以实现线程对象的重复使用。 此外,使用线程池还可以灵活地控制该项目的并发次数。

Java中使用的线程调度算法是什么

先发制人。 线程消耗CPU后,操作系统将根据线程优先级,线程饥饿和其他数据计算总优先级,并将下一个时间切片分配给某个线程以进行执行。

.sleep(0)的功能是什么

由于Java使用先发制线计划算法,因此可能会遇到某个线程通常获得CPU控件。 为了允许一些优先级的线程获得CPU控件,您可以使用.sleep(0)手动触发操作系统分配时间切片的操作,这也是平衡CPU控制的操作。

什么是cas

CAS代表和交换,这意味着比较替代。 假设有三个操作数:内存值V,旧期望值A和要修改的值。被返回。 否则,什么都不是返回false。 当然,CAS必须与变量合作,以确保每次获得的变量是主内存中的最新值。 否则,旧的期望值A将永远是某个线程的不变值A。 只要CAS操作失败,只要某个线程就永远不会成功。

什么是乐观的锁定和悲观的锁定

乐观的锁:乐观的锁认为并非总是发生竞争,因此它不需要锁定锁。 它将作为原子操作进行比较并替换两个操作,以尝试修改内存中的变量。 如果失败,则意味着发生冲突,那么应该有相应的重试逻辑。

悲观的锁:悲观的锁认为竞争将永远发生,因此每次运营资源时,都会举行独家锁定。 就像无论如何,锁定锁后将直接操作资源。

什么是并发程度?

并发性是默认值为16的大小,这意味着多达16个线程可以同时运行。 这也是最大的优势。 在任何情况下,两个线程是否可以同时获得数据?

工作准则

JDK 1.7和JDK 1.8中的实施原则不同。

JDK 1.7:

是线程安全的,但是与线程安全的实现方式相比不同。 它锁定了哈希表结构并正在阻止。 当线程占据此锁时,其他线程必须阻止并等待其释放锁。 它使用单独的锁定方法。 它不会锁定整个哈希表,而是部分锁。 也就是说,当线程占据此本地锁时,它不会影响其他线程对哈希表其他部分的访问。

具体实现:内部有一个。

JDK 1.8

在JDK 8中,不再使用独立的锁,但是使用乐观的锁定CAS算法来实现同步问题,但其底层仍然是“ Array + List-> Red -Black Tree”的实现。

和差异

这两个类非常相似。 他们都在java.util下。 可以用来指示代码运行到一定点。 两者的区别在于:

Java线程安全中的++操作员是吗?

不是线程安全操作。 它涉及多个指令,例如读取变量值,将其递增,然后将其存储回存储器。 此过程可能涉及多个线程相交。

您对多线程开发的好实践是什么?

命名线程

最小化同步范围

优先使用

只要可能

比同步容器更喜欢并发容器。

考虑使用线程池

可以创建有关关键字的数组吗?

您可以在Java中创建键入的数组,但它只是对数组的引用,而不是整个数组。 如果将指向指向的数组更改为参考,则将受到保护,但是如果多个线程同时更改数组的元素,则标识符将不会扮演先前的保护角色。

可以制作非原子操作吗?

一个典型的示例是类中的一个长类型成员变量。 如果您知道会员变量将通过多个线程(例如计数器,价格等)访问,则最好将其设置为。 为什么? 因为长类型变量在Java中不是原子,因此需要将其分为两个步骤。 如果一个线程正在修改长变量的值,则另一个线程只能看到值的一半(第一个32 -bit)。 但是一种长或变量的读物是原子。

一种做法是修改长变量,以便可以根据原子类型的类型读写和写入。 它们宽64位,因此对这两种类型的读数分为两部分。 第一个32位读取是第一次读取,然后读取其余32位。 此过程不是原子,但是Java中型长或可变的读写是原子。 维修特征的另一个角色是提供内存屏障(),例如分布式框架中的应用程序。 简而言之,当您编写变量时,Java内存模型将插入写入(写),并且在阅读变量之前,您将插入读取障碍。 这意味着,当您编写域时,您可以确保任何线程都可以看到您编写的值。 同时,在写作之前,您还可以确保所有线程可见任何值更新,因为内存屏障将是其他线程。 其他记忆障碍也将是其他。 所有书面值均更新为缓存。

类型变量提供什么保证?

有两个主要功能:1。避免说明重新释放2.视觉保证。 例如,JVM或JIT将对语句进行排序以获得更好的性能,但是即使没有同步块,该类型变量也不会分配值。 将与其他语句进行分类。 保证线程的修改可以看到其他线程。 在某些情况下,还可以提供原子能,例如读取64位数据类型,例如长而不是原子(低32位和高32-位),但是类型和长的是原子。

关于Java收集的收集和继承关系

关于收集系统,每个人都应该熟悉心脏,尤其是对于我们经常使用的列表,地图的原理应该相同。 在这里,我们可以看到这张照片:

如本站内容信息有侵犯到您的权益请联系我们删除,谢谢!!


Copyright © 2020 All Rights Reserved 京ICP5741267-1号 统计代码