Java多线程总结(二)锁、线程池
掌握Java中的多线程,必须掌握Java中的各种锁,以及了解Java中线程池的运用。关于Java多线程基础总结可以参考我的这篇博文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状态。
参考:http://www.cnblogs.com/skywang12345/p/java_threads_category.html
http://blog.csdn.net/aesop_wubo/article/details/10300273
如果觉得本文还不错的话,麻烦点击推荐哦!谢谢啦!
Java多线程总结(二)锁、线程池的更多相关文章
- Java多线程(二) —— 线程安全、线程同步、线程间通信(含面试题集)
一.线程安全 多个线程在执行同一段代码的时候,每次的执行结果和单线程执行的结果都是一样的,不存在执行结果的二义性,就可以称作是线程安全的. 讲到线程安全问题,其实是指多线程环境下对共享资源的访问可能会 ...
- java多线程总结五:线程池的原理及实现
1.线程池简介: 多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力. 假设一个服务器完成一项任务所需时间为:T1 创 ...
- (Java多线程系列九)线程池
线程池 1.什么是线程池 线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程.线程池中线程的数量通常取决于可用内存数量和应用程序的需求. ...
- java多线程详解(7)-线程池的使用
在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, 这样频繁创建线程就会大大降低系 ...
- java多线程系列六、线程池
一. 线程池简介 1. 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池. 2. 使用线程池的好处 a) 降低资源的消耗.使用线程池不用频繁的创建线程和销毁线程 b) 提高响应速度,任 ...
- java多线程(四)-自定义线程池
当我们使用 线程池的时候,可以使用 newCachedThreadPool()或者 newFixedThreadPool(int)等方法,其实我们深入到这些方法里面,就可以看到它们的是实现方式是这样的 ...
- 多线程学习二:线程池 ExecutorService
创建线程池的2种方式: 使用线程池方式1--Runnable接口: 通常,线程池都是通过线程池工厂创建,再调用线程池中的方法获取线程,再通过线程去执行任务方法. Executors:线程池创建工厂类: ...
- (Java多线程系列二)线程间同步
Java多线程间同步 1.什么是线程安全 通过一个案例了解线程安全 案例:需求现在有100张火车票,有两个窗口同时抢火车票,请使用多线程模拟抢票效果. 先来看一个线程不安全的例子 class Sell ...
- Java多线程并发04——合理使用线程池
在此之前,我们已经了解了关于线程的基本知识,今天将为各位带来,线程池这一技术.关注我的公众号「Java面典」了解更多 Java 相关知识点. 为什么使用线程池?线程池做的工作主要是控制运行的线程的数量 ...
- java多线程(二)-线程的生命周期及线程间通信
一.摘要 当我们将线程创建并start时候,它不会一直占据着cpu执行,而是多个线程间会去执行着这个cpu,此时这些线程就会在多个状态之间进行着切换. 在线程的生命周期中,它会有5种状态,分别为 ...
随机推荐
- .NET读写Excel工具Spire.XlS使用(DataExport )
Introduction E-ICEBLUE is developing office.net component, the main products include Spire.Doc, Spir ...
- 机器数据的价值 - Web 访问日志和数据库审计日志
计算机数据 大量的数据流,不断增长的来源,蕴含着巨大的价值 在 Splunk,我们大量谈及计算机数据.这些数据是指在数据中心.“物联网”和互联设备世界中运行的所有系统产生的数据.其中包括支撑组织的应用 ...
- 记录git多人协作开发常用的流程,供新手参考
声明:博主写的博客都是经过自己总结或者亲测成功的实例,绝不乱转载.读者可放心看,有不足之处请私信我,或者给我发邮件:pangchao620@163.com. 写作目的: 记录一下我看完廖学锋老师的gi ...
- form.submit() not a function的元凶
今天晚上学习jquery form plugin时,在明白了该插件的用法时, (1)该插件是将form的HTTP请求 改为AJax请求. (2)支持像jQuery.ajax(options)一样 的o ...
- 【hbase】——HBase 写优化之 BulkLoad 实现数据快速入库
1.为何要 BulkLoad 导入?传统的 HTableOutputFormat 写 HBase 有什么问题? 我们先看下 HBase 的写流程: 通常 MapReduce 在写HBase时使用的是 ...
- nginx日志切割脚本
#!/bin/bash ip=`ifconfig eth0 | grep "inet addr" | cut -f 2 -d ":" | cut -f 1 -d ...
- WIN 下的超动态菜单(二)用法
WIN 下的超动态菜单(一)简介 WIN 下的超动态菜单(二)用法 WIN 下的超动态菜单(三)代码 作者:黄山松,发表于博客园:http://www.cnblogs.com/tomview/ ...
- android handler runnable使用实例(关键是内部run中停止)
.java package com.example.mydemo; import android.app.Activity; import android.os.Bundle; import andr ...
- DevOps Workshop 研发运维一体化第一场(微软亚太研发集团总部)
准备了近两周,写了大量的操作手册,设计了大量的动手实验场景,终于在中关村的微软大厦完成了两天的DevOps培训. 最初报名160人,按照之前的培训经验,一般能到一半就不错了,没想到这次现场登记人员就超 ...
- 我的STL学习之路
说起STL(标准模板库),相信了解C++的都不会陌生吧^_^.LZ是从大三开始学习C++(ps:不是科班出身),并慢慢接触使用STL的,在学校中使用STL比较多的情况是写数据结构代码,使用STL实现数 ...