JAVA 面试 - 1

JAVA 面试 - 1

  • 本人即将大四,菜鸟一枚,经历了上半年的春招,深感自己的不足
  • 趁着暑假有空准本奋战今年的秋招,如果写的东西侵犯到你的隐私或者版权,请私信我,我会第一时间处理
  • 以下内容部分摘选自网上一位大神骆昊的博客:https://blog.csdn.net/jackfrued/article/details/44921941
  • 欢迎访问我的 个人网站

1. Thread 类的 sleep() 方法和对象的 wait() 方法都可以让线程暂停执行,它们有什么区别?

sleep() 方法是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复(线程回到就绪状态)。
wait() 是 Object 类的方法,调用对象的 wait() 方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的 notify()/notifyall() 方法时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。

线程和进程

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是操作系统进行资源分配和调度的一个独立单位
线程是进程的一个实体,是 CPU 调度和分派的基本单位,是比进程更小的能独立运行的基本单位。线程的划分尺度小于进程,这使得多线程程序的并发性高
进程在执行时通常拥有独立的内存单元,而线程之间可以共享内存。使用多线程的编程通常能够带来更好的性能和用户体验,但是多线程的程序对于其他程序是不友好的,因为它可能占用了更多的CPU资源。当然,也不是线程越多,程序的性能就越好,因为线程之间的调度和切换也会浪费 CPU 时间。时下很时髦的 Node.js 就采用了单线程异步I/O的工作模式。

2. 线程的 sleep() 方法和 yield() 方法有什么区别?

① sleep() 方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
② 线程执行sleep() 方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;
③ sleep() 方法声明抛出InterruptedException,而yield()方法没有声明任何异常;
④ sleep() 方法比 yield() 方法(跟操作系统CPU调度相关)具有更好的可移植性。

3. 当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?

不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。
因为非静态方法上的 synchronized 修饰符要求执行方法时要获得对象的锁,如果已经进入A方法说明对象锁已经被取走,那么试图进入B方法的线程就只能在等锁池(注意不是等待池)中等待对象的锁。

锁池和等待池

  • 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个 synchronized 方法(或者 synchronized 块),由于这些线程在进入对象的 synchronized 方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中。
  • 等待池:假设一个线程A调用了某个对象的 wait() 方法,线程A就会释放该对象的锁(因为 wait() 方法必须出现在 synchronized 中,这样自然在执行 wait() 方法之前线程A就已经拥有了该对象的锁),同时线程A就进入到了该对象的等待池中。如果另外的一个线程调用了相同对象的 notifyAll() 方法,那么处于该对象的等待池中的线程就会全部进入该对象的锁池中,准备争夺锁的拥有权。如果另外的一个线程调用了相同对象的 notify() 方法,那么仅仅有一个处于该对象的等待池中的线程会随机的进入该对象的锁池。

4. 请说出与线程同步以及线程调度相关的方法

  • wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁
  • sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常
  • notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关
  • notifyAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态

补充

Java 5 通过 Lock 接口提供了显式的锁机制(explicit lock),增强了灵活性以及对线程的协调。Lock 接口中定义了加锁(lock())和解锁(unlock())的方法,同时还提供了 newCondition() 方法来产生用于线程之间通信的 Condition 对象;
此外,Java 5 还提供了信号量机制(semaphore),信号量可以用来限制对某个共享资源进行访问的线程的数量。在对资源进行访问之前,线程必须得到信号量的许可(调用 Semaphore 对象的 acquire() 方法);在完成对资源的访问后,线程必须向信号量归还许可(调用 Semaphore 对象的 release() 方法)。

5. 编写多线程程序有几种实现方式?

  • 继承 Thread 类
  • 实现 Runnable 接口
  • 实现 Callable 接口

Thread类和 Runnable 接口方式都要通过重写 run() 方法来定义线程的行为,推荐使用后者,因为 Java 中的继承是单继承,一个类有一个父类,如果继承了 Thread 类就无法再继承其他类了,显然使用 Runnable 接口更为灵活。
实现Callable接口,该接口中的call方法可以在线程执行结束时产生一个返回值。(Future 设计模式)

public class MultiThread implements Callable<Integer> {

    private int upperBounds;

    public MultiThread(int upperBounds) {
        // TODO Auto-generated constructor stub
        this.upperBounds = upperBounds;
    }

    @Override
    public Integer call() throws Exception {
        // TODO Auto-generated method stub
        int sum = 0; 
        for(int i = 1; i <= upperBounds; i++) {
            sum += i;
        }
        return sum;
    }

    public static void main(String[] args) {
        List<Future<Integer>> list = new LinkedList<>();
        ExecutorService service = Executors.newFixedThreadPool(10);
        for(int i = 0; i < 10; i++) {
            list.add(service.submit(new MultiThread((int) (Math.random() * 100))));
        }

        int sum = 0;
        for(Future<Integer> future : list) {
            // while(!future.isDone()) ;
            try {
                sum += future.get();
            } catch (InterruptedException | ExecutionException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        System.out.println(sum);
    }

}

6. synchronized关键字的用法?

synchronized 关键字可以将对象或者方法标记为同步,以实现对对象和方法的互斥访问,可以用 synchronized(对象) { … } 定义同步代码块,或者在声明方法时将 synchronized 作为方法的修饰符。

7. 举例说明同步和异步

如果系统中存在临界资源(资源数量少于竞争资源的线程数量的资源),例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就必须进行同步存取(数据库操作中的排他锁就是最好的例子)。
当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。
事实上,所谓的同步就是指阻塞式操作,而异步就是非阻塞式操作。

8.启动一个线程是调用 run() 还是 start() 方法?

启动一个线程是调用 start() 方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由 JVM 调度并执行,这并不意味着线程就会立即运行。run() 方法是线程启动后要进行回调(callback)的方法。

9.简述 synchronized 和 java.util.concurrent.locks.Lock 的异同?

Lock 是 Java 5 以后引入的新的 API,和关键字 synchronized 相比
主要相同点:Lock 能完成 synchronized 所实现的所有功能
主要不同点:Lock 有比 synchronized 更精确的线程语义和更好的性能,而且不强制性的要求一定要获得锁。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且最好在finally 块中释放(这是释放外部资源的最好的地方)。

10. 线程的基本状态以及状态之间的关系?

  • Running 表示运行状态
  • Runnable 表示就绪状态(万事俱备,只欠CPU)
  • Blocked表示阻塞状态,阻塞状态又有多种情况,可能是因为调用wait() 方法进入等待池,也可能是执行同步方法或同步代码块进入等锁池,或者是调用了 sleep() 方法或 join() 方法等待休眠或其他线程结束,或是因为发生了 I/O 中断

这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值