一、线程同步

    当多个线程访问同一个数据时,非常容易出现线程安全问题。这时候就需要用线程同步。    
    不可变类总是线程安全的,因为它的对象状态是不可改变的,但可变类对象需要额外的方法来保证线程安全。

1、同步监视器

    释放同步监视器的锁定
    任何线程进入同步代码块、同步方法之前,必须先获得对同步监视器的锁定,那么何时会释放对同步监视器锁定?
  程序无法显示的释放对同步监视器的锁定,线程可以通过以下方式释放锁定:
    A、当线程的同步方法、同步代码库执行结束,就可以释放同步监视器
    B、当线程在同步代码库、方法中遇到break、return终止代码的运行,也可释放
    C、当线程在同步代码库、同步方法中遇到未处理的Error、Exception,导致该代码结束也可释放同步监视器
    D、当线程在同步代码库、同步方法中,程序执行了同步监视器对象的wait方法,导致方法暂停,释放同步监视器.
 
  下面情况不会释放同步监视器:
    A、当线程在执行同步代码库、同步方法时,程序调用了Thread.sleep()/Thread.yield()方法来暂停当前程序,当前程序不会释放同步监视器
    B、当线程在执行同步代码库、同步方法时,其他线程调用了该线程的suspend方法将该线程挂起,该线程不会释放同步监视器。注意尽量避免使用suspend、resume

2、synchronized

    法用synchronized同步关键字修饰,那么这个方法就是一个同步的方法。这样就只能有一个线程可以访问这个方法,
    在当前线程调用这个方法时,此方法是被锁状态,同步监视器是this。只有当此方法修改完毕后其他线程才能调用此方法。
    这样就可以保证线程的安全,处理多线程并发取钱的的安全问题。
  public synchronized void drawMoney(double money) {
//取钱操作
  }
    注意:synchronized可以修饰方法、代码块,但不能修饰属性、构造方法

3、同步锁(Lock)

    通常认为:Lock提供了比synchronized方法和synchronized代码块更广泛的锁定操作,Lock更灵活的结构,有很大的差别,并且可以支持多个Condition对象
    Lock是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,
    线程开始访问共享资源之前应先获得Lock对象。不过某些锁支持共享资源的并发访问,如:ReadWriteLock(读写锁),在线程安全控制中,
    通常使用ReentrantLock(可重入锁)。使用该Lock对象可以显示加锁、释放锁。
    class C {
//锁对象
private final ReentrantLock lock = new ReentrantLock();
......
//保证线程安全方法
public void method() {
//上锁
lock.lock();
try {
//保证线程安全操作代码
} catch() { } finally {
lock.unlock();//释放锁
}
}
}
    使用Lock对象进行同步时,锁定和释放锁时注意把释放锁放在finally中保证一定能够执行。
    使用锁和使用同步很类似,只是使用Lock时显示的调用lock方法来同步。而使用同步方法synchronized时系统会隐式使用当前对象作为同步监视器,同样都是“加锁->访问->释放锁”的操作模式,都可以保证只能有一个线程操作资源。
    同步方法和同步代码块使用与竞争资源相关的、隐式的同步监视器,并且强制要求加锁和释放锁要出现在一个块结构中,而且获得多个锁时,
    它们必须以相反的顺序释放,且必须在与所有锁被获取时相同的范围内释放所有资源。
    Lock提供了同步方法和同步代码库没有的其他功能,包括用于非块结构的tryLock方法,已经试图获取可中断锁lockInterruptibly()方法,
    还有获取超时失效锁的tryLock(long, timeUnit)方法。
    ReentrantLock具有重入性,也就是说线程可以对它已经加锁的ReentrantLock再次加锁,ReentrantLock对象会维持一个计数器来追踪lock方法的嵌套调用,
    线程在每次调用lock()加锁后,必须显示的调用unlock()来释放锁,所以一段被保护的代码可以调用另一个被相同锁保护的方法。

二、线程通信

(1)、线程的协调运行

        场景:用2个线程,这2个线程分别代表存款和取款。——现在系统要求存款者和取款者不断重复的存款和取款的动作,
        而且每当存款者将钱存入账户后,取款者立即取出这笔钱。不允许2次连续存款、2次连续取款。
        实现上述场景需要用到Object类,提供的wait、notify和notifyAll三个方法,这3个方法并不属于Thread类。但这3个方法必须由同步监视器调用,可分为2种情况:
        A、对于使用synchronized修饰的同步方法,因为该类的默认实例this就是同步监视器,所以可以在同步中直接调用这3个方法。
        B、对于使用synchronized修改的同步代码块,同步监视器是synchronized后可括号中的对象,所以必须使用括号中的对象调用这3个方法
        方法概述:
        一、wait方法:导致当前线程进入等待,直到其他线程调用该同步监视器的notify方法或notifyAll方法来唤醒该线程。
                wait方法有3中形式:无参数的wait方法,会一直等待,直到其他线程通知;带毫秒参数的wait和微妙参数的wait,
                这2种形式都是等待时间到达后苏醒。调用wait方法的当前线程会释放对该对象同步监视器的锁定。
        二、notify:唤醒在此同步监视器上等待的单个线程。如果所有线程都在此同步监视器上等待,则会随机选择唤醒其中一个线程。
            只有当前线程放弃对该同步监视器的锁定后(用wait方法),才可以执行被唤醒的线程。
        三、notifyAll:唤醒在此同步监视器上等待的所有线程。只有当前线程放弃对该同步监视器的锁定后,才能执行唤醒的线程。

(2)、条件变量控制协调

        如果程序不使用synchronized关键字来保证同步,而是直接使用Lock对象来保证同步,则系统中不存在隐式的同步监视器对象,也不能使用wait、notify、notifyAll方法来协调进程的运行。
        当使用Lock对象同步,Java提供一个Condition类来保持协调,使用Condition可以让那些已经得到Lock对象却无法组合使用,
        为每个对象提供了多个等待集(wait-set),这种情况下,Lock替代了同步方法和同步代码块,Condition替代同步监视器的功能。
        Condition实例实质上被绑定在一个Lock对象上,要获得特定的Lock实例的Condition实例,调用Lock对象的newCondition即可。
        Condition类方法介绍:
        一、await:类似于隐式同步监视器上的wait方法,导致当前程序等待,直到其他线程调用Condition的signal方法和signalAll方法来唤醒该线程。
            该await方法有跟多获取变体:long awaitNanos(long nanosTimeout),void awaitUninterruptibly()、awaitUntil(Date daadline)
        二、signal:唤醒在此Lock对象上等待的单个线程,如果所有的线程都在该Lock对象上等待,则会选择随机唤醒其中一个线程。
            只有当前线程放弃对该Lock对象的锁定后,使用await方法,才可以唤醒在执行的线程。
        三、signalAll:唤醒在此Lock对象上等待的所有线程。只有当前线程放弃对该Lock对象的锁定后,才可以执行被唤醒的线程。
     

(3)、使用管道流

        线程通信使用管道流,管道流有3种形式:
        PipedInputStream、PipedOutputStream、PipedReader和PipedWriter以及Pipe.SinkChannel和Pipe.SourceChannel,
        它们分别是管道流的字节流、管道字符流和新IO的管道Channel。
        管道流通信基本步骤:
        A、使用new操作法来创建管道输入、输出流
        B、使用管道输入流、输出流的connect方法把2个输入、输出流连接起来
        C、将管道输入、输出流分别传入2个线程
        D、2个线程可以分别依赖各自的管道输入流、管道输出流进行通信

Reference:

  Java Thread 多线程理论 目录索引

  Java Thread 多线程 介绍

  Java Thread 多线程 操作线程

  Java Thread 多线程同步、锁、通信

  Java Thread 多线程 线程池

Java线程同步和线程通信的更多相关文章

  1. 关于Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇高质量的博文)

    Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文) 前言:在学习多线程时,遇到了一些问题,这里我将这些问题都分享出来,同时也分享了几篇其他博客主的博客,并且将我个人的理解也分享 ...

  2. C# 多线程编程第二步——线程同步与线程安全

    上一篇博客学习了如何简单的使用多线程.其实普通的多线程确实很简单,但是一个安全的高效的多线程却不那么简单.所以很多时候不正确的使用多线程反倒会影响程序的性能. 下面先看一个例子 : class Pro ...

  3. iOS开发——高级篇——线程同步、线程依赖、线程组

    前言 对于iOS开发中的网络请求模块,AFNet的使用应该是最熟悉不过了,但你是否把握了网络请求正确的完成时机?本篇文章涉及线程同步.线程依赖.线程组等专用名词的含义,若对上述名词认识模糊,可先进行查 ...

  4. Java并发——线程安全、线程同步、线程通信

    线程安全 进程间"共享"对象 多个“写”线程同时访问对象. 例:Timer实例的num成员,即add()方法是用的次数.即Timer实例是资源对象. class TestSync ...

  5. Java多线程(二) —— 线程安全、线程同步、线程间通信(含面试题集)

    一.线程安全 多个线程在执行同一段代码的时候,每次的执行结果和单线程执行的结果都是一样的,不存在执行结果的二义性,就可以称作是线程安全的. 讲到线程安全问题,其实是指多线程环境下对共享资源的访问可能会 ...

  6. java多线程同步以及线程间通信详解&消费者生产者模式&死锁&Thread.join()(多线程编程之二)

    本篇我们将讨论以下知识点: 1.线程同步问题的产生 什么是线程同步问题,我们先来看一段卖票系统的代码,然后再分析这个问题: package com.zejian.test; /** * @author ...

  7. Java-多线程第三篇3种创建的线程方式、线程的生命周期、线程控制、线程同步、线程通信

    1.Java使用Thread类代表线程.     所有的线程对象必须是Thread类或其子类的实例. 当线程继承Thread类时,直接使用this即可获取当前线程,Thread对象的getName() ...

  8. 多线程,线程类三种方式,线程调度,线程同步,死锁,线程间的通信,阻塞队列,wait和sleep区别?

    重难点梳理 知识点梳理 学习目标 1.能够知道什么是进程什么是线程(进程和线程的概述,多进程和多线程的意义) 2.能够掌握线程常见API的使用 3.能够理解什么是线程安全问题 4.能够知道什么是锁 5 ...

  9. Python并发编程-进程 线程 同步锁 线程死锁和递归锁

    进程是最小的资源单位,线程是最小的执行单位 一.进程 进程:就是一个程序在一个数据集上的一次动态执行过程. 进程由三部分组成: 1.程序:我们编写的程序用来描述进程要完成哪些功能以及如何完成 2.数据 ...

随机推荐

  1. JAVA基础讲义

    一.安装JDK 第一步:双击JDK的exe文件. JDK(Java开发包),JRE(Java运行环境) 第二步:配置 path:jdk的根目录,jdk下的bin目录(两个目录之间记得用分号隔开) cl ...

  2. 最有用的Linux命令行使用技巧集锦

    最近在Quora上看到一个问答题目,关于在高效率Linux用户节省时间Tips.将该题目的回答进行学习总结,加上自己的一些经验,记录如下,方便自己和大家参考. 下面介绍的都是一些命令行工具,这些工具在 ...

  3. SQL server 2008数据库的备份与还原(转)

      一.SQL数据库的备份: 1.依次打开 开始菜单 → 程序 → Microsoft SQL Server 2008 → SQL Server Management Studio → 数据库:Dsi ...

  4. nodejs express测试

    1.页面请求 app.get('/list_user', function (req, res) { console.log("/list_user GET 请求"); //res ...

  5. 通过runtime替换系统类实现的代码(从github开源库fdstackview中摘录)

    其中部分代码为汇编:由此可见oc的runtime的灵活性和能力.此代码仅供参考 // ---------------------------------------------------- // R ...

  6. HBase的二级索引,以及phoenix的安装(需再做一次)

    一:HBase的二级索引 1.讲解 uid+ts 11111_20161126111111:查询某一uid的某一个时间段内的数据 查询某一时间段内所有用户的数据:按照时间 索引表 rowkey:ts+ ...

  7. 执行动态sql返回参数

    ref: https://support.microsoft.com/en-us/kb/262499 ) ) DECLARE @IntVariable INT ) SET @SQLString = N ...

  8. Prestashop 页面空白

    Advanced Parameters > Performance页面空白,无任何提示错误,解决方法: 更改文件/cache/class_index.php 权限为666

  9. LightOj1056 - Olympics(简单数学题)

    题目链接:http://lightoj.com/volume_showproblem.php?problem=1056 题意:已知体育场的形状是由一个矩形+两边的两个部分组成,两边的两个部分是属于同一 ...

  10. sql server 2008查询窗口怎么显示行数

    工具->选项