java 锁的种类及线程池
转自:http://www.cnblogs.com/zrtqsk/p/3784049.html
一、Java中锁
什么是锁。锁就是为了保护资源,防止多个线程同时操作资源时出错的机制。
我们先来看一下锁的类:
如图,Java中的锁有两个主要的根接口——Lock和ReadWriteLock,分别表示锁和读写锁。其中Lock的主要实现类是ReetrantLock。ReadWriteLock的主要实现类是ReetrantReadWriteLock。而ReetrantReadWriteLock读写锁是通过两个内部类——ReadLock和WriteLock实现的,其中ReadLock是共享锁,WriteLock是独占锁。这两个内部类都实现了Lock接口。
(1)、Java中的锁主要有以下几种概念:
1、同步锁
同一时刻,一个同步锁只能被一个线程访问。以对象为依据,通过synchronized关键字来进行同步,实现对竞争资源的互斥访问。
2、独占锁(可重入的互斥锁)
互斥,即在同一时间点,只能被一个线程持有;可重入,即可以被单个线程多次获取。什么意思呢?根据锁的获取机制,它分为“公平锁”和“非公平锁”。Java中通过ReentrantLock实现独占锁,默认为非公平锁。
3、公平锁
是按照通过CLH等待线程按照先来先得的规则,线程依次排队,公平的获取锁,是独占锁的一种。Java中,ReetrantLock中有一个Sync类型的成员变量sync,它的实例为FairSync类型的时候,ReetrantLock为公平锁。设置sync为FairSync类型,只需——Lock lock = new ReetrantLock(true)。
4、非公平锁
是当线程要获取锁时,它会无视CLH等待队列而直接获取锁。ReetrantLock默认为非公平锁,或——Lock lock = new ReetrantLock(false)。
5、共享锁
能被多个线程同时获取、共享的锁。即多个线程都可以获取该锁,对该锁对象进行处理。典型的就是读锁——ReentrantReadWriteLock.ReadLock。即多个线程都可以读它,而且不影响其他线程对它的读,但是大家都不能修改它。CyclicBarrier, CountDownLatch和Semaphore也都是共享锁。
6、读写锁
维护了一对相关的锁,“读取锁”用于只读操作,它是“共享锁”,能同时被多个线程获取。“写入锁”用于写入操作,它是“独占锁”,只能被一个线程锁获取(就是写的时候不能读,因为写锁是独占锁,当有线程写入数据时,这时数据是不能读的,同样有线程读时,写锁也是不可以写的)。Java中,读写锁为ReadWriteLock 接口定义,其实现类是ReentrantReadWriteLock,包括内部类ReadLock和WriteLock。方法readLock()、writeLock()分别返回度操作的锁和写操作的锁。
(至于“死锁”,并不是一种锁,而是一种状态,即两个线程互相等待对方释放同步监视器的时候,双方都无法继续进行,造成死锁。)
锁的用法主要就是下面的流程:
//先得到lock lock.lock();//然后获取锁 try { //各种控制操作 }catch(Exception e){ }finally { lock.unlock();//解锁 }
可以看到,这样的用法比synchronized关键字依据对象同步,要方便简单的多。
(2)LockSupport和Condition
1、LockSupport
是用来创建锁和其他同步类的基本线程阻塞原语。 LockSupport中的静态方法park() 和 unpark() 的作用分别是阻塞线程和解除阻塞线程,而不会导致死锁。演示如下:
package lock; import java.util.concurrent.locks.LockSupport; public class LockSupportTest { static Thread mainThread = null; public static void main(String[] args) { //获取主线程 mainThread = Thread.currentThread(); //新建线程并启动 MyThread thread1 = new MyThread("thread1"); thread1.start(); //模拟线程工作开始 System.out.println(Thread.currentThread().getName() + "-----》 runs now!"); for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "-----》 running step " + i); //当前线程睡眠1秒 sleepOneSecond(); if(i == 2){ System.out.println(Thread.currentThread().getName() + "-----》 now pack main thread——————————"); //让主线程阻塞 LockSupport.park(); } } System.out.println(Thread.currentThread().getName() + "-----》 run over!"); } /**当前线程暂停一秒钟 */ public static void sleepOneSecond(){ try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } static class MyThread extends Thread { public MyThread(String name){ super(name); } @Override public void run() { synchronized (this) { //模拟工作开始 System.out.println(Thread.currentThread().getName() + "-----》 runs now!"); for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "-----》 running step " + i); //当前线程睡眠1秒 sleepOneSecond(); } //模拟工作结束 System.out.println(Thread.currentThread().getName() + "-----》 run over!"); } System.out.println(Thread.currentThread().getName() + "-----》 now unpack main thread———————— "); //解除主线程的阻塞 LockSupport.unpark(mainThread); } } }
结果如下:
thread1-----》 runs now! thread1-----》 running step 0 main-----》 runs now! main-----》 running step 0 thread1-----》 running step 1 main-----》 running step 1 main-----》 running step 2 thread1-----》 running step 2 main-----》 now pack main thread—————————— thread1-----》 running step 3 thread1-----》 running step 4 thread1-----》 run over! thread1-----》 now unpack main thread———————— main-----》 running step 3 main-----》 running step 4 main-----》 run over!
2、Condition
对锁进行精确的控制,可用来代替Object中的wait、notify、notifyAll方法,需要和Lock联合使用。可以通过await(),signal()来休眠、唤醒线程。创建方式:Condition condition = lock.newCondition();
演示懒得自己写了,参照http://www.cnblogs.com/skywang12345/p/3496716.html,如下:
package LockSupportTest; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ConditionTest { private static Lock lock = new ReentrantLock(); private static Condition condition = lock.newCondition(); public static void main(String[] args) { ThreadA ta = new ThreadA("ta"); lock.lock(); // 获取锁 try { System.out.println(Thread.currentThread().getName()+" start ta"); ta.start(); System.out.println(Thread.currentThread().getName()+" block"); condition.await(); // 等待 System.out.println(Thread.currentThread().getName()+" continue"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); // 释放锁 } } static class ThreadA extends Thread{ public ThreadA(String name) { super(name); } public void run() { lock.lock(); // 获取锁 try { System.out.println(Thread.currentThread().getName()+" wakup others"); condition.signal(); // 唤醒“condition所在锁上的其它线程” } finally { lock.unlock(); // 释放锁 } } } }
结果如下:
main start ta main block ta wakup others main continue
如上,用起来挺简单的。
二、线程池
我们先来看一下线程池的类图:
1、介绍
可见,线程池的主要是由一个Executor接口统筹的。这个接口代表一个执行者,是一个典型的命令模式的运用。这个接口只有一个方法void execute(Runnable command),提交并执行任务。
ExecuteService顾名思义,指的是Executor的服务类,继承了Executor接口,提供了更详细的控制线程的方法。
AbstractExecutorService是一个抽象类,实现了ExecutorService大部分的方法。
而我们最常用的ThreadPoolExecutor则继承了ExecutorService。
ForkJoinPool是JDK7新增的线程池,也是继承了这个线程类。
ScheduledExecutorService这个接口继承了ExecutorService,比ExecutorService新增了“延时”和“周期执行”的功能。
ScheduledThreadPoolExecutor这个类则实现了ScheduledExecutorService接口,且继承了ThreadPoolExecutor,新增了“延时”和“周期执行”的功能。
Executors是一个线程池的工厂类,提供一系列静态方法,用于创建各种不同功能的线程池或线程相关的对象。
而线程池的使用,最基本的就是如下:
// 创建各种线程 Thread thread1 = new MyThread(); Thread thread2 = new MyThread(); Thread thread3 = new MyThread(); // 创建线程池pool // 将线程放入池中进行执行 pool.execute(thread1 ); pool.execute(thread2 ); pool.execute(thread3 ); // 关闭线程池 pool.shutdown();
2、ForkJoinPool
可见,整个线程池系列,说白了,也就3个类ThreadPoolExecutor、ScheduledThreadPoolExecutor、ForkJoinPool。一个是普通线程池,一个是新增了“延时”和“周期执行”的功能的线程池。那么ForkJoinPool是什么呢?
ForkJoinPool为了是解决现在、未来计算机多核的问题。ExecuteService其他实现类基本都是基于单核下执行的,解决的是并发问题,而ForkJoinPool解决的是并行问题。ExcuteService中处于后面的任务需要等待前面任务执行后才有机会执行,而ForkJoinPool会采用work-stealing模式帮助其他线程执行任务。work-stealing模式——所有在池中的线程尝试去执行其他线程创建的子任务,这样就很少有线程处于空闲状态,非常高效。
ForkJoinPool除了可以执行Runnable任务外,还可以执行ForkJoinTask任务,即ForkJoinPool的execute方法可以传入一个ForkJoinTask对象,这个任务对象跟Runnable的不同是,ForkJoinTask被放到线程内部的队列里面,而普通的Runnable任务被放到线程池的队列里面了。
需要详细了解ForkJoinPool,可以参考http://blog.csdn.net/aesop_wubo/article/details/10300273。
3、Executors
Executors是一个线程池的工厂类,提供一系列静态方法,用于创建各种不同功能的线程池或线程相关的对象。
主要有如下的几个静态方法:
newCachedThreadPool() : 创建一个具有缓存功能的线程池,系统根据需要创建线程,这些线程被缓存在线程池中。
newFixedThreadPool(int nThreads) : 创建一个可重用的,具有固定线程数的线程池。
newSingleThreadExecutor() : 创建一个只有一个单线程的线程池。
newScheduledThreadPool(int corePoolSize) : 创建具有指定数目的线程池,可以指定延时后执行任务,即使线程空闲,也被保持在线程池内。
newSingleThreadScheduledExecutor() : 创建一个只有一个单线程的线程池,可以指定延时后执行任务。
4、线程池的状态
线程池的状态有五种——RUNNING, SHUTDOWN, STOP, TIDYING, TERMINATED
(图片出处:http://www.cnblogs.com/skywang12345/p/3509960.html)
RUNNING : 线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。
SHUTDOWN : 线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。
STOP : 线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
TIDYING : 当所有的任务已终止,ctl记录的"任务数量"为0,线程池会变为TIDYING状态。 当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空 的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
TERMINATED : 线程池彻底终止,就变成TERMINATED状态。
相关推荐
为解决这一问题,可以对任务进行分类,并根据不同类型的任务分配不同的线程池。例如,可以将I/O密集型任务与计算密集型任务分开处理,以便更好地利用硬件资源。 #### 总结 总之,线程池虽然能极大地提高服务器的...
内容概要:面试自我概要介绍+java基础八股文+jvm详解+锁分类+线程池简要+map数据结构+缓存简要+redis简要+数据库(mysql详解)+spring概要+网络高频+linux简要 适用人群:适用java后端找工作的人群,工作经验三年内...
面试题包含了不同技术层面的面试问题,同时也能对一些没有面试开发经验的小白给予不可估量的包装, 让你的薪水绝对翻倍, 本人亲试有效.Java面试题84集、java面试专属及面试必问课程,所有的面试题有视屏讲解, 解答方案....
在编程实践中,程序员应当熟悉Java内存模型、锁的原理与种类、并发工具类、以及各种同步机制。在使用并发技术时,应该理解其带来的潜在问题,如死锁、资源竞争、线程饥饿等问题,并通过合理的设计和编程技巧避免这些...
您还可能会被要求解释Java中的锁机制、线程池、并发工具类等。 5. 异常处理:面试官可能会询问您关于Java异常处理的知识,包括异常的分类、try-catch-finally块的使用、自定义异常类等。 6. 输入输出(IO):面试...
垃圾回收器种类,垃圾回收扫描算法(root 扫描),回收算法(复制,标记清除,标记整理)。 4. Young GC 和 Full GC 触发条件;CMS 回收器的标记过程,内存碎片问题。 5. JVM 问题分析和命令:GC 频率,jstat -...
6. **多线程**:线程的创建方式(Thread类和Runnable接口),线程同步(synchronized关键字,wait()和notify(),Lock锁),线程池的使用。 7. **并发编程**:并发集合(ConcurrentHashMap、CopyOnWriteArrayList等...
锁分类的了解.docx 集合的扩容机制.png SpringMVC部分.docx Spring部分.docx 第一题.pdf 第七题 谈谈MySQL支持的事务隔离级别 (1).pdf 第三题 对比HashTable HashMap TreeMap有什么不同.pdf 第二题 Exception Error...
6. **多线程**:线程的创建(Thread类和Runnable接口)、线程同步(synchronized关键字、wait/notify机制、Lock锁)、线程池的使用等。 7. **反射机制**:Java反射机制允许在运行时动态地获取类的信息并创建对象,...
7. **异常处理**:Java中的异常分类(运行时异常和检查异常)及其处理机制。 8. **线程同步**:`synchronized` 关键字的实现原理和锁优化策略。 9. **volatile关键字**:保证可见性和禁止指令重排的作用。 10. **...
Java高级面试题及答案真题(2021年Java面试题答案大汇总) Java高级面试题大全,涵盖了Java语言常见面试题、Java工程师高级面试题及一些大厂Java开发面试宝典,面试经验技巧等。该套面试题汇总大全,包含大量经典的...
#### 锁的种类 Java中有多种锁机制,包括方法锁、对象锁、类锁。了解不同锁的用途和特点对于编写高效的并发代码至关重要。 #### 线程优先级 线程优先级是控制线程调度的一个参数,Java允许你为线程指定优先级,...
5. **线程编程**:线程的创建方式,同步机制(synchronized,wait(),notify(),锁),线程池的使用。 6. **反射机制**:运行时动态加载类,创建对象,访问私有成员,以及动态代理。 7. **网络编程**:Socket通信...
3. **多线程**:Java支持多线程编程,面试时会询问线程的创建(Thread类和Runnable接口)、线程同步(synchronized关键字、wait/notify机制、Lock锁)、线程池(ExecutorService、ThreadPoolExecutor、Future接口)...
6. **多线程**:在并发编程中,了解线程同步、锁机制、线程池等概念,可以帮助解决复杂的问题。 7. **算法和数据结构**:包括排序(快速排序、归并排序、堆排序等)、搜索(二分查找、深度优先搜索、广度优先搜索等...
- 字节流与字符流是两种基本的I/O流分类。 #### 九、多线程和并发编程基础 - **线程概念** - 线程是程序执行的基本单位。 - Java中使用Thread类或Runnable接口来创建线程。 - **同步与锁** - synchronized...
在并发编程方面,规范提供了关于线程安全、锁的使用、线程池创建等最佳实践。比如,推荐使用`Atomic`类进行原子操作,避免使用`synchronized`关键字时的死锁风险。 除此之外,规范还涉及日志记录、单元测试、性能...
4. **异常处理**:熟悉try-catch-finally语句块,异常的分类,自定义异常的创建及其使用。 5. **集合框架**:ArrayList、LinkedList、HashSet、HashMap等数据结构的特性和使用场景,以及集合的遍历、转换、操作方法...
8. **多线程**:讲解线程的创建方式(实现Runnable接口和继承Thread类),线程同步(synchronized关键字、wait/notify机制、Lock锁)以及线程池。 9. **反射机制**:通过反射,可以在运行时动态地获取类的信息并...
Java 是一门广泛应用的编程语言,它的面试题涵盖了 Java 基础、Java 集合、多线程、Java 异常、Spring、Spring MVC、Spring Boot、Spring Cloud、Netty、Dubbo、Activemq、Java Web、JVM、Kafka、MongoDB、MyBatis、...