-
多线程面试题
- 说明类 java.lang.ThreadLocal 的作用和原理。列举在哪些
程序中见过 ThreadLocal 的使用?
作用:
要编写一个多线程安全(Thread-safe)的程序是困难的,为了让线程共享资源,
必须小心地对共享资源进行同步,同步带来一定的效能延迟,而另一方面,
在处理同步的时候,又要注意对象的锁定与释放,避免产生死结,种种因素
都使得编写多线程程序变得困难。
尝试从另一个角度来思考多线程共享资源的问题,既然共享资源这么困难,
那么就干脆不要共享,何不为每个线程创造一个资源的复本。将每一个线程
存取数据的行为加以隔离,实现的方法就是给予每个线程一个特定空间来保
管该线程所独享的资源。
比如:在 Hibernate 中的 Session 就有使用。
ThreadLocal 的原理
ThreadLocal 是如何做到为每一个线程维护变量的副本的呢?其实实现的
思路很简单,在 ThreadLocal 类中有一个 Map,用于存储每一个线程的变
量的副本。
- 说说乐观锁与悲观锁
答:
悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时
候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这
个数据就会 block 直到它拿到锁。传统的关系型数据库里边就用到了很多这
种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时
候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此
期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多
读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于
write_condition 机制的其实都是提供的乐观锁。
两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较
少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大
了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行 retry,
这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。
- 在 Java 中怎么实现多线程?描述线程状态的变化过程。
答:当多个线程访问同一个数据时,容易出现线程安全问题,需要某种方式
来确保资源在某一时刻只被一个线程使用。需要让线程同步,保证数据安全
线程同步的实现方案:同步代码块和同步方法,均需要使用 synchronized
关键字
同步代码块:public void makeWithdrawal(int amt) {
synchronized (acct) { } }
同步方法:public synchronized void makeWithdrawal(int amt) { }
线程同步的好处:解决了线程安全问题
线程同步的缺点:性能下降,可能会带来死锁
- 请写出多线程代码使用 Thread 或者 Runnable,并说出两种
的区别。
方式 1:继承 Java.lang.Thread 类,并覆盖 run() 方法。优势:编写简单;
劣势:无法继承其它父类
public class ThreadDemo1 {
public static void main(String args[]) {
MyThread1 t = new MyThread1();
t.start();
while (true) {
System.out.println("兔子领先了,别骄傲");
} } }
class MyThread1 extends Thread {
public void run() {
while (true) {
System.out.println("乌龟领先了,加油");
} } }
方式 2:实现 Java.lang.Runnable 接口,并实现 run()方法。优势:可继承其
它类,多线程可共享同一个 Thread 对象;劣势:编程方式稍微复杂,如需访
问当前线程,需调用 Thread.currentThread()方法
public class ThreadDemo2 {
public static void main(String args[]) {
MyThread2 mt = new MyThread2();
Thread t = new Thread(mt);
t.start();
while (true) {
System.out.println("兔子领先了,加油");
} } }
class MyThread2 implements Runnable {
public void run() {
while (true) {
System.out.println("乌龟超过了,再接再厉");
} } }
- 在多线程编程里,wait 方法的调用方式是怎样的?
答:
wait 方法是线程通信的方法之一,必须用在 synchronized 方法或者
synchronized 代码块中,否则会抛出异常,这就涉及到一个“锁”的概念,
而 wait 方法必须使用上锁的对象来调用,从而持有该对象的锁进入线程等
待状态,直到使用该上锁的对象调用 notify 或者 notifyAll 方法来唤醒之前
进入等待的线程,以释放持有的锁。
- Java 线程的几种状态
答:
线程是一个动态执行的过程,它有一个从产生到死亡的过程,共五种状态:
尚学堂 Java 面试题大全及参考答案
新建(new Thread)
当创建 Thread 类的一个实例(对象)时,此线程进入新建状态(未被启动)
例如:Thread t1=new Thread();
就绪(runnable)
线程已经被启动,正在等待被分配给 CPU 时间片,也就是说此时线程正在
就绪队列中排队等候得到 CPU 资源。例如:t1.start();
运行(running)
线程获得 CPU 资源正在执行任务(run()方法),此时除非此线程自动放弃
CPU 资源或者有优先级更高的线程进入,线程将一直运行到结束。
死亡(dead)
当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能
再进入就绪状态等待执行。
自然终止:正常运行 run()方法后终止
异常终止:调用 stop()方法让一个线程终止运行
堵塞(blocked)
由于某种原因导致正在运行的线程让出 CPU 并暂停自己的执行,即进入堵
塞状态。
正在睡眠:用 sleep(long t) 方法可使线程进入睡眠方式。一个睡眠着的线
程在指定的时间过去可进入就绪状态。
正在等待:调用 wait()方法。(调用 motify()方法回到就绪状态)
被另一个线程所阻塞:调用 suspend()方法。(调用 resume()方法恢复)
- 在 Java 多线程中,请用下面哪种方式不会使线程进入阻塞状
态()
A sleep()
B. Suspend()
C. wait()
D. yield()
答案:D
分析:
yield 会是线程进入就绪状态
- volatile 关键字是否能保证线程安全?
答:
不能。虽然 volatile 提供了同步的机制,但是知识一种弱的同步机制,如需
要强线程安全,还需要使用 synchronized。
Java 语言提供了一种稍弱的同步机制,即 volatile 变量,用来确保将变量
的更新操作通知到其他线程。当把变量声明为 volatile 类型后,编译器与运行时
都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起
重排序。volatile 变量不会被缓存在寄存器或者对其他处理器不可见的地方,因
此在读取 volatile 类型的变量时总会返回最新写入的值。
一、volatile 的内存语义是:
当写一个 volatile 变量时,JMM 会把该线程对应的本地内存中的共享变量
值立即刷新到主内存中。
当读一个 volatile 变量时,JMM 会把该线程对应的本地内存设置为无效,
直接从主内存中读取共享变量。
二、volatile 底层的实现机制
如果把加入 volatile 关键字的代码和未加入 volatile 关键字的代码都生成汇
编代码,会发现加入 volatile 关键字的代码会多出一个 lock 前缀指令。
1 、重排序时不能把后面的指令重排序到内存屏障之前的位置
2、使得本 CPU 的 Cache 写入内存 3、写入动作也会引起别的 CPU 或者别的内核无效化其 Cache,相当于让
新写入的值对别的线程可见。
- 请写出常用的 Java 多线程启动方式,Executors 线程池有几
种常用类型?
(1) 继承 Thread 类 public class java_thread extends Thread{
public static void main(String args[]) {
new java_thread().run();
System.out.println("main thread run ");
}
public synchronized void run() {
System.out.println("sub thread run ");
}
}
(2) 实现 Runnable 接口
public class java_thread implements Runnable{
public static void main(String args[]) {
new Thread(new java_thread()).start();
System.out.println("main thread run ");
}
public void run() {
System.out.println("sub thread run ");
}
}在 Executor 框架下,利用 Executors 的静态方法可以创建三种类型的常用线程
池:1)FixedThreadPool 这个线程池可以创建固定线程数的线程池。
2)SingleThreadExecutor 是使用单个 worker 线程的 Executor。 3)CachedThreadPool 是一个”无限“容量的线程池,它会根据需要创建
新线程。
- 关于 sleep()和 wait(),以下描述错误的一项是()
A. sleep 是线程类(Thread)的方法,wait 是 Object 类的方法
B. Sleep 不释放对象锁,wait 放弃对象锁
C. Sleep 暂停线程、但监控状态任然保持,结束后会自动恢复
D. Wait 后进入等待锁定池,只针对此对象发出 notify 方法后获取对象
锁进入运行状态。
答案:D
分析:
针对此对象的 notify 方法后获取对象锁并进入就绪状态,而不是运行状态。
另外针对此对象的 notifyAll 方法后也可能获取对象锁并进入就绪状态,而
不是运行状态
- 进程和线程的区别是什么?
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程
是系统进行资源分配和调度的一个独立单位.
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能
独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中
必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程
的其他的线程共享进程所拥有的全部资源.
区别 进程 线程
根本区别 作为资源分配的单位 调度和执行的单位
开销 每个进程都有独立的代码和
数据空间(进程上下文),进程
间的切换会有较大的开销。
线程可以看成时轻量级的进程,同一类
线程共享代码和数据空间,每个线程有
独立的运行栈和程序计数器(PC),线程
切换的开销小。
所处环境 在操作系统中能同时运行多 在同一应用程序中有多个顺序流同时
个任务(程序) 执行
分配内存 系统在运行的时候会为每个
进程分配不同的内存区域
除了 CPU 之外,不会为线程分配内存
(线程所使用的资源是它所属的进程
的资源),线程组只能共享资源
包含关系 没有线程的进程是可以被看
作单线程的,如果一个进程内
拥有多个线程,则执行过程不
是一条线的,而是多条线(线
程)共同完成的。
线程是进程的一部分,所以线程有的时
候被称为是轻权进程或者轻量级进程。
- 以下锁机机制中,不能保证线程安全的是()
A. Lock
B. Synchronized
C. Volatile
答案:C
- 创建 n 多个线程,如何保证这些线程同时启动?看清,是“同
时”。
答:用一个 for 循环创建线程对象,同时调用 wait()方法,让所有线程
等待;直到最后一个线程也准备就绪后,调用 notifyAll(), 同时启动所有线
程
- 同步和异步有何异同,在什么情况下分别使用它们?
答:
1.如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,
或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数
据,必须进行同步存取。
2.当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不
希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异
步途径往往更有效率。
3.举个例子: 打电话是同步 发消息是异步
- Java 线程中,sleep()和 wait()区别
答:
sleep 是线程类(Thread)的方法;作用是导致此线程暂停执行指定时间,给
执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复;调用
sleep()不会释放对象锁。
wait 是 Object 类的方法;对此对象调用 wait 方法导致本线程放弃对象锁,
进入等 待此对象的等待锁定池。只有针对此对象发出 notify 方法(或
notifyAll)后本线程才进入对象锁定池,准备获得对象锁进行运行状态。
- 下面所述步骤中,是创建进程做必须的步骤是()
A 由调度程序为进程分配 CPU
B. 建立一个进程控制块
C. 为进程分配内存
D. 为进程分配文件描述符
答案:BC
- 无锁化编程有哪些常见方法?()
A 针对计数器,可以使用原子加
B. 只有一个生产者和一个消费者,那么就可以做到免锁访问环形缓冲区
(Ring Buffer)
C. RCU(Read-Copy-Update),新旧副本切换机制,对于旧副本可以
采用延迟释放的做法
D. CAS(Compare-and-Swap),如无锁栈,无锁队列等待
答案:D
分析:
A 这方法虽然不太好,但是常见
B ProducerConsumerQueue就是这个,到处都是
C linux kernel里面大量使用
D 本质上其实就是乐观锁,操作起来很困难。。单生产者多消费者或者多
生产者单消费者的情况下比较常见,也不容易遇到 ABA 问题。
- sleep()和 yield()有什么区别?
答:
① sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优
先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线
程以运行的机会;
② 线程执行 sleep()方法后转入阻塞(blocked)状态,而执行 yield()方法
后转入就绪(ready)状态;
③ sleep()方法声明抛出 InterruptedException,而 yield()方法没有声明任
何异常;
④ sleep()方法比 yield()方法(跟操作系统相关)具有更好的可移植性。
- 当一个线程进入一个对象的 synchronized 方法 A 之后,其
它线程是否可进入此对象的 synchronized 方法?
答:不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。
只有等待当前线程执行完毕释放锁资源之后,其他线程才有可能进行执行该
同步方法!
延伸 对象锁分为三种:共享资源、this、当前类的字节码文件对象
- 请说出与线程同步相关的方法。
答:
- wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
- sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此
方法要捕捉 InterruptedException 异常;
- notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不
能确切的唤醒某一个等待状态的线程,而是由 JVM 确定唤醒哪个线程,而
且与优先级无关;
- notityAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一
个对象的锁,而是让它们竞争;
- JDK 1.5 通过 Lock 接口提供了显式(explicit)的锁机制,增强了灵活性以
及对线程的协调。Lock 接口中定义了加锁(lock())和解锁(unlock())的方
法,同时还提供了 newCondition()方法来产生用于线程之间通信的
Condition 对象;
- JDK 1.5 还提供了信号量(semaphore)机制,信号量可以用来限制对某个
共享资源进行访问的线程的数量。在对资源进行访问之前,线程必须得到信
号量的许可(调用 Semaphore 对象的 acquire()方法);在完成对资源的
访问后,线程必须向信号量归还许可(调用 Semaphore 对象的 release()
方法)
- 编写多线程程序有几种实现方式?
答:Java 5 以前实现多线程有两种实现方法:一种是继承 Thread 类;另一
种是实现 Runnable 接口。两种方式都要通过重写 run()方法来定义线程的
行为,推荐使用后者,因为 Java 中的继承是单继承,一个类有一个父类,
如果继承了 Thread 类就无法再继承其他类了,同时也可以实现资源共享,
显然使用 Runnable 接口更为灵活。
补充:Java 5 以后创建线程还有第三种方式:实现 Callable 接口,该接口中
的 call 方法可以在线程执行结束时产生一个返回值
- synchronized 关键字的用法?
答:synchronized 关键字可以将对象或者方法标记为同步,以实现对对象
和方法的互斥访问,可以用 synchronized(对象) { … }定义同步代码块,或
者在声明方法时将 synchronized 作为方法的修饰符。在第 60 题的例子中
已经展示了 synchronized 关键字的用法。
- 启动一个线程是用 run()还是 start()方法?
答:启动一个线程是调用 start()方法,使线程所代表的虚拟处理机处于可运
行状态,这意味着它可以由 JVM 调度并执行,这并不意味着线程就会立即
运行。run()方法是线程启动后要进行回调(callback)的方法。
API 解释如下:
- 什么是线程池(thread pool)?
答:在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象
要获取内存资源或者其它更多资源。在 Java 中更是如此,虚拟机将试图跟
踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效
率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源
的对象创建和销毁,这就是"池化资源"技术产生的原因。线程池顾名思义就
是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中
获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少
创建和销毁线程对象的开销。
Java 5+中的 Executor 接口定义一个执行线程的工具。它的子类型即线程池
接口是 ExecutorService。要配置一个线程池是比较复杂的,尤其是对于线
程池的原理不是很清楚的情况下,因此在工具类 Executors 面提供了一些静
态工厂方法,生成一些常用的线程池,如下所示:
• newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只
有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个
唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池
保证所有任务的执行顺序按照任务的提交顺序执行。
• newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创
建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到
最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池
会补充一个新线程。
• newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小
超过了处理任务所需要的线程,那么就会回收部分空闲(60 秒不执行任
务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处
理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操
作系统(或者说 JVM)能够创建的最大线程大小。
• newScheduledThreadPool:创建一个大小无限的线程池。此线程池支
持定时以及周期性执行任务的需求。
• newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持
定时以及周期性执行任务的需求。
有通过 Executors 工具类创建线程池并使用线程池执行线程的代码。如果希
望在服务器上使用线程池,强烈建议使用 newFixedThreadPool 方法来创
建线程池,这样能获得更好的性能。
- 线程的基本状态以及状态之间的关系?
答:
除去起始(new)状态和结束(finished)状态,线程有三种状态,分别是:
就绪(ready)、运行(running)和阻塞(blocked)。其中就绪状态代表
线程具备了运行的所有条件,只等待 CPU 调度(万事俱备,只欠东风);
处于运行状态的线程可能因为 CPU 调度(时间片用完了)的原因回到就绪
状态,也有可能因为调用了线程的 yield 方法回到就绪状态,此时线程不会
释放它占有的资源的锁,坐等 CPU 以继续执行;运行状态的线程可能因为
I/O 中断、线程休眠、调用了对象的 wait 方法而进入阻塞状态(有的地方也
称之为等待状态);而进入阻塞状态的线程会因为休眠结束、调用了对象的
notify 方法或 notifyAll 方法或其他线程执行结束而进入就绪状态。注意:
调用 wait 方法会让线程进入等待池中等待被唤醒,notify 方法或 notifyAll
方法会让等待锁中的线程从等待池进入等锁池,在没有得到对象的锁之前,
线程仍然无法获得 CPU 的调度和执行。
- 简述 synchronized 和 java.util.concurrent.locks.Lock
的异同?
答:Lock 是 Java 5 以后引入的新的 API,和关键字 synchronized 相比主
要相同点:Lock 能完成 synchronized 所实现的所有功能;主要不同点:
Lock 有比 synchronized 更精确的线程语义和更好的性能。synchronized
会自动释放锁,而 Lock 一定要求程序员手工释放,并且必须在 finally 块
中释放(这是释放外部资源的最好的地方)
- 创建线程的两种方式分别是什么,优缺点是什么?
方式 1:继承 Java.lang.Thread 类,并覆盖 run() 方法。
优势:编写简单;
劣势:单继承的限制----无法继承其它父类,同时不能实现资源共享。
方式 2:实现 Java.lang.Runnable 接口,并实现 run()方法。
优势:可继承其它类,多线程可共享同一个 Thread 对象;
劣势:编程方式稍微复杂,如需访问当前线程,需调用
Thread.currentThread()方法
- Java 创建线程后,调用 start()方法和 run()的区别
两种方法的区别
1) start 方法:
用 start 方法来启动线程,真正实现了多线程运行,这时无需等待 run
方法体代码执行完毕而直接继续执行下面的代码。通过调用 Thread 类的
start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有
运行,一旦得到 cpu 时间片,就开始执行 run()方法,这里方法 run()称为线
程体,它包含了要执行的这个线程的内容,Run 方法运行结束,此线程随即
终止。
2) run():
run()方法只是类的一个普通方法而已,如果直接调用 run 方法,程序
中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序
执行,还是要等待,run 方法体执行完毕后才可继续执行下面的代码,这样
就没有达到写线程的目的。
总结:调用 start 方法方可启动线程,而 run 方法只是 thread 的一个
普通方法调用,还是在主线程里执行。这两个方法应该都比较熟悉,把需要
并行处理的代码放在 run()方法中,start()方法启动线程将自动调用 run()
方法,这是由 jvm 的内存机制规定的。并且 run()方法必须是 public 访问权
限,返回值类型为 void。
两种方式的比较 :
实际中往往采用实现 Runable 接口,一方面因为 java 只支持单继承,
继承了 Thread 类就无法再继续继承其它类,而且 Runable 接口只有一个
run 方法;另一方面通过结果可以看出实现 Runable 接口才是真正的多线程。
- 线程的生命周期
线程是一个动态执行的过程,它也有一个从产生到死亡的过程。
生命周期的五种状态
新建(new Thread)
当创建 Thread 类的一个实例(对象)时,此线程进入新建状态(未被
启动)
例如:Thread t1=new Thread();
就绪(runnable)
线程已经被启动,正在等待被分配给 CPU 时间片,也就是说此时线程
由于某种原因导致正在运行的线程让出 CPU 并暂停自己的执行,即进
入堵塞状态。
正在睡眠:用 sleep(long t) 方法可使线程进入睡眠方式。一个睡眠着
的线程在指定的时间过去可进入就绪状态。
正在等待:调用 wait()方法。(调用 motify()方法回到就绪状态)
被另一个线程所阻塞:调用 suspend()方法。(调用 resume()方法恢
复)
- 如何实现线程同步?
当多个线程访问同一个数据时,容易出现线程安全问题,需要某种方式
来确保资源在某一时刻只被一个线程使用。需要让线程同步,保证数据安全
线程同步的实现方案:
1)同步代码块,使用 synchronized 关键字
同步代码块:
synchronized (同步锁) {
授课代码; }
同步方法:
public synchronized void makeWithdrawal(int amt) {}
线程同步的好处:解决了线程安全问题
线程同步的缺点:性能下降,可能会带来死锁
注意: 同步代码块,所使用的同步锁可以是三种,
1、 this 2、 共享资源 3、 字节码文件对象
同步方法所使用的同步锁,默认的是 this
- 说说关于同步锁的更多细节
答:
Java 中每个对象都有一个内置锁。
当程序运行到非静态的 synchronized 同步方法上时,自动获得与正在执行
代码类的当前实例(this 实例)有关的锁。获得一个对象的锁也称为获取锁、
锁定对象、在对象上锁定或在对象上同步。
当程序运行到 synchronized 同步方法或代码块时才该对象锁才起作用。
一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以
获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不
能进入该对象上的 synchronized 方法或代码块,直到该锁被释放。
释放锁是指持锁线程退出了 synchronized 同步方法或代码块。
关于锁和同步,有一下几个要点:
1)只能同步方法,而不能同步变量和类;
2)每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是
说,在哪个对象上同步?
3)不必同步类中所有的方法,类可以同时拥有同步和非同步方法。
4)如果两个线程要执行一个类中的 synchronized 方法,并且两个线程使
用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个
需要等待,直到锁被释放。也就是说:如果一个线程在对象上获得一个锁,
就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。
5)如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访
问而不受锁的限制。
6)线程睡眠时,它所持的任何锁都不会释放。
7)线程可以获得多个锁。比如,在一个对象的同步方法里面调用另外一个
对象的同步方法,则获取了两个对象的同步锁。
8)同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方
法,还可以同步方法中一部分代码块。
9)在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取
哪个对象的锁。
- Java 中实现线程通信的三个方法的作用是什么?
Java 提供了 3 个方法解决线程之间的通信问题,均是 java.lang.Object 类
的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常。
方法名 作 用
final void wait() 表示线程一直等待,直到其它线程通知
void wait(long
timeout)
线程等待指定毫秒参数的时间
final void wait(long
timeout,int nanos)
线程等待指定毫秒、微妙的时间
final void notify()
唤醒一个处于等待状态的线程。注意的是
在调用此方法的时候,并不能确切的唤醒
某一个等待状态的线程,而是由 JVM 确定
唤醒哪个线程,而且不是按优先级。
final void notifyAll()
唤醒同一个对象上所有调用 wait()方法的
线程,注意并不是给所有唤醒线程一个对
象的锁,而是让它们竞争
正在就绪队列中排队等候得到 CPU 资源。例如:t1.start();
运行(running)
线程获得 CPU 资源正在执行任务(run()方法),此时除非此线程自动放
弃 CPU 资源或者有优先级更高的线程进入,线程将一直运行到结束。
死亡(dead)
当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不
可能再进入就绪状态等待执行。
自然终止:正常运行 run()方法后终止
异常终止:调用 stop()方法让一个线程终止运行
堵塞(blocked)
-
相关阅读:
云计算设计模式(十九)——运行重构模式
云计算设计模式(十八)——重试模式
云计算设计模式(十七)——基于队列的负载均衡模式
云计算设计模式(十六)——优先级队列模式
云计算设计模式(十五)——管道和过滤器模式
云计算设计模式(十四)——实体化视图模式
云计算设计模式(十三)——领导人选举模式
云计算设计模式(十二)——索引表模式
使用WebClient上传文件时的一些问题
.NET研发人员面试题(一)
-
原文地址:https://www.cnblogs.com/linanana/p/12545931.html
Copyright © 2020-2023
润新知