Java面试题-Java多线程

Java面试题

Java多线程

1.进程与线程的区别是什么。

1.进程是程序在处理机上一次调度的过程,是动态的概念。
  线程是进程的一个实体。
2.进程是资源分配和系统调度的基本单位。
  线程是cpu调度的基本单位。
3.一个进程至少包含一个线程。线程的上下文切换速度要比进程快得多。

2.单线程与多线程是什么关系。

1.多线程是指在一个进程中,并发执行了多个线程,
  每个线程都实现了不同的功能
2.在单核CPU中,将CPU分为很小的时间片,
  在每一时刻只能有一个线程在执行,是一种微观上轮流占用CPU的机制。
  由于CPU轮询的速度非常快,所以看起来像是“同时”在执行一样
3.多线程会存在线程上下文切换,会导致程序执行速度变慢
4.多线程不会提高程序的执行速度,反而会降低速度。
  但是对于用户来说,可以减少用户的等待响应时间,提高了资源的利用效率

3.简述线程的几种状态。

1.新建(NEW)
  线程刚创建出来,还未调用start所处的状态。
2.运行(Runnable)
  线程正在运行或者正在等待cpu资源。
3.阻塞(Blocked)
  当线程准备进入synchronized修饰的方法或者代码块的时候,
  需要获取一个监视器锁,会使线程进入阻塞状态。
4.等待(waiting)
  该状态是因为调用了Object.wait()、Thread.join()、
  LockSupport.part()会处于等待状态。
5.TIMED_WAITING
  与WAITING的状态是一样的,只不过是定时的。
6.消亡(TERMINTED)
  线程执行完毕,就处于消亡状态。  
  
拓展:线程状态,BLOCKED 和 WAITING 有什么区别
     答案:这道题第一眼看上去有点懵,但是答案就在上面,就是34
          的区别。

4.sleep、wait、join、yield有什么区别。

1.sleep方法:是Thread类的静态方法,当前线程将睡眠n毫秒,
  线程进入阻塞状态。当睡眠时间到了,会解除阻塞,进入可运行状态,
  等待CPU的到来。睡眠不释放锁(如果有的话)。
2.wait方法:是Object的方法,必须与synchronized关键字一起使用,
  线程进入阻塞状态,当notify或者notifyall被调用后,会解除阻塞。
  但是,只有重新占用互斥锁之后才会进入可运行状态。睡眠时,会释放互斥锁。
3.join 方法:当前线程调用,则其它线程全部停止,
  等待当前线程执行完毕,接着执行。
4.yield 方法:该方法使得线程放弃当前分得的 CPU 时间。
  但是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。

5.线程的活性障碍。

常见的线程活性故障包括死锁,锁死,活锁与线程饥饿。

1.死锁(产生死锁有四个必要条件)
  1.资源互斥:一个资源每次只能被一个线程使用
  2.请求和保持:一个线程因请求资源而阻塞时,对已获得的资源保持不放
  3.不可剥夺:线程已经获得的资源,在未使用完之前,不能强行剥夺
  4.循环等待:若干线程之间形成一种头尾相接的循环等待资源关系
拓展:怎么避免死锁
     答案:1.锁粗化:用来消除请求和保持条件。
          2.锁排序:将获取锁的进行排序,必须按顺序获取锁
            才能获取到资源。
            
2.线程锁死(有两个原因产生活锁)
  1.信号丢失:没有通知到线程,该线程一直处于阻塞的状态。
  2.监视器嵌套:一般都是因为要获取多重锁导致的。

3.活锁:线程处于可运行状态,但是任务一直无法进展,称为活锁。

4.线程饥饿:线程处于不可运行状态,因为获取不到某个资源。

总结:
1.线程饥饿发生时,如果线程处于可运行状态,也就是其一直在申请资源,
  那么就会转变为活锁
2.只要存在一个或多个线程因为获取不到其所需的资源而无法进展
  就是线程饥饿,所以线程死锁其实也算是线程饥饿

6.多线程的几种实现方式。

1.继承Thread类,重写run方法。
2.实现Runnable接口,重写run方法。
3.通过Callable和FutureTask创建线程。
4.通过线程池创建线程。

7.多个线程(可能是不同机器),相互之间需要等待协调,才能完成某种工作,问怎么设计这种协调方案。

此问题的本质是保持顺序执行。可以使用executors

8.用过读写锁吗,原理是什么,一般在什么场景下用。

与传统锁不同的是读写锁的规则是可以共享读,但只能一个写,总结起来为: 读读不互
斥,读写互斥,写写互斥 ,而一般的独占锁是: 读读互斥,读写互斥,写写互斥 ,而
场景中往往 读远远大于写 ,读写锁就是为了这种优化而创建出来的一种机制。就是使
用在读多写少的场景下。

9.什么是CAS。

CAS(Compare And Swap 比较并且替换)是乐观锁的一种实现方式,是一种轻量级
锁,JUC 中很多工具类的实现就是基于 CAS 的。

10.CAS 是怎么实现线程安全的?

线程在读取数据时不进行加锁,在准备写回数据时,先去查询原值,操作的时候比较原
值是否修改,若未被其他线程修改则写回,若已被修改,则重新执行读取流程。

11.CAS存在什么问题?

1.当写入的时候,如果重新读取的值与原值永远不相同,那么就会永远
  循环,造成cpu资源浪费。
2.CAS还存在AB问题。
3.只能保证一个共享变量的原子操作。
  CAS操作单个共享变量的时候可以保证原子的操作,多个变量就不行了,JDK 5之后 
  AtomicReference可以用来保证对象之间的原子性,就可以把多个对象放入CAS中
  操作。

12.JUC包下的原子类也是通过CAS实现的吗。

是的,比如说AutomicInteger类中的incrementAndGet(),自增方法
就是通过CAS实现的。

13.乐观锁在项目开发过程中有涉及到吗。

有的。比如我们在很多订单表,流水表,为了防止并发问题,就会加入CAS的校验过程,
保证了线程的安全,但是看场景使用,并不是适用所有场景,他的优点缺点都很明显。
拓展:乐观锁,并发容器,原子类等用的是CAS来实现的吗
     是的。

14.开发过程中ABA是怎么解决的。

加标志位,例如搞个自增的字段,操作一次就自增加一,或者搞个时间戳,比较时间戳
的值。

15.简单说一下你了解的悲观锁。

synchronized 是最常用的线程同步手段之一,上面提到的CAS是乐观锁的实现,
synchronized就是悲观锁了。

16.synchronized是如何保证同一时刻只有一个线程可以进入临界区的呢。

1.synchronized对方法加锁,相当于不管哪一个线程(例如线程A),运行到这
个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类的其他
同步方法),有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这
个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行。
2.synchronized 对对象进行加锁,在 JVM 中,对象在内存中分为三块区域:对象
  头(Header)、实例数据(InstanceData)和对齐填充(Padding)。
  1.对象头:我们以Hotspot虚拟机为例,Hotspot的对象头主要包括两部分数据:
    Mark Word(标记字段)、Klass Pointer(类型指针)。
    1.Mark Word:默认存储对象的HashCode,分代年龄和锁标志位信息。它会根
      据对象的状态复用自己的存储空间,也就是说在运行期间Mark Word里存储的
      数据会随着锁标志位的变化而变化。
    2.Klass Point:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这
      个对象是哪个类的实例。
   对象头中保存了锁标志位和指向 monitor 对象的起始地址,当 Monitor 被某个
   线程持有后,就会处于锁定状态

17.在对象级使用锁通常是一种比较粗糙的方法,为什么要将整个对象都上锁,而不允许其他线程短暂地使用对象中其他同步方法来访问共享资源。

1.如果一个对象拥有多个资源,就不需要只为了让一个线程使用其中一部分资源,就将
所有线程都锁在外面。

由于每个对象都有锁,可以如下所示使用虚拟对象来上锁:
 class FineGrainLock{
   MyMemberClassx,y;
   Object xlock = new Object(), ylock = newObject();
   public void foo(){
       synchronized(xlock){
       //accessxhere
        }
       //dosomethinghere-butdon'tusesharedresources
        synchronized(ylock){
        //accessyhere
        }
   }
      public void bar(){
        synchronized(this){
           //accessbothxandyhere
       }
      //dosomethinghere-butdon'tusesharedresources
      }
  }
  
2.synchronized 应用在方法上时,在字节码中是通过方法的 ACC_SYNCHRONIZED 标志来实现的。

我反编译了一小段代码,我们可以看一下我加锁了一个方法,在字节码长啥样,flags字段瞩目:

synchronized void test();
  descriptor: ()V
  flags: ACC_SYNCHRONIZED
  Code:
    stack=0, locals=1, args_size=1
       0: return
    LineNumberTable:
      line 7: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       1     0  this   Ljvm/ClassCompile;
反正其他线程进这个方法就看看是否有这个标志位,有就代表有别的仔拥有了他,你就别碰了。

3.synchronized 应用在同步块上时,在字节码中是通过 monitorenter 和 monitorexit 实现的。

每个对象都会与一个monitor相关联,当某个monitor被拥有之后就会被锁住,当线程执行到monitorenter指令时,就会去尝试获得对应的monitor。

步骤如下:

每个monitor维护着一个记录着拥有次数的计数器。未被拥有的monitor的该计数器为0,当一个线程获得monitor(执行monitorenter)后,该计数器自增变为 1 。

当同一个线程再次获得该monitor的时候,计数器再次自增;
当不同线程想要获得该monitor的时候,就会被阻塞。
当同一个线程释放 monitor(执行monitorexit指令)的时候,计数器再自减。

当计数器为0的时候,monitor将被释放,其他线程便可以获得monitor。

同样看一下反编译后的一段锁定代码块的结果:

public void syncTask();
  descriptor: ()V
  flags: ACC_PUBLIC
  Code:
    stack=3, locals=3, args_size=1
       0: aload_0
       1: dup
       2: astore_1
       3: monitorenter  //注意此处,进入同步方法
       4: aload_0
       5: dup
       6: getfield      #2             // Field i:I
       9: iconst_1
      10: iadd
      11: putfield      #2            // Field i:I
      14: aload_1
      15: monitorexit   //注意此处,退出同步方法
      16: goto          24
      19: astore_2
      20: aload_1
      21: monitorexit //注意此处,退出同步方法
      22: aload_2
      23: athrow
      24: return
    Exception table:
    //省略其他字节码.......

总结:
1.同步方法和同步代码块底层都是通过monitor来实现同步的。
2.两者的区别:同步方式是通过方法中的access_flags中设置ACC_SYNCHRONIZED
  标志来实现,同步代码块是通过monitorenter和monitorexit来实现。

18.以前我们一直锁synchronized是重量级的锁,为啥现在都不提了。

在多线程并发编程中 synchronized 一直是元老级角色,很多人都会称呼它为重量级
锁。
但是,随着 Java SE 1.6synchronized 进行了各种优化之后,有些情况下它
就并不那么重,Java SE 1.6 中为了减少获得锁和释放锁带来的性能消耗而引入的偏
向锁和轻量级锁。
针对 synchronized 获取锁的方式,JVM 使用了锁升级的优化方式,就是先使用偏
向锁优先同一线程然后再次获取锁,如果失败,就升级为 CAS 轻量级锁,如果失败就
会短暂自旋,防止线程被系统挂起。最后如果以上都失败就升级为重量级锁。
对了锁只能升级,不能降级。

19.还有其他的同步手段吗。

ReentrantLock但是在介绍这玩意之前,我觉得我有必要先介绍AQS(AbstractQueuedSynchronizer)。

AQS:也就是队列同步器,这是实现 ReentrantLock 的基础。

AQS 有一个 state 标记位,值为1 时表示有线程占用,其他线程需要进入到同步队列等待,同步队列是一个双向链表。

在这里插入图片描述

当获得锁的线程需要等待某个条件时,会进入 condition 的等待队列,等待队列可以
有多个。
当 condition 条件满足时,线程会从等待队列重新进入同步队列进行获取锁的竞争。
ReentrantLock 就是基于 AQS 实现的,如下图所示,ReentrantLock 内部有公
平锁和非公平锁两种实现,差别就在于新来的线程是否比已经在同步队列中的等待线程
更早获得锁。
和 ReentrantLock 实现方式类似,Semaphore 也是基于 AQS 的,差别在于
ReentrantLock 是独占锁,Semaphore 是共享锁。

在这里插入图片描述

从图中可以看到,ReentrantLock里面有一个内部类Sync,Sync继承
AQS(AbstractQueuedSynchronizer),添加锁和释放锁的大部分操作实际上都是
在Sync中实现的。

它有公平锁FairSync和非公平锁NonfairSync两个子类。

ReentrantLock默认使用非公平锁,也可以通过构造器来显示的指定使用公平锁。

20.volatile 修饰符的有过什么实践。

1.一种实践是用 volatile 修饰 longdouble 变量,使其能按原子类型来读
写。doublelong 都是64位宽,因此对这两种类型的读是分为两部分的,第一次
读取第一个 32 位,然后再读剩下的 32 位,这个过程不是原子的,但 Java 中 
volatile 型的 longdouble 变量的读写是原子的。

2.volatile 修复符的另一个作用是提供内存屏障(memory barrier),例如在分
布式框架中的应用。简单的说,就是当你写一个 volatile 变量之前,Java 内存模
型会插入一个写屏障(write barrier),读一个 volatile 变量之前,会插入一
个读屏障(read barrier)。意思就是说,在你写一个 volatile 域时,能保证任
何线程都能看到你写的值,同时,在写之前,也能保证任何数值的更新对所有线程是可
见的,因为内存屏障会将其他所有写的值更新到缓存。

21.volatile 变量和 atomic 变量有什么不同。

1.volatile关键字能确保可见性和有序性(禁止指令重排),但不保证原子性
  所以不是线程线程安全的。
2.atomic是线程安全的,内部是CAS来实现。

22.能创建 volatile 数组吗。

能,Java 中可以创建 volatile 类型数组,不过只是一个指向数组的引用,而不是
整个数组。我的意思是,如果改变引用指向的数组,将会受到 volatile 的保护,但
是如果多个线程同时改变数组的元素,volatile 标示符就不能起到之前的保护作用
了。

23.Vector, SimpleDateFormat 是线程安全类吗。

1.Verctor是线程安全的集合,但是并非绝对安全。当一个遍历
  该集合的时候,另一个线程进行add()或者remove()操作时会
  抛出异常。
2.SimpleDateFormat并不是一个线程安全的类。

拓展1:对第1点的解释。
      答案:第1点已经提到Verctor并非绝对安全,其真实原因
      是程序员操作失误所造成。比如ConCurrentHashMap也是
      一个线程安全的集合,但是由于不当的组合操作也会造成
      不安全的现象发生(如下代码),所以这种线程不安全是人为
      造成的并非集合不安全。
      @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            while (true) {
                Integer score = scores.get("小明");
                //此处非线程安全
                Integer newScore = score + 1;
                boolean b = scores.replace("小明", score, 
                newScore);
                if (b) {
                    break;
                }
            }
        }
    }

拓展2:SimpleDateFormat并不是一个线程安全的类,那怎么解决
      不安全的问题。
      答案:有如下三种解决方案。
      
1.需要的时候创建局部变量,如下代码:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
 
public class DateUtil1 {
 
    public static  String formatDate(Date date)throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(date);
    }
 
    public static Date parse(String strDate) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.parse(strDate);
    }
}
  在需要用到SimpleDateFormat 的地方新建一个实例,不管什么时候,
将有线程安全问题的对象由共享变为局部私有都能避免多线程问题,不过也
加重了创建对象的负担。在一般情况下,这样其实对性能影响并不是很明显的。

2.创建一个共享的SimpleDateFormat实例变量,但在使用的时候,
需要对这个变量进行同步

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
 
public class DateUtil2 {
 
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 
    public static  String formatDate(Date date)throws ParseException {
        synchronized (sdf){
            return sdf.format(date);
        }
    }
 
    public static Date parse(String strDate) throws ParseException{
        synchronized (sdf){
            return sdf.parse(strDate);
        }
    }
}
当线程较多时,当一个线程调用该方法时,其他想要调用此
方法的线程就要block,多线程并发量大的时候会对性能有一定的影响。

3.使用ThreadLocal为每个线程都创建一个线程独享的SimpleDateFormat变量

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
 
public class DateUtil3 {
 
    private static ThreadLocal<DateFormat> sdfThreadLocal =  new ThreadLocal<DateFormat>(){
            @Override
            public SimpleDateFormat initialValue(){
               return  new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            }
    };
 
    public static  String formatDate(Date date)throws ParseException {
        return sdfThreadLocal.get().format(date);
    }
 
    public static Date parse(String strDate) throws ParseException{
 
        return sdfThreadLocal.get().parse(strDate);
    }
}
使用ThreadLocal, 也是将共享变量变为独享,线程独享肯定能比
方法独享在并发环境中能减少不少创建对象的开销。如果对性能要求比
较高的情况下,一般推荐使用这种方法。

24.多线程中的忙循环是什么。

忙循环就是程序员用循环让一个线程等待,不像传统方法wait(), sleep()yield() 它们都放弃了CPU控制,而忙循环不会放弃CPU,它就是在运行一个空循环。
这么做的目的是为了保留CPU缓存,在多核系统中,一个等待线程醒来的时候可 能会在
另一个内核运行,这样会重建缓存。为了避免重建缓存和减少等待重建的时间就可以使
用它了。

25.ConcurrentHashMap的并发度是多少

ConcurrentHashMap把实际map划分成若干部分来实现它的可扩展性和线程安全。这
种划分是使用并发度获得的,它是ConcurrentHashMap类构造函数的一个可选参数,
默认值为16,这样在多线程情况下就能避免争用。

26.调用start()方法时会执行run()方法,为什么不能直接调用run()方法

如果我们直接调用子线程的run()方法,其方法还是运行在主线程中,代码在程序中是
顺序执行的,所以不会有解决耗时操作的问题。所以不能直接调用线程的run()方法,
只有子线程开始了,才会有异步的效果。当thread.start()方法执行了以后,子线程
才会执行run()方法,这样的效果和在主线程中直接调用run()方法的效果是截然不同
的。

27.Java 中如何停止一个线程

java提供了丰富的API但没有为停止线程提供API,JDK1.0本来有一些像stop(),
suspend()和resume()的控制方法但是由于潜在的死锁威胁因此在后续的JDK版本
中被他们弃用了,之后java API的设计者就没有提供一个兼容且线程安全的方法来停止
一个线程。当run()或者call()方法执行完的时候会自动结束,如果要手动结束一
个线程,你可以用volatile布尔变量来退出run()方法的循环或者是取消任务来中断
线程。
拓展:stop()suspend() 方法为何不推荐使用
     答案:1.stop()方法作为一种粗暴的线程终止行为,在线程终止之前没有对其
            做任何的清除操作,因此具有固有的不安全性。
          2.suspend()方法 该方法已经遭到反对,因为它具有固有的死锁倾向。
            调用suspend()方法的时候,目标线程会停下来。

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

1,如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对
象中有那个共享数据,例如,卖票系统就可以这么做。

2,如果每个线程执行的代码不同,这时候需要用不同的Runnable对象,例如,设计4
个线程。其中两个线程每次对j增加1,另外两个线程对j每次减1,银行存取款

29.如何强制启动一个线程

在线程操作中,可以使用join()方法让一个线程强制运行,线程强制运行期间,只有
该线程拥有CPU的执行权,其他线程无法运行,必须等待此线程完成之后才可以继续执行

30.什么是线程组,为什么在Java中不推荐使用

1.线程组ThreadGroup对象中的stop,resume,suspend会导致安全问题,主要是
死锁问题,已经被官方废弃,多以价值已经大不如以前。
2.线程组ThreadGroup不是线程安全的,在使用过程中不能及时获取安全的信息。

31.你是如何调用 wait(方法的)?使用 if 块还是循环?为什么

wait() 方法应该在循环调用,因为当线程获取到 CPU 开始执行的时候,其他条件可
能还没有满足,所以在处理前,循环检测条件是否满足会更好。下面是一段标准的使用
wait 和 notify 方法的代码:
 
 // The standard idiom for using the wait method
synchronized (obj) {
while (condition does not hold)
obj.wait(); // (Releases lock, and reacquires on wakeup)
... // Perform action appropriate to condition
}

32.线程池是什么?为什么要使用它

1.线程池其实就是将多个线程对象放到一个容器当中。
2.可以重用线程,减少创建和销毁线程带来的消耗。

33.线程池的关闭方式有几种,各自的区别是什么

1.shutdown()调用后,不可以再 submit 新的 task,已经 submit 的将继续执
行
2.shutdownNow()调用后,试图停止当前正在执行的 task,并返回尚未执行的 
task 的 list

34.线程池中submit() 和 execute()方法有什么区别?

1、接收的参数不一样
2、submit有返回值,而execute没有用到返回值的例子,比如说我有很多个做
validation的task,我希望所有的task执行完,然后每个task告诉我它的执行结
果,是成功还是失败,如果是失败,原因是什么。然后我就可以把所有失败的原因综合
起来发给调用者。个人觉得cancel execution这个用处不大,很少有需要去取消执行
的。而最大的用处应该是第二点。
3、submit方便Exception处理
意思就是如果你在你的task里会抛出checked或者unchecked exception,
而你又希望外面的调用者能够感知这些exception并做出及时的处理,那么就需要用到
submit,通过捕获Future.get抛出的异常。

35.Java中用到的线程调度算法是什么

抢占式。一个线程用完CPU之后,操作系统会根据线程优先级、线程饥饿情况等数据算出
一个总的优先级并分配下一个时间片给某个线程执行。
操作系统中可能会出现某条线程常常获取到VPU控制权的情况,为了让某些优先级比较低
的线程也能获取到CPU控制权,可以使用Thread.sleep(0)手动触发一次操作系统分
配时间片的操作,这也是平衡CPU控制权的一种操作。

36.什么是线程调度器 (Thread Scheduler) 和时间分片 (Time Slicing)

线程调度器是一个操作系统服务,它负责为 Runnable 状态的线程分配 CPU 时间。
一旦我们创建一个线程并启动它,它的执行便依赖于线程调度器的实现。同上一个问
题,线程调度并不受到 Java 虚拟机控制,所以由应用程序来控制它是 更好的选择
(也就是说不要让你的程序依赖于线程的优先级)。
时间分片是指将可用的 CPU 时间分配给可用的 Runnable 线程的过程。分配 CPU 
时间可以基于线程优先级或者线程等待的时间。

37.同步块内的线程抛出异常会发生什么

这个问题坑了很多Java程序员,若你能想到锁是否释放这条线索来回答还有点希望答
对。无论你的同步块是正常还是异常退出的,里面的线程都会释放锁,所以对比锁接口
我更喜欢同步块,因为它不用我花费精力去释放锁,该功能可以在finally block里释
放锁实现。

38.怎么检测一个线程是否拥有锁

使用java.lang.Thread类的holdsLock()方法它根据线程是否对传递的对象持有
锁来返回truefalse

39.有哪些无锁数据结构,他们实现的原理是什么

CAS就是一个无锁数据结构。当然无所数据结构不知CAS一下主要介绍一下
CAS的实现原理。
首先来介绍一下原子性操作,它可以简单地分为读写(read and write)、原子性交
换操作(read-modify-write,RMW)两部分。原子操作可认为是一个不可分的操
作;要么发生,要么没发生,我们看不到任何执行的中间过程,不存在部分结果
(partial effects),就像事务。CAS就是原子操作。

CAS伪代码如下:
bool CAS( int * pAddr, int nExpected, int nNew )
atomically {
    if ( *pAddr == nExpected ) {
         *pAddr = nNew ;
         return true ;
    }
    else
        return false ;
}

40.什么是Java线程转储(Thread Dump),如何得到它

线程转储是一个JVM活动线程的列表,它对于分析系统瓶颈和死锁非常有用。有很多方法
可以获取线程转储——使用Profiler,Kill-3命令,jstack工具等等。有的更喜欢
jstack工具,因为它容易使用并且是JDK自带的。由于它是一个基于终端的工具,所以
可以编写一些脚本去定时的产生线程转储以待分析。

41.在线程中你怎么处理不可捕捉异常

1.CheckException(非运行时异常):对于可恢复条件被检查的异常
2.UnCheckException(运行时异常):已经运行不可恢复的异常。
run()方法不支持throws语句,所以当线程对象的run()方法抛出非运行时异常时,必
须捕获并处理他们。当运行时异常从run方法中抛出时,默认行为是在控制台输出堆栈记
录并退出程序。
解决方法一般是调用线程的
setUncaughtExceptionHandler(UncaughtExceptionHandler x) 在
UncaughtExceptionHandler中处理异常。
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 数字20 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读