多线程面试题

同步机制应该遵循的基本准则 

· 空闲让进:当无进程处于临界区时,表明临界资源处于空闲状态,允许一个请求进入临界区的进程立即进入临界区,以有效利用临界资源

 · 忙则等待:当已有进程处于临界区时,表明临界资源正在被访问,因而其他试图进入临界区的进程必须等待,以保证对临界资源的互斥访问

 · 有限等待:对要求访问临界资源的进程,应保证在有限时间内能进入自己的临界区,以免陷入“死等”状态

 · 让权等待:当进程不能进入自己的临界区时,应立即释放处理机,以免进程陷入“忙等”状态

 

产生死锁的原因主要是: 
(1) 因为系统资源不足。 
(2) 进程运行推进的顺序不合适。 
(3) 资源分配不当等。
 
产生死锁的四个必要条件: 
(1) 互斥条件:一个资源每次只能被一个进程使用。 
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。 
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
 
共享内存并未提供同步机制,当某一个进程对共享内存提供写操作时,并未自动的阻止另一个进程对它进行读取;
共享内存:就是允许多个进程访问同一内存空间,进程间传递和共享数据非常有效
信号量:为了防止多个进程访问共享资源而造成冲突,设置临界区域每次只让一个进程访问,信号量提供了这样一种机制,用来控制对临界区域的访问
 
 
进程间通信的方式有管道(pipe)、共享存储器系统、消息传递系统(message queue)以及信号量。而mutex是互斥锁,在锁机制中通过原语保证资源状态的检查和修改作为一个整体来执行,以保证共享数据操作的完整性,并不能在两个进程间传递消息。网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket,也就是说socket也是两个进程间的通信方式。
 
 
进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
线程的划分尺度小于进程,使得多线程程序的并发性高
在同一进程中的各个线程,才可以共享该进程所拥有的资源
 
多线程调用时要进行保护时,主要是针对全局变量和静态变量的,函数内的局部变量不会受到影响。
这里i是全局变量,j是局部静态变量,所以 要进行保护。
 
——————————————————————————————————————————————————————————————

1、线程与进程的区别?

进程是操作系统分配资源的最小单元,线程是操作系统调度的最小单元。

一个程序至少有一个进程,一个进程至少有一个线程。

 

 

2、什么是多线程中的上下文切换?

多线程会共同使用一组计算机上的CPU,而线程数大于给程序分配的CPU数量时,为了让各个线程都有执行的机会,就需要轮转使用CPU。不同的线程切换使用CPU发生的切换数据等就是上下文切换。

 

 

3、多线程同步和互斥有几种实现方法,都是什么?

线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。

线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步。

线程间的同步方法大体可分为两类:用户模式和内核模式。顾名思义,内核模式就是指利用系统内核对象的单一性来进行同步,使用时需要切换内核态与用户态,而用户模式就是不需要切换到内核态,只在用户态完成操作。

用户模式下的方法有:原子操作(例如一个单一的全局变量),临界区。内核模式下的方法有:事件,信号量,互斥量。

 

21、java如何实现多线程之间的通讯和协作?

中断和共享变量

 

23、volatile有什么用?能否用一句话说明下volatile的应用场景?

volatile保证内存可见性和禁止指令重排。

volatile用于多线程环境下的单次操作(单次读或者单次写)。

 

 

25、在java中wait和sleep方法的不同?

最大的不同是在等待时wait会释放锁,而sleep一直持有锁。Wait通常被用于线程间交互,sleep通常被用于暂停执行。

直接了解的深入一点吧:

 

 

 

在Java中线程的状态一共被分成6种:

初始态:NEW

创建一个Thread对象,但还未调用start()启动线程时,线程处于初始态。

运行态:RUNNABLE

在Java中,运行态包括就绪态和运行态。

就绪态该状态下的线程已经获得执行所需的所有资源,只要CPU分配执行权就能运行。所有就绪态的线程存放在就绪队列中。

运行态获得CPU执行权,正在执行的线程。由于一个CPU同一时刻只能执行一条线程,因此每个CPU每个时刻只有一条运行态的线程。

阻塞态

当一条正在执行的线程请求某一资源失败时,就会进入阻塞态。而在Java中,阻塞态专指请求锁失败时进入的状态。由一个阻塞队列存放所有阻塞态的线程。处于阻塞态的线程会不断请求资源,一旦请求成功,就会进入就绪队列,等待执行。PS:锁、IO、Socket等都资源。

等待态

当前线程中调用wait、join、park函数时,当前线程就会进入等待态。也有一个等待队列存放所有等待态的线程。线程处于等待态表示它需要等待其他线程的指示才能继续运行。进入等待态的线程会释放CPU执行权,并释放资源(如:锁)

超时等待态

当运行中的线程调用sleep(time)、wait、join、parkNanos、parkUntil时,就会进入该状态;它和等待态一样,并不是因为请求不到资源,而是主动进入,并且进入后需要其他线程唤醒;进入该状态后释放CPU执行权 和 占有的资源。与等待态的区别:到了超时时间后自动进入阻塞队列,开始竞争锁。

终止态

线程执行结束后的状态。

注意:

wait()方法会释放CPU执行权 和 占有的锁。

sleep(long)方法仅释放CPU使用权,锁仍然占用;线程被放入超时等待队列,与yield相比,它会使线程较长时间得不到运行。

yield()方法仅释放CPU执行权,锁仍然占用,线程会被放入就绪队列,会在短时间内再次执行。

wait和notify必须配套使用,即必须使用同一把锁调用;

wait和notify必须放在一个同步块中调用wait和notify的对象必须是他们所处同步块的锁对象。

posted @ 2019-03-28 00:10  chenzquan  阅读(861)  评论(0编辑  收藏  举报