Java常见面试题整理【2】

来源:互联网 发布:网络钟点工兼职 编辑:程序博客网 时间:2024/06/08 12:23

9. 进程和线程的区别是什么?

首先程序是静态的指令集合,而进程是运行中的指令集合。 
进程是分配资源的基本单位,而线程是独立运行和调度的基本单位。
任意时刻,一个CPU只能运行一个进程,进程获得资源后进行分配,由不同的线程来执行和协作。
1. 进程:程序的一次执行 2. 线程:CPU的基本调度单位
一个进程的内存空间是可以被线程共享的。
进程的内存空间一般是独立的,而线程的内存空间一般是共享的,线程间的同步是为了防止竞争(因同时修改导致数据的不一致),所以要使用互斥锁,防止多个线程同时读写某一块内存区域。
还有的内存区域只允许固定个数的线程进入,就要使用信号量,防止线程之间产生冲突。
互斥锁止允许一个线程进入临界区,而信号量允许多个线程同时进入临界区。
在系统编程中,进程通信和线程同步内容以后有时间再查资料。

线程与进程的区别归纳:

a.地址空间和其它资源:进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。 b.通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。 c.调度和切换:线程上下文切换比进程上下文切换要快得多。 d.在多线程OS中,进程不是一个可执行的实体。

10. 创建线程有几种不同的方式?你喜欢哪一种?为什么?

①继承Thread类(真正意义上的线程类),是Runnable接口的实现。

②实现Runnable接口,并重写里面的run方法。

③使用Executor框架创建线程池。Executor框架是juc里提供的线程池的实现。

调用线程的start():启动此线程;调用相应的run()方法

继承于Thread类的线程类,可以直接调用start方法启动线程(使用static也可以实现资源共享).一个线程(对象)只能够执行一次start(),而且不能通过Thread实现类对象的run()去启动一个线程。

实现Runnable接口的类需要再次用Thread类包装后才能调用start方法。(三个Thread对象包装一个类对象,就实现了资源共享)。

线程的使用的话,注意锁和同步的使用。(多线程访问共享资源容易出现线程安全问题) 

一般情况下,常见的是第二种。

* Runnable接口有如下好处:

*①避免点继承的局限,一个类可以继承多个接口。

*②适合于资源的共享

/*

 * Thread的常用方法:

 * 1.start():启动线程并执行相应的run()方法

 * 2.run():子线程要执行的代码放入run()方法中

 * 3.currentThread():静态的,调取当前的线程

 * 4.getName():获取此线程的名字

 * 5.setName():设置此线程的名字

 * 6.yield():调用此方法的线程释放当前CPU的执行权(很可能自己再次抢到资源)

 * 7.join():在A线程中调用B线程的join()方法,表示:当执行到此方法,A线程停止执行,直至B线程执行完毕,

 * A线程再接着join()之后的代码执行

 * 8.isAlive():判断当前线程是否还存活

 * 9.sleep(long l):显式的让当前线程睡眠l毫秒  (只能捕获异常,因为父类run方法没有抛异常)

 * 10.线程通信(方法在Object类中):wait()   notify()  notifyAll()

 *

 *设置线程的优先级(非绝对,只是相对几率大些)

 * getPriority():返回线程优先值

 * setPriority(int newPriority):改变线程的优先级

 */

11.概括的解释下线程的几种可用状态。


1. 新建( new ):新创建了一个线程对象。
2. 可运行( runnable ):线程对象创建后,其他线程(比如 main 线程)调用了该对象 的 start ()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获 取 cpu 的使用权 
3. 运行( running ):可运行状态( runnable )的线程获得了 cpu 时间片( timeslice  ,执行程序代码。
4. 阻塞( block ):阻塞状态是指线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu timeslice ,暂时停止运行。直到线程进入可运行( runnable )状态,才有 机会再次获得 cpu timeslice 转到运行( running )状态。阻塞的情况分三种:
(一). 等待阻塞:运行( running )的线程执行 o . wait ()方法, JVM 会把该线程放 入等待队列( waitting queue )中。
(二). 同步阻塞:运行( running )的线程在获取对象的同步锁时,若该同步锁 被别的线程占用,则 JVM 会把该线程放入锁池( lock pool )中。
(三). 其他阻塞: 运行( running )的线程执行 Thread . sleep ( long ms )或 t . join ()方法,或者发出了 I / O 请求时, JVM 会把该线程置为阻塞状态。
 sleep ()状态超时、 join ()等待线程终止或者超时、或者 I / O 处理完毕时,线程重新转入可运行( runnable )状态。
5. 死亡( dead ):线程 run ()、 main () 方法执行结束,或者因异常退出了 run ()方法,则该线程结束生命周期。死亡的线程不可再次复生。

12.同步方法和同步代码块的区别是什么?

为何使用同步? 
java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(增删改查),将会导致数据的不准确,相互之间产生冲突。类似于在atm取钱,银行数据确没有变,这是不行的,要存在于一个事务中。因此加入了同步锁,以避免在该线程没有结束前,调用其他线程。从而保证了变量的唯一性,准确性。
1.同步方法:
即有synchronized    (同步, ['sɪŋkrənaɪzd] ) 修饰符修饰的方法。
由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用给方法前,要获取内置锁,否则处于阻塞状态。
例:public synchronized getMoney(){}
注:synchronized修饰静态方法,如果调用该静态方法,将锁住整个类。
2.同步代码块
即有synchronized修饰符修饰的语句块,被该关键词修饰的语句块,将加上内置锁。实现同步。
例:synchronized(Object o ){}
同步是高开销的操作,因此尽量减少同步的内容。通常没有必要同步整个方法,同步部分代码块即可。
同步方法默认用this或者当前类class对象作为锁。
同步代码块可以选择以什么来加锁,比同步方法要更颗粒化,我们可以选择只同步会发生问题的部分代码而不是整个方法。

13.在监视器(Monitor)内部,是如何做线程同步的?程序应该做哪种级别的同步?

在 java 虚拟机中, 每个对象( Object 和 class )通过某种逻辑关联监视器,每个监视器和一个对象引用相关联,为了实现监视器的互斥功能, 每个对象都关联着一把锁
一旦方法或者代码块被 synchronized 修饰, 那么这个部分就放入了监视器的监视区域, 确保一次只能有一个线程执行该部分的代码, 线程在获取锁之前不允许执行该部分的代码。

14. 什么是死锁(deadlock)?

死锁 :是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
原因:
(1) 因为系统资源不足。
(2) 资源分配不当等。
(3) 进程运行推进顺序不合适。
如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(3) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
死锁的解除与预防:
理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和解除死锁。所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确定资源的合理分配算法,避免进程永久占据系统资源。此外,也要防止进程在处于等待状态的情况下占用资源。因此,对资源的分配要给予合理的规划。
其中最简单的方法就是线程都是以同样的顺序加锁和释放锁,也就是破坏了第四个条件。


原创粉丝点击