面试题15解析-守护线程 / JAVA多线程问题及其补充

来源:互联网 发布:vb控件随窗体变化 编辑:程序博客网 时间:2024/05/20 21:43

面试题15解析-守护线程

题目:说一下你对Daemon线程的理解?它有什么意义?一般应用于什么样的场景?

这个题目主要考查守护线程。

守护线程与用户线程

  • 用户线程:我们平常创建的普通线程。

  • 守护线程:用来服务于用户线程;不需要上层逻辑介入。

通过一个栗子来区分一下它们与JVM的关系。

class DaemonRunner implements Runnable { @Override public void run() { while (true) { for (int i = 1; i <= 100; i++) { System.out.println("daemon thread:"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }}Thread daemonThread = new Thread(new DaemonRunner());daemonThread.setDaemon(true);daemonThread.start();System.out.println("isDaemon? = " + daemonThread.isDaemon());Scanner scanner = new Scanner(System.in);scanner.next();Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { System.out.println("JVM Exit!"); }});

我们分析结果,可以得出结论:当线程只剩下守护线程的时候,JVM就会退出;补充一点如果还有其他的任意一个用户线程存在,JVM就不会退出。

使用它需要注意些什么?

  1. thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。

  2. 在Daemon线程中产生的新线程也是Daemon的。

  3. 守护线程不能用于去访问固有资源,比如读写操作或者计算逻辑。因为它会在任何时候甚至在一个操作的中间发生中断。

  4. Java自带的多线程框架,比如ExecutorService,会将守护线程转换为用户线程,所以如果要使用后台线程就不能用Java的线程池。

意义及应用场景

当主线程结束时,结束其余的子线程(守护线程)自动关闭,就免去了还要继续关闭子线程的麻烦。如:Java垃圾回收线程就是一个典型的守护线程;内存资源或者线程的管理,但是非守护线程也可以。

它的存在,必定有它的意义,我们只需在乎怎么把它用到恰到好处。

记得关注我们哦,这里全部都是干货!!!


===============================================================================================================


进程

运行中的应用程序叫进程,每个进程运行时,都有自已的地址空间(内存空间)

如IE浏览器在任务管器中可以看到

操作系统都是支持多进程的

JAVA多线程问题及其补充

线程

线程是轻量级的进程,是进程中一个负责程序执行的控制单元

线程没有独立的地址空间(内存空间)

线程是由进程创建的(寄生在进程中)

一个进程可以拥有多个线程,至少一个线程

线程有几种状态(新建new,就绪Runnable,运行Running,阻塞Blocked,死亡Dead)

开启多个线程是为了同时运行多部分代码,每个线程都有自已的运行的内容,这个内容可以称线程要执行的任务(放在run()方法中)

多线程

多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。

多线程的好处

Java支持编写多线程的程序;

多线程最大的好处在于可以同时并发执行多个任务;

多线程可以最大限度地减低CPU的闲置时间,从而提高CPU的利用率。

多线程的不利方面

线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;

多线程需要协调和管理,所以需要CPU时间跟踪线程;

线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题;

线程太多会导致控制太复杂,最终可能造成很多Bug。

Java创建线程的二种方式

第一种:继承Thread类的方式

步骤:1)创建一个类继承Thread

2)覆写run方法 目的是存放多线程要执行的自定义代码。

3)在main函数中创建该类

4)使用start()方1法调用该线程(start方法有两种含义:1,启动多线程。2,调用线程中的run方法)

第二种:实现Runnable接口的方式

步骤:

1)创建类实现Runnable接口

2)实现Runnable接口中的run方法

3)创建Thread对象

4)将Runnable对象作为实际参数传递给Thread的构造方法

5)调用Thread类的start方法,自动执行Runnable对象中的run方法

得到线程的名字:getName(),Thread的方法

当前执行的线程:currentThread()

设置线程的名字:setName("名字");

线程优先级:

Thread类有如下三个静态变量来表示优先级

MAX_PRIORITY:取值为10,表示最高优先级

MIN_PRIORITY:取值为1,表示最低优先级

NORM_PRIORITY:取值为5,表示默认的优先级

用数字设置优先级:setPriority(1);

用静态变量设置优先级:setPriority(MAX_PRIORITY);

获得优先级:getPriority();

JAVA多线程问题及其补充

线程安全问题产生的原因:

多个线程操作共享数据

共享数据的线程代码有多条

当一个线程在执行操作共享数据的多条代码过程中,其它线程参与了运算,就会导致线程的安全问题的产生

线程同步有两种方法:

1.同步语句块:只对这个区域块的资源实行互斥访问

synchronized(共享对象名){

被同步的代码段

}

它锁定的是共享对象名对应的当前对象,线程中实现同步块一般是在run方法中添加。

2.使用synchronized修饰的方法:

访问修饰符 synchronized 数据返回类型 方法名(){

...

}

它锁定的是调用这个同步方法的对象。其他线程不能同时访问这个对象中任何一个synchronized方法。

线程死锁的概念

指的是两个线程互相持有对方依赖的共享资源,造成无限阻塞。导致死锁的根源在于不适当的运用synchronized关键字来管理线程对特定对象的访问。

解决死锁的方法

让线程持有独立的资源。

尽量不采用嵌套的synchronized语句。

死锁要通过通过优良的设计来尽量降低死锁的发生。

线程通讯:指的是多个线程通过消息传递实现相互牵制,相互调度,即线程间的相互作用。

通过Object类的方法wait()--导致当前线程等待、notify()--唤醒等待的线程,和synchronized一起使用来达成效果

sleep():Thread的静态方法,必须指定时间。让本线程进入睡眠模式,到时间就醒。不会释放同步锁。

wait():Object的方法,可以指定也可以不指定时间。让本线程进入等待状态,需要有人叫醒notify()。会释放同步锁。

yield ( )

线程A名.yield( )

线程A让出cpu的使用权,让其它线程执行(不是绝对的)

注意:这二个线程必须是同一优先级的

join( )

join:哪个线程调用join()方法,哪个线程就先执行完,

1)一个线程A调用join()方法时,main线程后执行

2)二个线程A,B都调用join()方法时 ,A与B相互抢着执行,main线程最后执行

注意:join()方法要在start()之后写,才起作用

setDaemon()

JAVA多线程问题及其补充

守护线程 :

守护线程是守护主线程(main)的,主线程结束,不管守护线程当前是什么状态都随之结束

一个线程要调用 setDaemon(true);//参数是true时是守护线程

注意:setDaemon(true)方法必须在start()之前使用

C/S B/S 区别?

C/S (Client/Server):该结构软件,客户端服务器端都要编写,开发成本高,维护麻烦,好处是客户端在本地可以分担一部分运算

B/S (Browser/Server):该结构软件只开发服务端,不开发客户端,因为客户端直接由浏览器取代,开发成本低,维护更简单,缺点是所有的运算都在服务器端完成,加大了服务端的负担

String StringBuffer StringBuilder

对于三者使用的总结:

1.如果要操作少量的字符数据,或对字符串修改不多情况下用 String,最为适合

2.如果要操作大量的字符数据,或对字符串修改比较多的情况下用BufferBuilder 因为它带缓冲区,由于StringBuilder线程不全安,所以当我们项目用的是多线程时不要用BufferBuilder,StringBuilder的执行效率比较快

3.多线程操作字符串缓冲区下操作大量数据 = StringBuffer

clone()创建并返回此对象的一个副本。

可以克隆一个对象,即创建一个对象的副本,要使类的对象能够克隆,类必须实现Cloneable接口。

BigInteger类

位于Java.math包中

用于计算非常大的整数用的

BigInteger big1 = new BigInteger("12345676134896413132");

BigInteger big2 = new BigInteger("123");

BigInteger big3= big1.multiply(big2);

System.out.println(big3);

BigDecimal类

位于Java.math包中

用于计算非常大的浮点数用的

BigDecimal big1 = new BigDecimal("12345676134896413132.02");

BigDecimal big2 = new BigDecimal("123.36");

BigDecimal big3= big1.multiply(big2);

System.out.println(big3)

Collections和Collection的区别?

Collection:集合类的接口

Collections:操作集合用的类,这个类里有很多static的方法,这些方法大多对List操作,主要包括排序,重排,查找等。

Arrays类定义了对数组进行操作的方法,包括对数组进行排序查找。

Arrays类:

排序:sort():对数组中的内容进行升序排序

查找:binarySearch(数组):利用对数组中的内容进行查找(二分查找法),注意数组中的数值要有顺序(从小到大或从大到小)

Collections类:

排序:sort():对数组中的内容进行升序排序

查找:binarySerach(集合):利用对数组中的内容进行查找(二分查找法),注意数组中的数值要有顺序(从小到大或从大到小)

JAVA多线程问题及其补充

封装性:

封装性是面向对象的一个重要特征,在java中,对象就是一组变量和方法的封装,其中变量描述对象的状态,方法描述对象的行为。通过对象的封装,用户不必了解对象是如何实现的,只需要通过对象提供的接口与对象进行交互就可以了,封装性实现了模块化和信息隐藏,有利于程序的可移植性和对象的管理。

在Java中,对象的封装是通过如下2种方式实现的:

1)通过包实现封装,它定义了程序类的访问权限

2)通过类或类的成员的访问权限实现封装性。

Random

Random类,用于生成随机数。

位置于java.util包下

构造方法摘要

Random()创建一个新的随机数生成器。

Random(long seed)使用单个 long 种子创建一个新的随机数生成器。

方法:

int nextInt()返回下一个伪随机数,它是此随机数生成器的序列中均匀分布的 int 值。

int nextInt(int n)返回一个伪随机数,它是取自此随机数生成器序列的、在 0(包括)和指定值(不包括)之间均匀分布的 int 值。

long nextLong()返回下一个伪随机数,它是取自此随机数生成器序列的均匀分布的 long 值。

boolean nextBoolean()返回下一个伪随机数,它是取自此随机数生成器序列的均匀分布的 boolean 值。

void nextBytes(byte[] bytes)生成随机字节并将其置于用户提供的 byte 数组中。

double nextDouble()返回下一个伪随机数,它是取自此随机数生成器序列的、在 0.0 和 1.0 之间均匀分布的 double 值。

float nextFloat()返回下一个伪随机数,它是取自此随机数生成器序列的、在 0.0 和 1.0 之间均匀分布的 float 值。

堆(heap)和栈(stack)的区别

1)栈中存入局部变量,数组或对象的引用变量,及方法

2)堆存放着new 创建的对象(及全局变量)和数组

3)栈中的数据,超出作用域后,会自动清除

4)堆中的数据,超出作用域后,不会自动清除,由垃圾回收器不定时回收

5)栈先进后出,堆先进先出


更多干货文章点击优就业官网:www.ujiuye.com/?wt.bd=qzb36000

就业促进计划,我们出钱帮你实现梦想:www.ujiuye.com/zt/jycj/?wt.bd=qzb36000

Java技术实战交流群,权威老师答疑:529655344