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()的区别