Java并发程序设计-总览学习

1、使用线程的一些经验

  • 设置名称

    无论何种方式,启动一个线程,就要给它一个名字!这对排错诊断系统监控有帮助。否则诊断问题时,无法直观知道某个线程的用途。
  • 响应中断

    程序应该对线程中断作出恰当的响应。
  • 使用ThreadLocal

    它的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。

    注意:使用ThreadLocal,一般都是声明在静态变量中,如果不断的创建ThreadLocal而且没有调用其remove方法,将会导致内存泄露。

2、Executor :ExecutorService和Future

为了方便并发执行任务,出现了一种专门用来执行任务的实现,也就是Executor。由此,任务提交者不需要再创建管理线程,使用更方便,也减少了开销。
Java并发程序设计-总览学习_第1张图片

java.util.concurrent.Executors是Executor的工厂类,通过Executors可以创建你所需要的Executor。

ExecutorService executor = Executors.newSingleThreadExecutor();
Callable task = new Callable < Object > () {
    public Object call () throws Exception {
        Object result = "...";
        return result;
    }
} ;
Future future = executor.submit(task);
future.get(); 
 

有两种任务:Runnable、Callable,Callable是需要返回值的任务。
Java并发程序设计-总览学习_第2张图片

Task Submitter把任务提交给Executor执行,他们之间需要一种通讯手段,这种手段的具体实现,通常叫做Future。Future通常包括get(阻塞至任务完成),cancel,get(timeout)(等待一段时间)等等。Future也用于异步变同步的场景。

3、阻塞队列: put和take、offer和poll、drainTo

Java并发程序设计-总览学习_第3张图片

阻塞队列,是一种常用的并发数据结构,常用于生产者-消费者模式。
在Java中,有三种常用的阻塞队列:
ArrayBlockingQueue
LinkedBlockingQueue
SynchronousQueue

使用阻塞队列:
Java并发程序设计-总览学习_第4张图片

Monitor的理论模型
Java并发程序设计-总览学习_第5张图片

4、ReentrantLock和Synchronized

Synchronized是Lock的一种简化实现,一个Lock可以对应多个Condition,而synchronized把Lock和Condition合并了,一个synchronizedLock只对应一个Condition,可以说Synchronized是Lock的简化版本。

在JDK5,Synchronized要比Lock慢很多,但是在JDK6中,它们的效率差不多。

Java并发程序设计-总览学习_第6张图片

5、Lock-free算法

LockFree算法,不需要加锁。
通常都是三个部分组成:
①循环
②CAS (CompareAndSet)
③break

Java并发程序设计-总览学习_第7张图片

进一步使用Lock-Free数据结构

class BeanManager {
    private ConcurrentMap map = new ConcurrentHashMap();

    public Object getBean
            (String key) {
        Object bean = map.get(key);
        if (bean == null) {
            map.putIfAbsent(key, createBean());
            bean = map.get(key);
        }
        return bean;
    }
}

ConcurrentHashMap并没有实现Lock-Free,只是使用了分离锁的办法使得能够支持多个Writer并发。ConcurrentHashMap需要使用更多的内存。

同样的思路用于更新数据库-乐观锁。

Lock-Free算法,可以说是乐观锁,如果非激烈竞争的时候,不需要使用锁,从而开销更小,速度更快。

适当使用CopyOnWriteArrayList,能够提高读操作时的效率。

6、关于锁使用的经验介绍

使用支持CAS的数据结构,避免使用锁,如:AtomicXXX、ConcurrentMap、CopyOnWriteList、ConcurrentLinkedQueue

一定要使用锁的时候,注意获得锁的顺序,相反顺序获得锁,就容易产生死锁。

死锁经常是无法完全避免的,鸵鸟策略被很多基础框架所采用。

通过Dump线程的StackTrace,例如linux下执行命令kill -3 ,或者jstack–l ,或者使用Jconsole连接上去查看线程的StackTrace,由此来诊断死锁问题。

外部锁常被忽视而导致死锁,例如数据库的锁

7、并发流程控制手段:CountDownlatch、Barrier

并发流程控制-使用CoutDownLatch

final int COUNT = 10;
final CountDownLatch completeLatch = new CountDownLatch(COUNT);
for (int i = 0; i < COUNT; ++i) {
    Thread thread = new Thread("worker thread " + i) {
        public void run() {
            // do xxxx
            completeLatch.countDown();
        }
    };
    thread.start();
}
// 当你启动了一个线程,你需要等它执行结束,此时,CountDownLatch也许是一个很好的选择。
completeLatch.await();
final CountDownLatch startLatch = new CountDownLatch(1);
for (int i = 0; i < 10; ++i) {
    Thread thread = new Thread("worker thread " + i) {
        public void run() {
            try {
                // 当你启动很多线程,你需要这些线程等到通知后才真正开始,CountDownLatch也许是一个很好的选择。
                startLatch.await();
            } catch (InterruptedException e) {
                return;
            }
            // do xxxx
        }
    };
    thread.start();
}
// do xxx
startLatch.countDown();

Barrier:
Java并发程序设计-总览学习_第8张图片

屏障是一种协调机制(一种算法),它迫使参与并发(或分布式)算法的进程等待,直到它们中的每一个都达到其程序中的某个点。这些协调点的集合被称为屏障。一旦所有流程都达到了障碍,它们都可以继续通过障碍。

8、定时器

使用定时器ScheduledExecutorService

Java并发程序设计-总览学习_第9张图片

java.util.concurrent.Executors是ScheduledExecutorService的工厂类,通过Executors,你可以创建你所需要的ScheduledExecutorService。

JDK 1.5之后有了ScheduledExecutorService,不建议你再使用java.util.Timer,因为它无论功能性能都不如ScheduledExecutorService。

大规模定时器TimerWheel

存在一种算法TimerWheel,适用于大规模的定时器实现。这个算法最早是被设计用来实现BSD 内核中定时器的,后来被广泛移植到诸如ACE 等框架中,堪称BSD 中经典算法之一,能针对定时器的各类常见操作提供接近常数时间的响应,且能根据需要很容易进行扩展。

Java并发程序设计-总览学习_第10张图片

9、并发三大定律:Amdahl、Gustafson、Sun-Ni

Amdahl 定律

Gene Amdahl 发现在计算机体系架构设计过程中,某个部件的优化对整个架构的优化和改善是有上限的。这个发现后来成为知名的Amdahl 定律。

Gustafson 定律

Gustafson假设随着处理器个数的增加,并行与串行的计算总量也是可以增加的。Gustafson定律认为加速系数几乎跟处理器个数成正比,如果现实情况符合Gustafson定律的假设前提的话,那么软件的性能将可以随着处理个数的增加而增加。

Sun-Ni 定律

充分利用存储空间等计算资源,尽量增大问题规模以产生更好/更精确的解。

你可能感兴趣的:(java并发编程)