JAVA多线程面试题整理及答案

JAVA多线程面试题整理及答案

什么是线程,进程?
进程:进程就是一段程序的执行过程,进程是表示资源分配的基本单位,又是调度运行的基本单位.

进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元

线程:线程是进程中执行运算的最小单位,亦即执行处理机调度的基本单位,一个进程可以包含多个线程,至少是一个线程

线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。每个进程所能访问的内存是互相独立的(一些交换区除外)。而进程中的线程所以共享进程所分配的内存空间.
如何在Java中实现线程?

java.lang.Thread 类的实例就是一个线程但是它需要调用java.lang.Runnable接口来执行,由于Thread类本身就是调用的Runnable接口所以你可以继承 java.lang.Thread 类或者直接调用Runnable接口来重写run()方法实现线程

Thread
Thread thread = new Thread() {
    @Override
    public void run() {
        System.out.println("Thread started!");
} };
thread.start();
Runnable
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("Thread with Runnable started!");
} };
Thread thread = new Thread(runnable);
thread.start();
使用Thread还是Runnable

Runnable是一个接口.而Thread是一个实现了Runnable接口的类,在JAVA中是不允许多继承的,但是可以实现多个接口.如果你的类既需要继承其他的类又需要实现多线程,就需要实现runnable接口了

Thread 类中的start() 和 run() 方法有什么区别

start()方法被用来启动新创建的线程,而且start()内部 调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启 动,start()方法才会启动新线程。

JAVA中Runnable和Callable有什么不同?

Runnable和Callable都代表那些要在不同的线程中执行的任务。Runnable从JDK1.0开始就有了,Callable是在 JDK1.5增加的。它们的主要区别是Callable的 call() 方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结果的Future对象。但是如果callable没有执行完的话,调用Future.get()是会堵塞线程的

线程同步与线程安全
什么是线程安全?
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

在多个线程访问共同的资源时,在某⼀一个线程对资源进⾏行行写操作的中途(写⼊入已经开始,但还没 结束),其他线程对这个写了了⼀一半的资源进⾏行行了了读操作,或者基于这个写了了⼀一半的资源进⾏行行了了写 操作,导致出现数据错误。

synchronized
  • 保证方法内部或代码块内部资源(数据)的互斥访问。即同一时间、由同⼀个 Monitor 监视的代码,最多只能有一个线程在访问
  • 保证线程之间对监视资源的数据同步。即,任何线程在获取到 Monitor 后的第⼀时 间,会先将共享内存中的数据复制到⾃己的缓存中;任何线程在释放 Monitor 的第一 时间,会先将缓存中的数据复制到共享内存中。
同步方法,同步代码块synchronized(object) synchronized(class) ,静态同步方法的区别。
  • 同步方法,同步代码块synchronized(object)
    这两种的写法的效果是一致的,当一个线程对synchronized(object)进行访问时,其他线程对synchronized(object)和对同一个object锁的其 他synchronized(object)进行访问都将会堵塞
  • synchronized(class) 静态同步方法
    这两种的写法的效果是一致的,同步synchronized(*.class)代码块的作用其实和synchronized static方法作用一样。Class锁对类的所有对象实例起作用。
    详细分析见 https://blog.csdn.net/luckey_zh/article/details/53815694
volatile
  • 保证加了 volatile 关键字的字段的操作具有原子性和同步性,其中原⼦性相当于实现了针对 单一字段的线程间互斥访问。因此 volatile 可以看做是简化版的 synchronized。

  • volatile 只对基本类型 (byte、char、short、int、long、float、double、boolean) 的赋值操作和对象的引用赋值操作有效 ,只对赋值操作有效,自增自减等无效,还是需要用synchronized和lock

  • 禁止指令重排序

    1. 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行

    2. 在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行

      //x、y为非volatile变量
      //flag为volatile变量
       
      x = 2;        //语句1
      y = 0;        //语句2
      flag = true;  //语句3
      x = 4;         //语句4
      y = -1;       //语句5
      

      由于flag变量为volatile变量,那么在进行指令重排序的过程的时候,不会将语句3放到语句1、语句2前面,也不会讲语句3放到语句4、语句5后面。但是要注意语句1和语句2的顺序、语句4和语句5的顺序是不作任何保证的。

什么是竞态条件?
如果程序运行顺序的改变会影响最终结果,这就是一个竞态条件
Java多线程中调用wait() 和 sleep()方法有什么不同?
Java程序中wait 和 sleep都会造成某种形式的暂停,它们可以满足不同的需要。wait()方法调用会释放锁,而 sleep()方法仅仅释放CPU资源或者让当前线程停止执行一段时间,但不会释放锁。
如何终止线程
stop和interrupt()

stop强行结束,

interrupt是温和的结束,标记线程结束的状态,不立即,不强制.需要被终结的线程支持 才能使用interrupt.

Thread.interrupted()和thread.isInterrupted()的区别
  • Thread.interrupted()在获取线程中断状态为true后,会重置线程中断状态,也就是把中断状态变为false。
  • thread.isInterrupted(),只是单纯的获取中断线程状态,不会重置。
sleep(),wait(),notify(),notifyAll(), join(),yield()
  • sleep()

    sleep()方法需要指定等待的时间,它可以让当前正在执行的线程在指定的时间内暂停执行,进入阻塞状态,该方法既可以让其他同优先级或者高优先级的线程得到执行的机会,也可以让低优先级的线程得到执行机会。但是sleep()方法不会释放“锁标志”,也就是说如果有synchronized同步块,其他线程仍然不能访问共享数据。

  • wait()

    wait()方法需要和notify()及notifyAll()两个方法一起介绍,这三个方法用于协调多个线程对共享数据的存取,所以必须在synchronized语句块内使用,也就是说,调用wait(),notify()和notifyAll()的任务在调用这些方法前必须拥有对象的锁。注意,它们都是Object类的方法,而不是Thread类的方法。   wait()方法与sleep()方法的不同之处在于,wait()方法会释放对象的“锁标志”。当调用某一对象的wait()方法后,会使当前线程暂停执行,并将当前线程放入对象等待池中,直到调用了notify()方法后,将从对象等待池中移出任意一个线程并放入锁标志等待池中,只有锁标志等待池中的线程可以获取锁标志,它们随时准备争夺锁的拥有权。当调用了某个对象的notifyAll()方法,会将对象等待池中的所有线程都移动到该对象的锁标志等待池。   除了使用notify()和notifyAll()方法,还可以使用带毫秒参数的wait(long timeout)方法,效果是在延迟timeout毫秒后,被暂停的线程将被恢复到锁标志等待池。   此外,wait(),notify()及notifyAll()只能在synchronized语句中使用,但是如果使用的是ReenTrantLock实现同步,该如何达到这三个方法的效果呢?解决方法是使用ReenTrantLock.newCondition()获取一个Condition类对象,然后Condition的await(),signal()以及signalAll()分别对应上面的三个方法。

  • yield()

    yield()方法和sleep()方法类似,也不会释放“锁标志”,区别在于,它没有参数,即yield()方法只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行,另外yield()方法只能使同优先级或者高优先级的线程得到执行机会,这也和sleep()方法不同。

  • join()

    join()方法会使当前线程等待调用join()方法的线程结束后才能继续执行

线程的优先级
线程总是存在优先级,**优先级范围在1~10之间**,可以通过**setPriority(int newPriority**),**线程默认优先级是5**。Thread类中有三个常量,定义线程优先级范围:

static int MAX_PRIORITY

      线程可以具有的最高优先级。

static int MIN_PRIORITY
线程可以具有的最低优先级。
static int NORM_PRIORITY
分配给线程的默认优先级

JVM线程调度程序是基于优先级的抢先调度机制。在大多数情况下,当前运行的线程优先级将大于或等于线程池中任何线程的优先级。

  • 9
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值