zoukankan      html  css  js  c++  java
  • 多线程编程核心技术总结(读周志明书籍的总结)

    多线程编程核心技术总结

     

    1.Java多线程基本技能

    1.1进程和线程的概念:

    进程是独立的程序,线程是在进程中独立运行的子任务。

    1.2使用多线程

    1.2.1实现方法:继承Thread类,重写Runnable接口。

    1.2.2线程安全问题:并发修改公共的实例变量,i++,i--

    1.3线程Thread类的一些方法:

    currentThread() 放回代码段正在被那个线程调用

    isAlive() 判断线程是否处于活动状态

    sleep() 使得当前线程退出CPU片段,等待获取锁

    1.4停止线程

    1.4.1使用interrupt()方法不是使线程马上停止执行,而是打了个暂停的标记。

    this.interrupted():测试当前线程是否已经中断,this.isInterrupted():测试当前线程是否已经中断。

    1.4.2停止线程常用方法--异常法(抛出异常)

    1.4.3 在sleep()时,去interrupted。

    1.4.4 yield()方法的作用是放弃当前的CPU资源,放弃多长时间不确定,立即加入到CPU的竞争中。

    1.5线程优先级

    优先级从1—10不等,设置用setPriority()方法,但是优先级具有随机性,优先级高的不一定先执行。

    1.6守护线程

    Java线程有两种,一种是守护线程(Daemon)另一是用户线程,垃圾回收线程就是典型的守护线程。守护线程为其它线程的运行提供便利,当其他线程都结束时,守护线程才与JVM一同结束工作。

     

    2.对象及变量的并发访问

    线程安全问题出现在“实例变量”中,如果是方法内部的私有变量,则不存在线程安全问题。

    2.1synchronized同步方法

    (1)Synchronized获得对象锁,那个线程先执行带synchronized关键字的方法;那个线程就持有该方法所属对象的锁,其它访问同一对象的线程就只能等待;

    (2)只有共享资源的读写才需要做同步化;

    (3)A线程先持有object对象的锁,B线程可以以异步的方式调用object对象的非synchronized方法。

    A线程先持有object对象的锁,B线程如果调用object对象的synchronized方法,则需要等待,即同步。

    (4) synchronized锁重入:synchronized方法内部调用本类其它的synchronized方法时,是永远可以得到锁的。

    (5) 出现异常,锁自动释放。同步不具有继承性。

    2.2 synchronized同步语句块

    Synchronized方法是对当前对象进行加锁,而synchronized代码块是对某一个对象加锁。Synchronized方法效率太低,synchronized代码块更加细粒化

    (1)synchronized同步方法

    1)对其他同一对象的synchronized方法或synchronized(this)同步代码块呈阻塞状态。

    2)同一时间只有一个线程可以执行synchronized同步方法中的代码

    (2)synchronized(this)同步代码块

    1)对其他同一对象的synchronized方法或synchronized(this)同步代码块呈阻塞状态。

    2)同一时间只有一个线程可以执行synchronized(this)同步代码块中的代码

    (3)synchronized(对象x)同步代码块

    1)在多个线程持有“对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(对象x)同步代码块;

    2)持有“对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(对象x)同步代码块。

    对象监视器必须是同一个对象才是同步的,否则是异步调用。

     

    (4)静态同步Synchronized方法与Synchronized(class)代码块

    Synchronized关键字用于static方法上时,如果这样写,那是对当前类进行加锁与Synchronized(class)代码块作用一致。Synchronized关键字用于非static方法上时,如果这样写,那是对当前类的对象进行加锁。

     

    (5)多线程死锁:比如两个对象持有对象锁,在同步方法(或者块)内部同时请求另一个对象的对象锁。

    (6)只要对象不变,即使对象属性被改变,运行的结果还是同步的。

    2.3 volatile修饰属性

    (1) volatile强制从公共堆栈中取得变量的值,而不是从线程私有数据栈取得变量值。

    (2) volatile与synchronized关键字比较

    1)volatile是线程同步的轻量级实现,其性能肯定比synchronized要好,并且volatile只能修饰变量,而synchronized可修饰方法、代码块。

    2)多线程访问volatile不会发生阻塞,而synchronized会出现阻塞。

    3) volatile保证数据的可见性,不保证原子性;而synchronized既可以保证原子性也间接保证可见性。

    4) volatile解决变量在多线程之间的可见性,而synchronized解决的是多线程之间访问资源的同步性。线程安全包括原子性和可见性。

     

    线程工作内存中: read  (load  use asign) store write 括号中的三步非原子性。

     

    (3)synchronized可以保证同一时刻,只有一个线程可以执行某一个方法或某一个代码块。包括两个特征:互斥性和可见性。同步synchronized不仅可以解决一个线程看到对象处于不一致状态,还可以保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护之前所有的修改效果。

    3.线程间通信

    3.1等待通知机制的实现

    (1)Wait()/notify()/notifyAll()都是Object类的方法。调用这些方法之前需先获得对象锁。调用wait()方法的线程将会在该代码行处停止且释放锁,进入“预执行队列”被唤醒之后才有机会获取锁。执行Notify()方法的线程不会释放锁,随机唤醒一个正在等待“共享资源”的线程,如果一个阻塞的共享资源的线程都没有,则忽略。notifyAll()唤醒全部的,等本线程退出CPU,这些线程去争抢锁。

    (2)每个锁对象都有两个队列,一个是就绪队列(可运行),一个是阻塞队列(要唤醒之后才是可运行)。

    (3)sleep方法不释放锁。遇到异常会释放锁。

    (4)wait(long)等待long时间,如果没有其他线程来唤醒它,自动唤醒。使用wait和notify时要注意wait条件发生变化,容易造成程序逻辑混乱。

    (5)生产者、消费者模式实现:生产者中Value值不为空,则lock.wait();否则进行设置值的操作,然后lock.notify()唤醒消费者。消费者中value为空,则lock.wait(),否则进行取值操作,然后lock.notify()唤醒生产者。

    多生产-单消费:使用notify会造成假死,使用notifyAll来解决。

    (6)通过管道流来进行线程间通信,PipedInputStream,PipedOutputStream,PipedReader和PipedWriter。最后outputStream.connect(inputStream)

    3.2方法join的使用

    (1)方法join是使所属的线程对象x执行run方法的任务,而使当前线程z进行无限期的阻塞,等待线程销毁后在继续执行z后面的代码。Join过程中,如果当前线程被中断,则出现异常。

    (2)join(long)内部是使用wait来实现的,所以会释放锁。Sleep却不会。

    3.3类ThreadLocal的使用

    (1)这个类解决每个线程都有自己的共享变量的问题。

    ThreadLocal t=new ThreadLocal(); 这个对象有get()、set()方法。解决的是变量在不同线程间的隔离性。也就是不同线程可以有自己的值,并放在ThreadLocal中保存。

    (2)InheritableThreadLocal类,可以在子线程中取得父线程继承下来的值。

     

    4.Lock的使用

    4.1 ReentrantLock类,目的是为了实现同步异步,比synchronized更加灵活。

    (1)首先 Lock lock =new ReentrantLock(); 然后在需要同步的方法中的首行使用lock.lock(),方法的结束行使用lock.unlock()。效果和使用synchronized关键字一样。当然也可以去锁一部分代码块。

    (2)Condition对象实现指定线程的等待、通知,相当于wait和notify升级版。Synchronized相当于只有一个condition对象。

    首先 Lock lock =new ReentrantLock();

         Condition condition=new Condition();

    在方法中先调用lock.lock()获得同步监视器(否则报异常),然后用condition.await()来让这个线程进入阻塞队列。

    (3)使用Condition实现等待通知

    conditionA.await()进入阻塞,conditionA.signal()唤醒指定的这个线程。

    signalAll()相当于notifyAll。

    (4)使用多个Condition实现通知部分线程

      Condition conditionA=new Condition();

      Condition conditionB=new Condition();

    conditionA.await()只有conditionA.signal(All)可以唤醒

    (5)也可以实现生产者消费者模式,lock.lock()和signall方法。

    (6)公平锁:线程获取的顺序是按照线程加锁的顺序来分配的,先来先得。非公平锁,采用抢占机制,随机获得锁。reentranLock类默认情况使用的是非公平锁。

    (7)int getHoldCount()返回当前线程调用lock()方法的次数。

    (8)int getQueueLength()返回正在等待获取此锁定的线程估计数。

    (9)int getWaitQueueLength(Condition condition )返回等待此锁定相关的给定条件condition的线程估计数。

    (10)boolean hasQueuedThread(thread) 查询指定线程是否在等待此锁,

    方法boolean hasWaiters(Condition condition) 作用是查询是否有线程正在等待与此锁定有关的condition条件。

    (11)可使用多个Condition对象实现业务的顺序执行。交替链接唤醒。

    A方法中 conditionA.await()---conditionB.signalAll--

    B方法中 conditionB.await()---conditionC.signalAll--

    C方法中 conditionC.await()---conditionA.signalAll--

     

    4.2ReentrantReadWriteLock类

    读锁是共享锁,写锁是排他锁,读读共享是异步的,写读、写写都是互斥的同步的。

    (1)ReentrantReadWriteLock类实现读读共享

    ReentrantReadWriteLock lock=new ReentrantReadWriteLock();

     lock.readLock().lock();

    ……代码段

    lock.readLock().unlock()

    是异步的

    (2)ReentrantReadWriteLock类实现写写互斥

    ReentrantReadWriteLock lock=new ReentrantReadWriteLock();

     lock.WriteLock().lock();

    ……代码段

    lock.WriteLock().unlock()

    是同步的

    (3)ReentrantReadWriteLock类实现读写互斥

    ReentrantReadWriteLock lock=new ReentrantReadWriteLock();

    方法1

     lock.readLock().lock();

    ……代码段

    lock.readLock().unlock()

    方法2

    lock.WriteLock().lock();

    ……代码段

    lock.WriteLock().unlock()

    两个方法是互斥的。

     

    5.单例模式与多线程

    (1)立即加载/饿汉模式

    立即加载是在使用类的时候对象就已经创建完毕,常见的实现办法就是new实例化。

    Public class MyObject{

    private static MyObject myObject=new MyObject();

    private MyObject(){

    }

    public static MyObject getInstance(){

    Return myObject

    }

    }

    (2)延迟加载/懒汉模式

    在调用getInstance()方法时实例才被创建,在方法中实例化对象。

    Public class MyObject{

    private static MyObject myObject;

    private MyObject(){

    }

    public static MyObject getInstance(){

    if(myObject!=null){

     

    }else{

    myObject=new MyObject();

    }

    return myObject

    }

    }

    懒汉模式在多线程环境下容易出错,无法保持单例。

    解决方案:

    1.方法声明synchronized-但是效率低下

    2.同步代码块—(全部方法)效率低,(关键语句)无法单例。

    3.同步代码块,使用DCL双检测,多数采用这种方式。即是说在内部关键方法里再判断一次。

    Public class MyObject{

    Valotile private static MyObject myObject;

    Public static MyObject getInstance(){

    if(myObject!=null){

    }else{

    Thread.sleep(3000);

    Synchronized(MyObject.class){

    If(myObject==null){

    myObject=new MyObject();

     

    }

    }

    }

    return myObject

    }

    }

    4.static代码块实现单例模式

    利用静态代码块在使用类时就已经执行的特点。

     

     

    三年程序员,专注语音文本分析、大数据挖掘、预训练模型及知识图谱相关技术的探索
  • 相关阅读:
    html5学习笔记2——新元素
    html5学习笔记——基础
    html学习笔记之2——多媒体
    Python调试打印错误信息
    Python随机字符串验证码
    js传递数组
    js上传图片并预览
    JS获取当前日期、比较日期大小
    nrm管理npm源
    使用Git Subtree在多个项目中共用同一个子项目
  • 原文地址:https://www.cnblogs.com/jetHu/p/7010901.html
Copyright © 2011-2022 走看看