java并发编程实战学习笔记之对象的组合与基础构建模块

第四章 对象的组合

4.1 构建安全的类

4.2 实例封闭

@ThreadSafe
public class PersonSet {
    @GuardedBy("this") private final Set<Person> mySet = new HashSet<Person>();

    public synchronized void addPerson(Person p) {
        mySet.add(p);
    }

    public synchronized boolean containsPerson(Person p) {
        return mySet.contains(p);
    }

    interface Person {
    }
}

对personset的对象myset进行了封装,但是person的安全性未知,也叫java监视器模式

使用私有锁来保护对象

public class PrivateLock {
    private final Object myLock = new Object();
    @GuardedBy("myLock") Widget widget;

    void someMethod() {
        synchronized (myLock) {
            // Access or modify the state of widget
        }
    }
}

4.3 线程安全性的委托

不可变的域是线程安全的,可以被自由的共享与发布

应用线程安全的容器来封装类中的状态域来实现线程安全,将类的安全***给底层的其他类去实现,就叫做线程安全性委托,但是如果存在多个相互依赖的组合对象时,还必须用锁来实现安全性。

4.4 在现有的线程安全类中添加功能

1)在原始类中添加方法

2)通过继承来拓展原来的类

3)通过一个辅助类来添加功能

客户端加锁:

@NotThreadSafe
class BadListHelper <E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());

    public synchronized boolean putIfAbsent(E x) {   //内置锁对于客户端来讲是不同的
        boolean absent = !list.contains(x);
        if (absent)
            list.add(x);
        return absent;
    }
}

@ThreadSafe
class GoodListHelper <E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());

    public boolean putIfAbsent(E x) {
        synchronized (list) {      //list对象锁对每个客户端来讲都是同一个
            boolean absent = !list.contains(x);
            if (absent)
                list.add(x);
            return absent;
        }
    }
}

上述第一个内置类是谁调用就是谁

组合:

@ThreadSafe
public class ImprovedList<T> implements List<T> {
    private final List<T> list;

    /**
     * PRE: list argument is thread-safe.
     */
    public ImprovedList(List<T> list) { this.list = list; }

    public synchronized boolean putIfAbsent(T x) {
        boolean contains = list.contains(x);
        if (contains)
            list.add(x);
        return !contains;
    }

将list引入作为自己的成员变量,此时的内置锁就是该类的对象本身

第五章 基础构建模块

5.1 同步容器类:Vector Hashtable

通过封装,对每个公有方法都进行同步,使得每次只能有一个线程来访问容器的状态,将所有对容器状态的访问都串行化,效率很低。

同步容器类都是线程安全的,但是需要自己加锁来执行复杂的复合操作,比如迭代等。迭代过程中,如果有其他线程在修改该容器,则会出现错误。设计同步类容器时并没有解决该问题,因此在使用中还是需要自己加锁来避免异常。但是这会使得效率很低,因为整个迭代期间其他线程不得访问,改进办法为容器克隆。

提防另外一种隐藏迭代:“helloworld”+set;将调用set的toString()函数,在该函数里面执行迭代,容器的hashcode和equals方法也会执行迭代。

5.2 并发容器:concurrentHashMap CopeOnWriteArrayList

concurrentHashMap:并不是将每个方法都放在同一个锁上同步并且同时只能有一个线程访问容器,二十使用了更细粒度的分段锁来实现更大程度的共享。其提供的迭代器不会抛出异常,因此不需要在迭代过程中加锁。

CopeOnWriteArrayList:用于替代同步list,迭代期间不需要加锁和复制。每次修改时都会复制底层数组,修改结束后重新发布一个新的容器版本。

5.3 阻塞队列和生产者-消费者模式

blockingQueue

5.4 阻塞方法与中断方法

当在代码中调用了一个将抛出interruptedException异常的方法时,自己就变成了一个可阻塞方法,在捕获到interruptedException异常时,最明智的选择是传递给调用者,即再次抛出interruptedException异常。如果不能抛出异常,则调用该线程的interrupt方法恢复中断,上层代码将看到引发了一个中断。比如:

public class TaskRunnable implements Runnable {
    BlockingQueue<Task> queue;

    public void run() {
        try {
            processTask(queue.take());
        } catch (InterruptedException e) {
            // restore interrupted status
            Thread.currentThread().interrupt();
        }
    }

    void processTask(Task task) {
        // Handle the task
    }

    interface Task {
    }
}

5.5 同步工具类

闭锁:countDownLatch

futureTask:异步事件

信号量:counting semaphore

栅栏:

闭锁用于等待事件,栅栏用于等待进程。栅栏可重置用于重复使用

时间: 2024-11-03 03:46:22

java并发编程实战学习笔记之对象的组合与基础构建模块的相关文章

java并发编程实战学习笔记之基础知识与对象的共享

第二章:线程安全性 2.1 什么是线程安全性 可以被多个线程调用,并且在线程之间不会出现错误的交互 方法内的局部变量不需要保护,因为它存储在栈中,是每个线程独有的 2.2 原子性 一个共享变量可以定义为原子变量:atomic 多个共享变量时,之间可能存在某种依赖关系,分别定义为原子变量会由于竞态条件,出现错误,比如先检查后执行. 竞态条件:某个计算的正确性取决于多个线程的交替执行时序时 这就需要复合操作,将多个变量的读写操作作为一个程序块,接下来采用 2.3 加锁机制 将一个复合程序块采用内置锁

java并发编程实战学习笔记之第三部分:活跃性、性能与测试

第十章 避免活跃性危险 锁顺序死锁:定义锁的顺序,可以通过某种方法决定每个锁的顺序,比如hashcode或者序列号之类的 在锁的调用顺序不是很明显的情况下,在持有锁的情况下调用其他外部方法一定要注意,可以通过开放调用,避免发生死锁的危险,即使用同步代码块保护仅仅保护那些共享变量即可,但这种降低锁粒度的方法可能会使得原本大的代码块失去原子性,解决办法为:将服务的状态改为关闭之前一直持有锁,状态改变之后,其他线程也就能够看到关闭信息从而不会再次执行关闭操作... 死锁的诊断与避免:    通过try

java并发编程实战学习笔记之任务执行

第六章 任务执行 6.1 在线程中执行任务 串行->多线程->有限个多线程 6.2 executor框架 通过有界队列可以防止高负荷程序过度消耗内存 java.lang.concurrent提供了线程池作为实现executor框架的一部分 executor接口:提供生产者-消费者模式 基于executor的web服务器: public class TaskExecutionWebServer {     private static final int NTHREADS = 100;     

java并发编程实战学习笔记之取消与关闭

第七章 取消与关闭 7.1 任务取消 方式一.通过volatile类型的域来保存取消状态 方式二.interrupt()方法 interrupt()可以中断目标线程 isinterrupted()方法用来检测目标线程的中断状态 interrupted()用于清除中断状态,并且返回之前的中断状态,这是唯一可以清除中断状态的方法,如果在调用该方法是返回了true,那么除非你想屏蔽这个中断,否则你必须对他进行处理,可以抛出interruptExeption异常或者重新通过interrupt来恢复中断状

JAVA并发编程实战 读书笔记(二)对象的共享

<java并发编程实战>读书摘要 birdhack 2015年1月2日 对象的共享 JAVA并发编程实战读书笔记 我们已经知道了同步代码块和同步方法可以确保以原子的方式执行操作,但一种常见的误解是,认为关键之synchronized只能用于实现原子性或者确定临界区.同步还有另一个重要的方面:内存可见性. 1.可见性 为了确保多个线程之间对内存写入操作的可见性,必须使用同步机制. 在没有同步的情况下,编译器.处理器以及运行时等都可能对操作的执行顺序进行一些意想不到的调整.在缺乏足够同步的多线程程

《java并发编程实战》笔记(一)

最近在看<java并发编程实战>,希望自己有毅力把它读完. 线程本身有很多优势,比如可以发挥多处理器的强大能力.建模更加简单.简化异步事件的处理.使用户界面的相应更加灵敏,但是更多的需要程序猿面对的是安全性问题.看下面例子: public class UnsafeSequence { private int value; /*返回一个唯一的数值*/ public int getNext(){ return value++; } } UnsafeSequence的问题在于,如果执行时机不对,那么

java并发编程实战学习(3)--基础构建模块

转自:java并发编程实战 5.3阻塞队列和生产者-消费者模式 BlockingQueue阻塞队列提供可阻塞的put和take方法,以及支持定时的offer和poll方法.如果队列已经满了,那么put方法将阻塞直到空间可用:如果队列为空,那么take方法将阻塞直到有元素可用.队列可以是有界的也可以是无界的. 如果生产者生成工作的速率比消费者处理工作的速率款,那么工作项会在队列中累计起来,最终好紧内存.同样,put方法的阻塞特性也极大地简化了生产者的编码.如果使用有界队列,当队列充满时,生产者将阻

读书笔记-----Java并发编程实战(二)对象的共享

1 public class NoVisibility{ 2 private static boolean ready; 3 private static int number; 4 private static class ReaderThread extends Thread{ 5 public void run(){ 6 while(!ready) 7 Thread.yield(); 8 System.out.println(number); 9 } 10 } 11 12 public s

JAVA并发编程实战 读书笔记(一)线程安全性

线程安全性   1.什么是线程安全 在线程安全的定义中,最核心的概念是正确性.正确性的含义是,某个类的行为与规范完全一致.当对正确性有了一个比较清晰的定义后,就可以定义线程安全性:当多个线程访问某个类时,这个类始终能表现出正确的行为,那这个类就是线程安全的. 举例:无状态对象一定是线程安全的. 大多数Servlet都是无状态的,当Servlet在处理请求时需要保存一些信息时,线程安全才会成为一个问题. 2.原子性 举个例子:语句 ++i:虽然递增操作++i是一种紧凑的语法,使其看上去是一个操作,