`

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状态。

 

分享到:
评论

相关推荐

    java后端面经:面试自我介绍+java+jvm+锁+线程池+数据结构+缓存+redis+数据库+spring+网络+linux

    内容概要:面试自我概要介绍+java基础八股文+jvm详解+锁分类+线程池简要+map数据结构+缓存简要+redis简要+数据库(mysql详解)+spring概要+网络高频+linux简要 适用人群:适用java后端找工作的人群,工作经验三年内...

    Java后端面试问题整理.docx

    • 熟悉Java多线程并发中线程基本方法,线程池,线程生命周期,熟悉Java锁中常见锁分类(乐观/悲观锁、自旋锁、独/共享锁、可重入锁、公平/非公平锁、分段锁、偏向锁,轻/重量级锁)和基本的锁升级策略

    最新Java面试题视频网盘,Java面试题84集、java面试专属及面试必问课程

    面试题包含了不同技术层面的面试问题,同时也能对一些没有面试开发经验的小白给予不可估量的包装, 让你的薪水绝对翻倍, 本人亲试有效.Java面试题84集、java面试专属及面试必问课程,所有的面试题有视屏讲解, 解答方案....

    Java面试技术面知识扩展包第一弹

    您还可能会被要求解释Java中的锁机制、线程池、并发工具类等。 5. 异常处理:面试官可能会询问您关于Java异常处理的知识,包括异常的分类、try-catch-finally块的使用、自定义异常类等。 6. 输入输出(IO):面试...

    java面试笔试资料包括JAVA基础核心知识点深度学习Spring面试题等资料合集.zip

    锁分类的了解.docx 集合的扩容机制.png SpringMVC部分.docx Spring部分.docx 第一题.pdf 第七题 谈谈MySQL支持的事务隔离级别 (1).pdf 第三题 对比HashTable HashMap TreeMap有什么不同.pdf 第二题 Exception Error...

    Java-线程状态和等待唤醒机制和线程池

    创建一个顾客线程(消费者):告知老板要的包子的种类和数量,调用wait方法,放弃cpu的执行,进入到WAITING状态(无限等待) 创建一个老板线程(生产者):花了5秒做包子,做好包子之后,调用notify方法,唤醒顾客吃包子 注意: ...

    Java并发编程(学习笔记).xmind

    Java并发编程 背景介绍 并发历史 必要性 进程 资源分配的最小单位 线程 CPU调度的最小单位 线程的优势 ... Java并发程序中的串行,主要来自独占的资源锁 优化策略 缩

    疯狂JAVA讲义

    1.2 Java的竞争对手及各自优势 4 1.2.1 C#简介和优势 4 1.2.2 Ruby简介和优势 4 1.2.3 Python的简介和优势 5 1.3 Java程序运行机制 5 1.3.1 高级语言的运行机制 6 1.3.2 Java程序的运行机制和JVM 6 1.4 开发...

    免费超全面的Java基础类型,容器,并发,IO流,面向对象,Web编程等代码总结

    锁锁机制,API详解 Fork/Join 框架机制详解 Executor线程池框架简介 面向对象 泛型机制与反射原理 Proxy动态代理机制详解 从整体上观察对象 网络开发 Servlet基础,生命周期执行过程 Http请求详解,握手挥手流程...

    java范例开发大全源代码

     实例31 判断字母分类 46  实例32 优良及差 47  实例33 打印任意一年日历 48  实例34 一年四季的划分 51  第2篇 Java数据处理  第4章 异常处理(教学视频:62分钟) 54  4.1 编译时异常 54  ...

    java范例开发大全

    实例31 判断字母分类 46 实例32 优良及差 47 实例33 打印任意一年日历 48 实例34 一年四季的划分 51 第2篇 Java数据处理 第4章 异常处理(教学视频:62分钟) 54 4.1 编译时异常 54 实例35 除0发生的算术异常...

    Java范例开发大全 (源程序)

     实例31 判断字母分类 46  实例32 优良及差 47  实例33 打印任意一年日历 48  实例34 一年四季的划分 51  第2篇 Java数据处理  第4章 异常处理(教学视频:62分钟) 54  4.1 编译时异常 54  实例35 ...

    xsocketjava包

    阅读xsocket需要一些线程,nio,niosocket,和java.util.concurrent(锁,线程池等)包的一些知识。要不读起来很费劲,建议先去了解下这些知识。可以在我的文章分类concurrent里面有一些,

    Java多线程和并发知识整理

    1.5线程安全的分类 1.6线程安全的方法 二、线程基础 2.1状态 2.2使用方式 2.3基础机制 2.4中断 2.5互斥同步 2.6线程合作 三、Synchronized 详解 3.1 使用 3.2 原理分析 3.3 JVM中锁的优化 3.4 ...

    java核心知识点整理.pdf

    25 JAVA8 与元数据.................................................................................................................................25 2.4. 垃圾回收与算法 .................................

    Java范例开发大全(全书源程序)

    实例31 判断字母分类 46 实例32 优良及差 47 实例33 打印任意一年日历 48 实例34 一年四季的划分 51 第2篇 Java数据处理 第4章 异常处理(教学视频:62分钟) 54 4.1 编译时异常 54 实例35 除0发生的算术...

    java范例开发大全(pdf&源码)

    实例31 判断字母分类 46 实例32 优良及差 47 实例33 打印任意一年日历 48 实例34 一年四季的划分 51 第2篇 Java数据处理 第4章 异常处理(教学视频:62分钟) 54 4.1 编译时异常 54 实例35 除0发生的算术异常...

    Java开发实战1200例(第1卷).(清华出版.李钟尉.陈丹丹).part3

    书名:《Java开发实战1200例(第I卷)》(清华大学出版社.李钟尉,陈丹丹) PDF格式扫描版,全书分为24章,共817页。2011年1月出版。 全书压缩打包成4部分,这是第3部分 注:本系列图书的第I、II卷再版时均相应改名为...

Global site tag (gtag.js) - Google Analytics