Java面试题系列——JavaSE面试题(线程五)

1、synchronized与乐观锁的区别?

        定义:

        乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

        悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如Java里面的同步原语synchronized关键字的实现也是悲观锁。

        适用场景:
        悲观锁:比较适合写入操做比较频繁的场景,若是出现大量的读取操做,每次读取的时候都会进行加锁,这样会增长大量的锁的开销,下降了系统的吞吐量。对象

        乐观锁:比较适合读取操做比较频繁的场景,若是出现大量的写入操做,数据发生冲突的可能性就会增大,为了保证数据的一致性,应用层须要不断的从新获取数据,这样会增长大量的查询操做,下降了系统的吞吐量。

2、你是怎么创建线程池的?

总体来说线程池的创建可以分为以下两类:(1)通过 ThreadPoolExecutor 手动创建线程池。(2)通过 Executors 执行器自动创建线程池。而以上两类创建线程池的方式,又有 7 种具体实现方法,这 7 种实现方法分别是:(1)Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。(Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。(3)Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序。(4)Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池。(5)Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池。(6)Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加】。(7)ThreadPoolExecutor:手动创建线程池的方式,它创建时最多可以设置 7 个参数。

(1)FixedThreadPool

public static void fixedThreadPool() {
    // 创建线程池
    ExecutorService threadPool = Executors.newFixedThreadPool(2);
    // 执行任务
    threadPool.execute(() -> {
        System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
    });
}

(2)CachedThreadPool

public static void cachedThreadPool() {
    // 创建线程池
    ExecutorService threadPool = Executors.newCachedThreadPool();
    // 执行任务
    for (int i = 0; i < 10; i++) {
        threadPool.execute(() -> {
            System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
            }
        });
    }
}

(3)SingleThreadExecutor

public static void singleThreadExecutor() {
    // 创建线程池
    ExecutorService threadPool = Executors.newSingleThreadExecutor();
    // 执行任务
    for (int i = 0; i < 10; i++) {
        final int index = i;
        threadPool.execute(() -> {
            System.out.println(index + ":任务被执行");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
            }
        });
    }
}

(4)ScheduledThreadPool

public static void scheduledThreadPool() {
    // 创建线程池
    ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
    // 添加定时执行任务(1s 后执行)
    System.out.println("添加任务,时间:" + new Date());
    threadPool.schedule(() -> {
        System.out.println("任务被执行,时间:" + new Date());
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
        }
    }, 1, TimeUnit.SECONDS);
}

(5)SingleThreadScheduledExecutor

public static void SingleThreadScheduledExecutor() {
    // 创建线程池
    ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
    // 添加定时执行任务(2s 后执行)
    System.out.println("添加任务,时间:" + new Date());
    threadPool.schedule(() -> {
        System.out.println("任务被执行,时间:" + new Date());
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
        }
    }, 2, TimeUnit.SECONDS);
}

(6)newWorkStealingPool

public static void workStealingPool() {
    // 创建线程池
    ExecutorService threadPool = Executors.newWorkStealingPool();
    // 执行任务
    for (int i = 0; i < 10; i++) {
        final int index = i;
        threadPool.execute(() -> {
            System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
        });
    }
    // 确保任务执行完成
    while (!threadPool.isTerminated()) {
    }
}

(7)ThreadPoolExecutorl(推荐使用)

public static void myThreadPoolExecutor() {
    // 创建线程池
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
    // 执行任务
    for (int i = 0; i < 10; i++) {
        final int index = i;
        threadPool.execute(() -> {
            System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }
}

3、线程的原子性是什么?

原子性        原子性是指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行。原子性就是指该操作是不可再分的。如果一个操作是原子性的,那么多线程并发的情况下,就不会出现变量被修改的情况。非原子操作都会存在线程安全问题,需要使用同步技术(sychronized)来让它变成一个原子操作。一个操作是原子操作,那么称它具有原子性。java的concurrent包下提供了一些原子类,比如:AtomicInteger、AtomicLong、AtomicReference等。

4、说说你对线程的3个特性(原子,可见,有序)的理解?

        (1)原子性:是指一个操作是不可中断的。即使是多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。比如,对于一个静态全局变量int i,两个线程同时对它赋值,线程A给他赋值为1,线程B给他赋值为-1。那么不管这两个线程以何种方式。何种步调工作,i的值要么是1,要么是-1.线程A和线程B之间是没有干扰的。这就是原子性的一个特点,不可被中断。

        (2)可见性:是指当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道这个修改。显然,对于串行来说,可见性问题是不存在的。

        (3)有序性:在并发时,程序的执行可能会出现乱序。给人的直观感觉就是:写在前面的代码,会在后面执行。有序性问题的原因是因为程序在执行时,可能会进行指令重排,重排后的指令与原指令的顺序未必一致。

5、sleep()和wait() 的区别

(1)原理不同。sleep()是Thread类的静态方法,是线程用来控制自身流程的,它会使线程暂停一段时间,把执行机会让给其他线程,等计时时间一到,此线程会自动“苏醒”;而wait()方法时Object类的方法,用于线程之间的通信,这个方法会使拥有对象锁的线程进入等待状态,直到其他线程调用notify方法才能“唤醒”,当然也可以指定一个时间醒来。
(2)对锁的处理机制不同。由于sleep()方法主要是让线程暂停执行一段时间,时间一到则立即恢复运行,不涉及线程之间的通信,所以调用sleep()方法不会释放锁;当调用wait()方法后,线程释放它所占有的锁,让给其他线程去争夺锁。
(3)使用区域不同。sleep()可以放在任何地方使用,而wait()只能放在同步方法或者同步块中使用。
(4)sleep()必须捕获异常,而wait()不用,由于,在sleep()的时间当中有可能被对象调用interrupt()方法,产生InterruptException。
(5)由于sleep()不会释放锁,容易导致“死锁”的情况发生,在多线程的时候,一般情况下用wait()方法好。

 6、如何停止一根线程?

(1) 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止

(2)使用stop方法强行终止,但是不推荐这个方法,因为stop和suspend及resume一样都是过期作废的方法

(3)使用interrupt方法中断线程

持续更新中,敬请期待!

参考文章:

synchronized和lock的区别;悲观锁和乐观锁的区别_毕富国的博客-CSDN博客

悲观锁(Synchronized)和乐观锁(CAS)_wyplj_sir的博客-CSDN博客_synchronized是乐观锁还是悲观锁

线程池的创建方式_detity的博客-CSDN博客_线程池的创建

多线程三大特性——原子性_城南孔乙己的博客-CSDN博客_线程原子性

sleep()方法与wait()方法的区别_李太白不太白的博客-CSDN博客_sleep方法和wait方法的区别

sleep()和wait()的区别_小志的博客的博客-CSDN博客_sleep()和wait()的区别

如何停止一个线程?_码上得天下的博客-CSDN博客_如何停止一个线程

如何停止一个正在运行的线程? - 知乎

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小海海不怕困难

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

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

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

打赏作者

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

抵扣说明:

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

余额充值