public class LockService {
private ReentrantLock reentrantLock;
public LockService(boolean fair) {
reentrantLock = new ReentrantLock(fair);
}
public void test() {
reentrantLock.lock();
System.out.println("获取锁的线程"+Thread.currentThread().getName());
reentrantLock.unlock();
}
}
public static void main(String[]args){
final LockService lockService=new LockService(true);
ExecutorService executorService= Executors.newFixedThreadPool(5);
for (int i=0;i<5;i++){
executorService.execute(new Runnable() {
public void run() {
System.out.println("当前线程"+Thread.currentThread().getName());
lockService.test();
}
});
}
}
//非公平锁
public static void main(String[]args){
final LockService lockService=new LockService(false);
ExecutorService executorService= Executors.newFixedThreadPool(5);
for (int i=0;i<5;i++){
executorService.execute(new Runnable() {
public void run() {
System.out.println("当前线程"+Thread.currentThread().getName());
lockService.test();
}
});
}
}
1. 什么是多线程?作用?
线程是操作系统能够进行运算调度的最小单位。通常一个进程包含一个或者多个线程;多线程指的是多个线程同时进行工作;多线程可以提高运行效率,比如,多线程下载,多线程处理耗时任务。
2. 创建线程的方式
2.1 Thread
//创建线程
//启动线程
2.2 Runnable
//创建
//启动
2.3 juc
juc(java.util.concurrent)是jdk提供的多线程开发包。包括线程池、原子类、线程同步、锁等。 //创建线程方式一
//创建线程方式一
注意Runnable和Callable的区别:
Callalbe接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。
3. 什么是线程安全?线程不安全?
线程安全和和线程不安全都是在多线程环境下才会有这个区别;线程安全指多个线程操作同一个资源时,执行结果和预期理论结果一致(有保护机制);而线程不安全指多个线程操作同一个资源时,执行结果可能有多种情形,和预期的理论结果不一致。(没有保护机制)
4. 锁
锁是java中一种保证线程安全的机制。
4.1 公平锁/非公平锁
-公平锁:加锁前检查是否有排队等待的线程,优先处理排队等待的线程,先来先得,获得锁的顺序是确定的。(FIFO)
注:ReentrantLock和ReadWriteLock默认都是非公平锁,非公平锁的效率比较高。
//公平锁实现
//非公平锁
注:公平锁执行结果按线程的先后顺序;而非公平锁执行结果顺序不确定。
4.2 可重入锁
可重入锁指可重复、可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁;java中ReentrantLock、synchronized是可重入锁。
4.3 独享锁/共享锁
4.4 读写锁
4.5 乐观锁/悲观锁
4.6 分段锁
分段锁指的是将需要加锁的数据进行分段,然后对每一段进行加锁,这样可以保证每一段的操作都是线程安全的,由于多段可以同时进行操作,因此提升了并发效率。其缺点就是维护多个锁实现更加复杂。在java中,ConcurrentHashMap使用的就是分段锁实现的。(分而治之思想)
4.7 偏向锁/轻量级锁/重量级锁
4.8 自旋锁
自旋锁指持有锁的线程能在很短时间内释放锁资源,即那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋),并且不停地尝试拿到这个锁(类似tryLock),当然循环的次数是有限制的,等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。
4.9 什么是死锁?死锁是怎么产出的?如何避免死锁?
死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。 死锁产生的条件:
避免死锁的方式: 1.指定加锁的顺序;
死锁检测;使用工具JConsole和Jstack来检查死锁问题。
5. 线程同步
5.1 synchronized关键字
synchronized是线程同步的关键字可以修饰同步方法、同步代码块。synchronized可以锁定类或者锁定对象,在锁定对象时,一定要注意锁定的是同一个对象。 //锁定方法
//锁定代码块
5.2 volatile
volatile是一个线程同步中一个关键字,有以下几个特点:
private Singleton() {
}
public static Singleton getInstance() { if(instance==null) { synchronized (Singleton.class) { if(instance==null) instance = new Singleton(); } } return instance; } }
指令重排:指JVM在编译时,CPU和编译器为了提升程序执行的效率,会按照一定的规则允许进行指令优化。
5.3 Lock
Lock是juc包中锁机制主要包含ReentrantLock和ReentrantReadWriteLock锁,以ReentrantLock为例,实现如下:
public static void main(String[] args) {
zgq2 zgq3 zgq
public static void main(String[] args) {
add:Sun Jun 23 19:39:19 CST 2019 add:Sun Jun 23 19:39:19 CST 2019 add:Sun Jun 23 19:39:19 CST 2019 队列大小:3 队列大小:2 add:Sun Jun 23 19:39:21 CST 2019 队列大小:3
public static void main(String[] args) { final AtomicInteger atomicInteger=new AtomicInteger(); ExecutorService executorService= Executors.newFixedThreadPool(4); for (int i=0;i<4;i++){ executorService.execute(new Runnable() { public void run() { atomicInteger.getAndIncrement(); } }); } try { Thread.sleep(1000); System.out.println(atomicInteger.get()); } catch (InterruptedException e) { e.printStackTrace(); }