Java面试题(每天10题)-------连载(28)

目录

多线程篇

1、线程和进程有什么区别?

2、线程实现的方式有几种?

3、高并发、任务执行时间短的业务怎么使用线程池?并发布高、任务执行时间长的业务怎么使用线程池?并发高业务执行时间长的业务怎么使用线程池?

4、如果你提交任务时,线程池队列已满,只是会发生什么?

5、锁的等级:方法锁、对象锁、类锁

6、如果同步块内的线程抛出异常会发生什么?

7、并发线程(concurrency)并发编程(parallellism)有什么区别?

8、如何保证多线程下i++结果正确?

9、一个线程如果出现了运行时异常会怎么样?

10、如何再两个线程之间共享数据?


多线程篇

1、线程和进程有什么区别?

1. 进程是系统进⾏资源分配的基本单位,有独⽴的内存地址空间
2. 线程是 CPU 独⽴运⾏和独⽴调度的基本单位,没有单独地址空间,有独⽴的栈,局部变量,寄存器, 程序计数器等。
3. 创建进程的开销⼤,包括创建虚拟地址空间等需要⼤量系统资源。

4. 创建线程开销⼩,基本上只有⼀个内核对象和⼀个堆栈。
5. ⼀个进程⽆法直接访问另⼀个进程的资源;同⼀进程内的多个线程共享进程的资源。
6. 进程切换开销⼤,线程切换开销⼩;进程间通信开销⼤,线程间通信开销⼩。
7. 线程属于进程,不能独⽴执⾏。每个进程⾄少要有⼀个线程,成为主线程。

2、线程实现的方式有几种?

1. 继承 Thread 类,重写 run ⽅法
2. 实现 Runnable 接⼝,重写 run ⽅法,实现 Runnable 接⼝的实现类的实例对象作为 Thread 构造函数的 target
3. 实现Callable接⼝通过FutureTask包装器来创建Thread线程
4. 通过线程池创建线程
    public class ThreadDemo03 {
        public static void main(String[] args) {
            Callable<Object> oneCallable = new Tickets<Object>();
            FutureTask<Object> oneTask = new FutureTask<Object>(oneCallable);
            Thread t = new Thread(oneTask);
            System.out.println(Thread.currentThread().getName());
            t.start();
        }
    }

    class Tickets<Object> implements Callable<Object>{
        //重写call⽅法
        @Override
        public Object call() throws Exception {
            // TODO Auto-generated method stub
            System.out.println(Thread.currentThread().getName()+"-->我是通过实现Callable接⼝通过FutureTask包装器来实现的线程"
            return null;
        }
    }

3、高并发、任务执行时间短的业务怎么使用线程池?并发布高、任务执行时间长的业务怎么使用线程池?并发高业务执行时间长的业务怎么使用线程池?

1. ⾼并发、任务执⾏时间短的业务:线程池线程数可以设置为 CPU 核数 +1 ,减少线程上下⽂的切换。
2. 并发不⾼、任务执⾏时间⻓的业务要区分开看:
a. 假如是业务时间⻓集中在 IO 操作上,也就是 IO 密集型的任务,因为 IO 操作并不占⽤ CPU ,所以不要让所有的 CPU闲下来,可以加⼤线程池中的线程数⽬,让CPU 处理更多的业务
b. 假如是业务时间⻓集中在计算操作上,也就是计算密集型任务,这个就没办法了,和(1)⼀样吧,线程池中的线程数设置得少⼀些,减少线程上下⽂的切换
3. 并发⾼、业务执⾏时间⻓,解决这种类型任务的关键不在于线程池⽽在于整体架构的设计,看看这些业务⾥⾯某些数据是否能做缓存是第⼀步,增加服务器是第⼆步,⾄于线程池的设置,设置参考(2)。最后,业务执⾏时间⻓的问题,也可能需要分析⼀下,看看能不能使⽤中间件对任务进⾏拆分和解耦。

4、如果你提交任务时,线程池队列已满,只是会发生什么?

1、如果你使⽤的 LinkedBlockingQueue ,也就是⽆界队列的话,没关系,继续添加任务到阻塞队列中等待执⾏,因为LinkedBlockingQueue可以近乎认为是⼀个⽆穷⼤的队列,可以⽆限存放任务;
2 、如果你使⽤的是有界队列⽐⽅说 ArrayBlockingQueue 的话,任务⾸先会被添加到 ArrayBlockingQueue 中,ArrayBlockingQueue满了,则会使⽤拒绝策略 RejectedExecutionHandler 处理满了的任务,默认是 AbortPolicy。

5、锁的等级:方法锁、对象锁、类锁

1. ⽅法锁( synchronized 修饰⽅法时)
a. 通过在⽅法声明中加⼊ synchronized 关键字来声明 synchronized ⽅法。
b. synchronized ⽅法控制对类成员变量的访问:
c. 每个类实例对应⼀把锁,每个 synchronized ⽅法都必须获得调⽤该⽅法的类实例的锁⽅能执⾏,否则所属线程阻塞,⽅法⼀旦执⾏,就独占该锁 ,直到从该⽅法返回时才将锁释放,此后被阻塞的线程⽅能获得该锁,重新进⼊可 执⾏状态。这种机制确保了同⼀时刻对于每⼀个类实例,其所有声明为 synchronized 的成员函数中⾄多只有⼀个处于可执⾏状态,从⽽有效避免了类成员变量的访问冲突。
2. 对象锁( synchronized 修饰⽅法或代码块)
a. 当⼀个对象中有 synchronized method synchronized block 的时候调⽤此对象的同步⽅法或进⼊其同步区域时,就必须先获得对象锁。如果此对象的对象锁已被其他调⽤者占⽤,则需要等待此锁被释放。(⽅法锁也是对象锁)
b. java 的所有对象都含有 1 个互斥锁,这个锁由 JVM ⾃动获取和释放。线程进⼊ synchronized ⽅法的时候获取该对象的锁,当然如果已经有线程获取了这个对象的锁,那么当前线程会等待;synchronized ⽅法正常返回或者抛异常⽽终⽌,JVM 会⾃动释放对象锁 。这⾥也体现了⽤ synchronized 来加锁的 1 个好处,⽅法抛异常的时候,锁仍然可以由JVM来⾃动释放。 
3. 类锁( synchronized 修饰静态的⽅法或代码块)
a. 由于⼀个 class 不论被实例化多少次,其中的静态⽅法和静态变量在内存中都只有⼀份。所以,⼀旦⼀个静态的⽅法被申明为synchronized 。此类所有的实例化对象在 调⽤此⽅法 ,共⽤同⼀把锁,我们称之为类锁。
4. 对象锁是⽤来控制实例⽅法之间的同步,类锁是⽤来控制静态⽅法(或静态变量互斥体)之间的同步。

6、如果同步块内的线程抛出异常会发生什么?

 synchronized⽅法正常返回或者抛异常⽽终⽌,JVM会⾃动释放对象锁

7、并发线程(concurrency)并发编程(parallellism)有什么区别?

1. 解释⼀:并⾏是指两个或者多个事件在同⼀时刻发⽣;⽽并发是指两个或多个事件在同⼀时间间隔发⽣。
2. 解释⼆:并⾏是在不同实体上的多个事件,并发是在同⼀实体上的多个事件。
3. 解释三:在⼀台处理器上“同时”处理多个任务,在多台处理器上同时处理多个任务。如 hadoop 分布式集群。
所以并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。

8、如何保证多线程下i++结果正确?

1. volatile 只能保证你数据的可⻅性,获取到的是最新的数据,不能保证原⼦性;
2. AtomicInteger 保证原⼦性。
3. synchronized 既能保证共享变量可⻅性,也可以保证锁内操作的原⼦性。

9、一个线程如果出现了运行时异常会怎么样?

1、如果这个异常没有被捕获的话,这个线程就停⽌执⾏了。
2.、 另外重要的⼀点是:如果这个线程持有某个对象的监视器,那么这个对象监视器会被⽴即释放。

10、如何再两个线程之间共享数据?

通过在线程之间共享对象就可以了,然后通过wait/notify/notifyAllawait/signal/signalAll进⾏唤起和等待,⽐⽅说阻塞队列BlockingQueue就是为线程之间共享数据⽽设计的。

1、 卖票系统:
public class Ticket implements Runnable {
    private int ticket = 10;

    public void run() {
        while (ticket > 0) {
            ticket--;
            System.out.println("当前票数为:" + ticket);
        }
    }
}

        package 多线程共享数据;

public class SellTicket {
    public static void main(String[] args) {
        Ticket t = new Ticket();
        new Thread(t).start();
        new Thread(t).start();
    }
}

2、银行存取款 

public class MyData {
    private int j = 0;

    public synchronized void add() {
        j++;
        System.out.println("线程" + Thread.currentThread().getName() + "j为:" + j);
    }

    public synchronized void dec() {
        j--;
        System.out.println("线程" + Thread.currentThread().getName() + "j为:" + j);
    }

    public int getData() {
        return j;
    }
}

public class AddRunnable implements Runnable {
    MyData data;

    public AddRunnable(MyData data) {
        this.data = data;
    }

    public void run() {
        data.add();
    }
}

public class DecRunnable implements Runnable {
    MyData data;

    public DecRunnable(MyData data) {
        this.data = data;
    }

    public void run() {
        data.dec();
    }
}

public class TestOne {
    public static void main(String[] args) {
        MyData data = new MyData();
        Runnable add = new AddRunnable(data);
        Runnable dec = new DecRunnable(data);
        for (int i = 0; i < 2; i++) {
            new Thread(add).start();
            new Thread(dec).start();
        }
    }
}
       

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秃头的Tomcat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值