Java 进阶7 并发优化 5 并发控制板方法 20131114
前言:
         Java 中多线程并发程序中存在线程安全的问题,之前学习 Java的同步机制,掌握的同步方法只有一种就是使用 synchronized关键字,解决线程之间的同步问题。同时在操作系统 C++多线程中也了解到其他的多线程同步机制:比如信号量、临界区、互斥锁等等
          在这里系统的整理一下在 Java中实现线程同步的机制:内部锁,重入锁,读写锁,信号量等等。
volatile 关键字,保证读写的共享对象时在共享内存中的,而不是线程中的副本,但是不保证线程安全。
Synchronized关键字,可以锁住当前对象,或者是类的对象,同时还有代码块等等,用法之前介绍过,同时还有配合使用的 obj.wait() obj.notify()
ReentrantLock重入锁:相比synchronized可以同步
ReentrantReadWriteLock读写锁,获取读写锁到两个对象
Condition对象:通过Lock 中newCondition获取一个 Condition对象实例。其中的await() signal()类似 wait()和notify() 方法。
Semaphore信号量机制:方法有acquire和 release
ThreadLocal机制,使用空间换时间的理念,为每一个线程存储一个副本。
1.Java 内存模型和 volatile
         Java 中每一个线程都会有自己的内存工作区,其中存放着被所有线程共享的主内存变量的值得拷贝。当线程执行的时候,他在自己的工作内存中操作变量。为了取得一个共享变量,一个线程通常是先获取锁定并且清除其他工作内存区,保证共享变量从所有的线程的内存共享区正确的装入到线程的内存工作区,当线程解锁的时候保证工作内存区中变量的值写回到共享内存区。
          线程中可以执行的操作 use assign load store lock unlock.而主内存可以执行的操作有 read write lock unlock. 每一个操作都是原子的。
Java 进阶7 并发优化 5 并发控制板方法 20131114
前言:
         Java 中多线程并发程序中存在线程安全的问题,之前学习 Java的同步机制,掌握的同步方法只有一种就是使用 synchronized关键字,解决线程之间的同步问题。同时在操作系统 C++多线程中也了解到其他的多线程同步机制:比如信号量、临界区、互斥锁等等
          在这里系统的整理一下在 Java中实现线程同步的机制:内部锁,重入锁,读写锁,信号量等等。
volatile 关键字,保证读写的共享对象时在共享内存中的,而不是线程中的副本,但是不保证线程安全。
Synchronized关键字,可以锁住当前对象,或者是类的对象,同时还有代码块等等,用法之前介绍过,同时还有配合使用的 obj.wait() obj.notify()
ReentrantLock重入锁:相比synchronized可以同步
ReentrantReadWriteLock读写锁,获取读写锁到两个对象
Condition对象:通过Lock 中newCondition获取一个 Condition对象实例。其中的await() signal()类似 wait()和notify() 方法。
Semaphore信号量机制:方法有acquire和 release
ThreadLocal机制,使用空间换时间的理念,为每一个线程存储一个副本。
1.Java 内存模型和 volatile
         Java 中每一个线程都会有自己的内存工作区,其中存放着被所有线程共享的主内存变量的值得拷贝。当线程执行的时候,他在自己的工作内存中操作变量。为了取得一个共享变量,一个线程通常是先获取锁定并且清除其他工作内存区,保证共享变量从所有的线程的内存共享区正确的装入到线程的内存工作区,当线程解锁的时候保证工作内存区中变量的值写回到共享内存区。
          线程中可以执行的操作 use assign load store lock unlock.而主内存可以执行的操作有 read write lock unlock. 每一个操作都是原子的。
          当一个线程使用一个变量的时候,不论程序是否争取的使用线程同步操作,他获得值一定是由它本身或者其他线程存储到变量的值。
          每一个线程都有自己的工作内存区,因此当一个线程改变自己的内存中的数据的时候,对于其他线程是不可见的,因此可以使用关键字 volatile关键字强制多线程读写主内存中的数据,从而使得 volatile变量在多线程中是可见的。
          声明变量时 volatile的时候,可以保证以下几点:
          其他线程对于变量的修改可以及时反映在当前线程中;
          确保当前线程对 volatile变量的修改,能够及时的写回主内存中,并且被其他的线程可见;
          使用volatile可以确保编译的时候保证有序性。
          但是volatile并不保证数据的线程安全
2. 同步关键字 synchronized
          最常用的同步关键字 synchronized,相比其他的同步机制synchronized关键字更加简洁明了,便于代码的维护。
         1)Synchronized 可以锁定一个方法,获得的是当前对象的锁
         public synchronized void func(){}
         2)Synchronized 同步代码块,同步更为精准,这样的话,缩小了同步的范围,提高了性能。
         3) 同步静态方法,获得的是该类的对象上面,执行方法之前首先要获得当前类的对象的锁。同时为了实现多线程之间的交互,还应该使用 wait()/ notify() notifyAll()
         synchronized(obj){
         while(condition){
         obj.wait();// 这样因为不满足继续执行的条件,所以 wait会释放当前对象的锁,供其他线程使用该对象。当等待的一个线程受到 notify的时候,就会唤醒被阻塞的线程,当有多个线程被同一个对象阻塞的时候,只会唤醒一个线程。
}
}
3.ReentrantLock 重入锁
          ReentrantLock是重入锁,他比 synchronized内部锁拥有更加强大的功能,他可以中断,可以定时,在高并发的情况下,比 synchronized有着明显的优势。JDK6中差别不是太明显。同时 ReentrantLock提供了公平和不公平的两种锁机制。公平锁保证等待锁的线程
是公平的,不存在插队的情况,总是按照队列的方式先进先出;
          但还不公平锁是可以插队的,在性能上来说,非公平锁的兴根更佳。
          构造函数 public ReentrantLock(Boolean fair);决定重入锁是否是公平的。并且需要记得在使用完成之后一定要释放锁,一般是放在 finally中释放的。
         ReentrantLock 提供的重要的方法:
         lock() :获得锁,如果被占用,则等待;
         lockInterruptibly(): 获得锁,但优先响应中断
         tryLock() 尝试获得锁,如果成功返回 true,反之,返回false,该方法是不会等待的,立即返回
         tryLock(long time, TimeUnit unit);
         unlock(): 释放锁
4.ReadWriteLock 读写,有效的减少所锁的竞争机制,提升系统的性能。读写锁允许多个线程同时读,但是考虑到数据之间的完整性,写写操作和读写操作需要相互等待和持有锁。如果在系统中读的次数远远大于写的次数的话,则读写多会发挥很大的优势。
ReentrantReadWriteLock
private static Lock lock  = new ReentrantLock();
private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private static Lock readLock = readWriteLock.readLock();
private static Lock writeLock = readWriteLock.writeLock();
private static int value = 10;
public int handleRead() throws InterruptedException{
    try{
         readLock.lock();
        Thread. sleep(2000);
         return value ;
    }finally{
         readLock.unlock();
    }   
}
public void handleWrite(int v) throws InterruptedException{
    try{
         writeLock.lock();
        Thread. sleep(2000);
         value = v;
    }finally{
         writeLock.unlock();
    }
}
         JDK1.5 之后提供的读写锁分离,对于读操作较多的并发系统中,使用读写锁的机制会提高程序的性能。
5.Condition 对象
          线程之间协调工作使用的是 Condition对象。Condition是于锁相关的,通过 Lock接口的Condition newCondition()方法可以生成一个和锁绑定的 Condition实例。Condition 对象和Lock的关系就是 Object.wait()和Object.notify() 的关系一样,它们可以配合使用完成多线程的协作任务。
          Condition提供的函数有:
          void await() ;当前线程等待,同时释放 lock在其他的线程中使用signal() 或者是 signalAll(),才可以唤醒当前线程;
         void awaitUninterruptibly(); 等待过程不被中断 ,功能是和await 相同
         void signal() 唤醒一个的等待的线程,相对于 signalAll()方法,会唤醒所有等待的线程。
6.Semaphore 信号量
          信号量为多线程写作提供了强大的控制方法,信号量是对锁的扩展,无论是内部锁( synchronized)还是重入锁(ReentrantLock),一次只允许一个线程访问资源,但是信号量确可以指定多个线程同时访问某一个资源。
         public Semaphore(int permits);
         public Semaphore(int permits, boolean fair);
          在构造信号量对象的时候,必须指定信号量的准入线程数目,其主要的方法有:
         public void acquire();
         public void acquireUninterruptibly();
         public boolean tryAcquire();
         public boolean tryAcquire(long timeout, TimeUnit unit);
         public void release();
          一个对象池,其中对象的数量是 100,当他同时有超过100个对象的请求,资源池就会出现资源短缺,未获得资源的线程就必须等待,当某一个线程使用对象完毕的时候,就会将对象返回到资源池,此时会激活一个等待的线程。
7.ThreadLocal 线程局部变量
         ThreadLocal 是一种多线程间并发访问变量的解决方案,和 synchronized等锁方式是不同的,他完全不提供锁机制,而是使用的是空间换时间的手段,为每一线程提供变量的独立副本,来保证线程的安全。但是这种方式不是具有绝对的优势,在并发量不高的情况下,使用锁机制会更好,但是在高并发的情况下, ThreadLocal可以减少锁的竞争。
         ThreadLocal 接口:
         public void set(T val); 将次线程局部变量的当前线程副本设置成指定的值
         public T get(); 返回次线程局部变量在当前线程中的副本值
         public void remove(); 移除此线程局部变量当前线程值
public class ThreadLocalExample implements Runnable {
    public static final ThreadLocal<Date> localvar = new ThreadLocal<Date>();
    private long time ;
    public ThreadLocalExample( long time){
        this.time = time;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
       Date d = new Date(time );
        for(int i = 0; i< 100; i++){
            localvar.set(d);
            if(localvar .get().getTime() != time){
              System. out.println("id= " + time + " localvar = " + localvar.get().getTime() );
           }
       }
    }
}
并发的时候,可以保证多个线程间的 localvar是相互独立的,虽然没有同步操作,但是多个线程的数据是不会相互影响的。因此永远不会出现当前线程持有的时间和成员变量 time不一致。同时不同对象上的 Date对象副本并不是由ThreadLocal创建的,而且是必须在线程内创建,并保证不同线程间实例均不相同。如果多个线程使用的是同一个 Date对象实例,及时方法ThreadLocal中保护起来也是没有用的。
 
YangTengfei
2013.11.23
          当一个线程使用一个变量的时候,不论程序是否争取的使用线程同步操作,他获得值一定是由它本身或者其他线程存储到变量的值。
          每一个线程都有自己的工作内存区,因此当一个线程改变自己的内存中的数据的时候,对于其他线程是不可见的,因此可以使用关键字 volatile关键字强制多线程读写主内存中的数据,从而使得 volatile变量在多线程中是可见的。
          声明变量时 volatile的时候,可以保证以下几点:
          其他线程对于变量的修改可以及时反映在当前线程中;
          确保当前线程对 volatile变量的修改,能够及时的写回主内存中,并且被其他的线程可见;
          使用volatile可以确保编译的时候保证有序性。
          但是volatile并不保证数据的线程安全
2. 同步关键字 synchronized
          最常用的同步关键字 synchronized,相比其他的同步机制synchronized关键字更加简洁明了,便于代码的维护。
         1)Synchronized 可以锁定一个方法,获得的是当前对象的锁
         public synchronized void func(){}
         2)Synchronized 同步代码块,同步更为精准,这样的话,缩小了同步的范围,提高了性能。
         3) 同步静态方法,获得的是该类的对象上面,执行方法之前首先要获得当前类的对象的锁。同时为了实现多线程之间的交互,还应该使用 wait()/ notify() notifyAll()
         synchronized(obj){
         while(condition){
         obj.wait();// 这样因为不满足继续执行的条件,所以 wait会释放当前对象的锁,供其他线程使用该对象。当等待的一个线程受到 notify的时候,就会唤醒被阻塞的线程,当有多个线程被同一个对象阻塞的时候,只会唤醒一个线程。
}
}
3.ReentrantLock 重入锁
          ReentrantLock是重入锁,他比 synchronized内部锁拥有更加强大的功能,他可以中断,可以定时,在高并发的情况下,比 synchronized有着明显的优势。JDK6中差别不是太明显。同时 ReentrantLock提供了公平和不公平的两种锁机制。公平锁保证等待锁的线程
是公平的,不存在插队的情况,总是按照队列的方式先进先出;
          但还不公平锁是可以插队的,在性能上来说,非公平锁的兴根更佳。
          构造函数 public ReentrantLock(Boolean fair);决定重入锁是否是公平的。并且需要记得在使用完成之后一定要释放锁,一般是放在 finally中释放的。
         ReentrantLock 提供的重要的方法:
         lock() :获得锁,如果被占用,则等待;
         lockInterruptibly(): 获得锁,但优先响应中断
         tryLock() 尝试获得锁,如果成功返回 true,反之,返回false,该方法是不会等待的,立即返回
         tryLock(long time, TimeUnit unit);
         unlock(): 释放锁
4.ReadWriteLock 读写,有效的减少所锁的竞争机制,提升系统的性能。读写锁允许多个线程同时读,但是考虑到数据之间的完整性,写写操作和读写操作需要相互等待和持有锁。如果在系统中读的次数远远大于写的次数的话,则读写多会发挥很大的优势。
ReentrantReadWriteLock
private static Lock lock  = new ReentrantLock();
private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private static Lock readLock = readWriteLock.readLock();
private static Lock writeLock = readWriteLock.writeLock();
private static int value = 10;
public int handleRead() throws InterruptedException{
    try{
         readLock.lock();
        Thread. sleep(2000);
         return value ;
    }finally{
         readLock.unlock();
    }   
}
public void handleWrite(int v) throws InterruptedException{
    try{
         writeLock.lock();
        Thread. sleep(2000);
         value = v;
    }finally{
         writeLock.unlock();
    }
}
         JDK1.5 之后提供的读写锁分离,对于读操作较多的并发系统中,使用读写锁的机制会提高程序的性能。
5.Condition 对象
          线程之间协调工作使用的是 Condition对象。Condition是于锁相关的,通过 Lock接口的Condition newCondition()方法可以生成一个和锁绑定的 Condition实例。Condition 对象和Lock的关系就是 Object.wait()和Object.notify() 的关系一样,它们可以配合使用完成多线程的协作任务。
          Condition提供的函数有:
          void await() ;当前线程等待,同时释放 lock在其他的线程中使用signal() 或者是 signalAll(),才可以唤醒当前线程;
         void awaitUninterruptibly(); 等待过程不被中断 ,功能是和await 相同
         void signal() 唤醒一个的等待的线程,相对于 signalAll()方法,会唤醒所有等待的线程。
6.Semaphore 信号量
          信号量为多线程写作提供了强大的控制方法,信号量是对锁的扩展,无论是内部锁( synchronized)还是重入锁(ReentrantLock),一次只允许一个线程访问资源,但是信号量确可以指定多个线程同时访问某一个资源。
         public Semaphore(int permits);
         public Semaphore(int permits, boolean fair);
          在构造信号量对象的时候,必须指定信号量的准入线程数目,其主要的方法有:
         public void acquire();
         public void acquireUninterruptibly();
         public boolean tryAcquire();
         public boolean tryAcquire(long timeout, TimeUnit unit);
         public void release();
          一个对象池,其中对象的数量是 100,当他同时有超过100个对象的请求,资源池就会出现资源短缺,未获得资源的线程就必须等待,当某一个线程使用对象完毕的时候,就会将对象返回到资源池,此时会激活一个等待的线程。
7.ThreadLocal 线程局部变量
         ThreadLocal 是一种多线程间并发访问变量的解决方案,和 synchronized等锁方式是不同的,他完全不提供锁机制,而是使用的是空间换时间的手段,为每一线程提供变量的独立副本,来保证线程的安全。但是这种方式不是具有绝对的优势,在并发量不高的情况下,使用锁机制会更好,但是在高并发的情况下, ThreadLocal可以减少锁的竞争。
         ThreadLocal 接口:
         public void set(T val); 将次线程局部变量的当前线程副本设置成指定的值
         public T get(); 返回次线程局部变量在当前线程中的副本值
         public void remove(); 移除此线程局部变量当前线程值
public class ThreadLocalExample implements Runnable {
    public static final ThreadLocal<Date> localvar = new ThreadLocal<Date>();
    private long time ;
    public ThreadLocalExample( long time){
        this.time = time;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
       Date d = new Date(time );
        for(int i = 0; i< 100; i++){
            localvar.set(d);
            if(localvar .get().getTime() != time){
              System. out.println("id= " + time + " localvar = " + localvar.get().getTime() );
           }
       }
    }
}
并发的时候,可以保证多个线程间的 localvar是相互独立的,虽然没有同步操作,但是多个线程的数据是不会相互影响的。因此永远不会出现当前线程持有的时间和成员变量 time不一致。同时不同对象上的 Date对象副本并不是由ThreadLocal创建的,而且是必须在线程内创建,并保证不同线程间实例均不相同。如果多个线程使用的是同一个 Date对象实例,及时方法ThreadLocal中保护起来也是没有用的。
 
YangTengfei
2013.11.23

Java 进阶7 并发优化 5 并发控制板方法的更多相关文章

  1. Java 进阶7 并行优化 JDK多任务执行框架技术

    Java 进阶7 并行优化 JDK多任务执行框架技术 20131114          Java 语言本身就是支持多线程机制的,他提供了 Thread 类 Runnable 接口等简单的多线程支持工 ...

  2. Java进阶知识点:服务端高并发的基石 - NIO与Reactor AIO与Proactor

    一.背景 要提升服务器的并发处理能力,通常有两大方向的思路. 1.系统架构层面.比如负载均衡.多级缓存.单元化部署等等. 2.单节点优化层面.比如修复代码级别的性能Bug.JVM参数调优.IO优化等等 ...

  3. 项目四:Java秒杀系统方案优化-高性能高并发实战

    技术栈 前端:Thymeleaf.Bootstrap.JQuery 后端:SpringBoot.JSR303.MyBatis 中间件:RabbitMQ.Redis.Druid 功能模块 分布式会话,商 ...

  4. Java进阶7并发优化4——JDK并发数据结构

    Java进阶7并发优化4——JDK并发数据结构20131114 由于并发程序和串行程序的不同特点,在串行程序中使用的数据结构可能无法在并行程序中直接的正常使用,因为这些数据结构可能不是线程安全的,所以 ...

  5. Java进阶7 并发优化2 并行程序设计模式

    Java进阶7 并发优化2 并行程序设计模式20131114 1.Master-worker模式 前面讲解了Future模式,并且使用了简单的FutureTask来实现并发中的Future模式.下面介 ...

  6. Java高并发秒杀API之高并发优化

    ---恢复内容开始--- 第1章 秒杀系统高并发优化分析   1.为什么要单独获得系统时间 访问cdn这些静态资源不用请求系统服务器 而CDN上没有系统时间,需要单独获取,获取系统时间不用优化,只是n ...

  7. Java进阶知识点:并发容器背后的设计理念

    一.背景 容器是Java编程中使用频率很高的组件,但Java默认提供的基本容器(ArrayList,HashMap等)均不是线程安全的.当容器和多线程并发编程相遇时,程序员又该何去何从呢? 通常有两种 ...

  8. Java进阶知识点6:并发容器背后的设计理念 - 锁分段、写时复制和弱一致性

    一.背景 容器是Java编程中使用频率很高的组件,但Java默认提供的基本容器(ArrayList,HashMap等)均不是线程安全的.当容器和多线程并发编程相遇时,程序员又该何去何从呢? 通常有两种 ...

  9. 【Java虚拟机5】Java内存模型(硬件层面的并发优化基础知识--指令乱序问题)

    前言 其实之前大家都了解过volatile,它的第一个作用是保证内存可见,第二个作用是禁止指令重排序.今天系统学习下为什么CPU会指令重排. 存储器的层次结构图 1.CPU乱序执行指令的根源 CPU读 ...

随机推荐

  1. 1-CommonJs

    诞生背景JS没有模块系统.标准库较少.缺乏包管理工具:前端端没有模块化编程还可以,因为前端逻辑没那么复杂,可以工作下去,在服务器端逻辑性那么强必须要有模块为了让JS可以在任何地方运行,以达到Java. ...

  2. BFC 详说 Block Formatting Contexts (块级格式化上下文)

    定位方案是控制元素的布局,在 CSS 2.1 中,有三种定位方案——普通流 (Normal Flow) .浮动 (Floats) 和绝对定位 (Absolute Positioning) ,下面分别对 ...

  3. GYM - 101490 J Programming Tutors (匈牙利+二分)

    题意:有N个学生和N个老师,每个人都有自己的坐标X,Y,给每个学生匹配一个老师,要求N个匹配中的距离最大值最小.其中距离的定义为:|X − X’| + |Y − Y ‘|. 分析:一道典型的最大值最小 ...

  4. $PDB——Python调试利器详解

    python 2.7 pdb官方文档:https://docs.python.org/2.7/library/pdb.html pdb是ptyhon内置的一个调试库,是调试python代码的好帮手,本 ...

  5. CentOS下yum安装FFmpeg

    一.yum安装FFmpeg 1.    最偷懒的方式就是yum安装了,自动解决依赖.不过CentOS系统默认无FFmpeg源,企业版 Linux 附加软件包EPEL源也不包含,需要手动添加yum源配置 ...

  6. GoEasyWeb实时推送

    GoEasyWeb实时推送,轻松实现实时消息推送. Web页面订阅(约5行代码),服务器端推送(2行代码)就可以轻松实现,而且在高并发时消息推送稳定. 自己完全可以只花五分钟写出属于自己的第一个实时推 ...

  7. flume从log4j收集日志输出到kafka

    1. flume安装 (1)下载:wget http://archive.cloudera.com/cdh5/cdh/5/flume-ng-1.6.0-cdh5.7.1.tar.gz (2)解压:ta ...

  8. 20145328 《Java程序设计》第2周学习总结

    20145328 <Java程序设计>第2周学习总结 教材学习内容总结 掌握了上周没有学会的IDEA的用法 掌握了一些快捷键用法,在用IDEA编写程序的过程中的体验比直接使用cmd进行编写 ...

  9. php7不支持curl

    百度出来的东西没有一个有用的 终极解决方案: 1.将extension=curl前的分号去掉: 2.将php目录下的libssh2.dll放到apache安装目录的bin目录下 3.重启apache ...

  10. 【bzoj2734】集合选数(有点思维的状压dp)

    题目传送门:bzoj2734 这题一个月前看的时候没什么头绪.现在一看,其实超简单. 我们对于每个在$ [1,n] $范围内的,没有因数2和3的数$ d $,将它的倍数$ 2^a 3^b d $一起处 ...