`

Java多线程和并发性

 
阅读更多
1. ThreadPoolExecutor

http://blog.csdn.net/wangwenhui11/article/details/6760474
http://iamzhongyong.iteye.com/blog/1458349

线程池类为 java.util.concurrent.ThreadPoolExecutor,常用构造方法为:

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,

long keepAliveTime, TimeUnit unit,

BlockingQueue<Runnable> workQueue,

RejectedExecutionHandler handler)


corePoolSize: 线程池维护线程的最少数量

maximumPoolSize:线程池维护线程的最大数量

keepAliveTime: 线程池维护线程所允许的空闲时间

unit: 线程池维护线程所允许的空闲时间的单位

workQueue: 线程池所使用的缓冲队列

handler: 线程池对拒绝任务的处理策略

一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是Runnable类型对象的run()方法。

当一个任务通过execute(Runnable)方法欲添加到线程池时:

l  如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。

l  如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。

l  如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。

l  如果此时线程池中的数量大于corePoolSize,缓冲队列 workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用 handler处理被拒绝的任务。

l  当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

排队

所有 BlockingQueue 都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互:

A.        如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。

B.        如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。

C.        如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。

排队有三种通用策略:

直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集合时出现锁定。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙的情况下将新任务加入队列。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。



2. ScheduledThreadPoolExecutor


3. ExecutorService

ThreadPoolExecutor简介: http://blog.csdn.net/longeremmy/article/details/8225498


ExecutorService的execute和submit方法
三个区别:

1、接收的参数不一样

2、submit有返回值,而execute没有

private final ExecutorService exec =  Executors.newCachedThreadPool();
Future<?> future = exec.submit(buildJob);
           synchronized (futures) {
               futures.put(buildJob.getBuild().getId(), future);
}

Method submit extends base method Executor.execute by creating and returning a Future that can be used to cancel execution and/or wait for completion.

用到返回值的例子,比如说我有很多个做validation的task,我希望所有的task执行完,然后每个task告诉我它的执行结果,是成功还是失败,如果是失败,原因是什么。然后我就可以把所有失败的原因综合起来发给调用者。

个人觉得cancel execution这个用处不大,很少有需要去取消执行的。

而最大的用处应该是第二点。

3、submit方便Exception处理

There is a difference when looking at exception handling. If your tasks throws an exception and if it was submitted with execute this exception will go to the uncaught exception handler (when you don't have provided one explicitly, the default one will just print the stack trace to System.err). If you submitted the task with submit any thrown exception, checked or not, is then part of the task's return status. For a task that was submitted with submit and that terminates with an exception, the Future.get will rethrow this exception, wrapped in an ExecutionException.


意思就是如果你在你的task里会抛出checked或者unchecked exception,而你又希望外面的调用者能够感知这些exception并做出及时的处理,那么就需要用到submit,通过捕获Future.get抛出的异常。

比如说,我有很多更新各种数据的task,我希望如果其中一个task失败,其它的task就不需要执行了。那我就需要catch Future.get抛出的异常,然后终止其它task的执行,代码如下:

51cto上有一篇非常好的文章“Java5并发学习”(http://lavasoft.blog.51cto.com/62575/115112),下面的代码是基于它之上修改的。

[java]view plaincopyprint?
import java.util.ArrayList; 

import java.util.List; 

import java.util.Random; 

import java.util.concurrent.Callable; 

import java.util.concurrent.ExecutionException; 

import java.util.concurrent.ExecutorService; 

import java.util.concurrent.Executors; 

import java.util.concurrent.Future; 

 

public class ExecutorServiceTest { 

    public static void main(String[] args) { 

        ExecutorService executorService = Executors.newCachedThreadPool(); 

        List<Future<String>> resultList = new ArrayList<Future<String>>(); 

        // 创建10个任务并执行 

        for (int i = 0; i < 10; i++) { 
            // 使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中 
            Future<String> future = executorService.submit(new TaskWithResult(i)); 
            // 将任务执行结果存储到List中 

            resultList.add(future); 
        } 
        executorService.shutdown(); 

        // 遍历任务的结果 

        for (Future<String> fs : resultList) { 
            try { 
                System.out.println(fs.get()); // 打印各个线程(任务)执行的结果 

            } catch (InterruptedException e) { 

                e.printStackTrace(); 

            } catch (ExecutionException e) { 

                executorService.shutdownNow(); 

                e.printStackTrace(); 

                return; 
            } 
        } 
    } 


 
class TaskWithResult implements Callable<String> { 

    private int id; 
    public TaskWithResult(int id) { 

        this.id = id; 
    } 

    /**

     * 任务的具体过程,一旦任务传给ExecutorService的submit方法,则该方法自动在一个线程上执行。

     *
     * @return

     * @throws Exception

     */ 

    public String call() throws Exception { 

        System.out.println("call()方法被自动调用,干活!!!             " + Thread.currentThread().getName()); 

        if (new Random().nextBoolean()) 

            throw new TaskException("Meet error in task." + Thread.currentThread().getName()); 

        // 一个模拟耗时的操作 

        for (int i = 999999999; i > 0; i--) 

            ; 

        return "call()方法被自动调用,任务的结果是:" + id + "    " + Thread.currentThread().getName(); 

    } 


class TaskException extends Exception { 

    public TaskException(String message) { 

        super(message); 
    } 




执行的结果类似于:
[java]view plaincopyprint?
call()方法被自动调用,干活!!!             pool-1-thread-1 

call()方法被自动调用,干活!!!             pool-1-thread-2 

call()方法被自动调用,干活!!!             pool-1-thread-3 

call()方法被自动调用,干活!!!             pool-1-thread-5 

call()方法被自动调用,干活!!!             pool-1-thread-7 

call()方法被自动调用,干活!!!             pool-1-thread-4 

call()方法被自动调用,干活!!!             pool-1-thread-6 

call()方法被自动调用,干活!!!             pool-1-thread-7 

call()方法被自动调用,干活!!!             pool-1-thread-5 

call()方法被自动调用,干活!!!             pool-1-thread-8 

call()方法被自动调用,任务的结果是:0    pool-1-thread-1 

call()方法被自动调用,任务的结果是:1    pool-1-thread-2 

java.util.concurrent.ExecutionException: com.cicc.pts.TaskException: Meet error in task.pool-1-thread-3 

    at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222) 

    at java.util.concurrent.FutureTask.get(FutureTask.java:83) 

    at com.cicc.pts.ExecutorServiceTest.main(ExecutorServiceTest.java:29) 

Caused by: com.cicc.pts.TaskException: Meet error in task.pool-1-thread-3 

    at com.cicc.pts.TaskWithResult.call(ExecutorServiceTest.java:57) 

    at com.cicc.pts.TaskWithResult.call(ExecutorServiceTest.java:1) 

    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) 

    at java.util.concurrent.FutureTask.run(FutureTask.java:138) 

    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) 

    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) 

    at java.lang.Thread.run(Thread.java:619) 

可以看见一旦某个task出错,其它的task就停止执行。



4. Executors


5. ProcessBuilder


6. ReentrantLock

http://blog.csdn.net/fw0124/article/details/6672522

http://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html

对 synchronized 的改进

如此看来同步相当好了,是么?那么为什么 JSR 166 小组花了这么多时间来开发 java.util.concurrent.lock 框架呢?答案很简单-同步是不错,但它并不完美。它有一些功能性的限制 —— 它无法中断一个正在等候获得锁的线程,也无法通过投票得到锁,如果不想等下去,也就没法得到锁。同步还要求锁的释放只能在与获得锁所在的堆栈帧相同的堆栈帧中进行,多数情况下,这没问题(而且与异常处理交互得很好),但是,确实存在一些非块结构的锁定更合适的情况。

除此之外,与目前的 synchronized 实现相比,争用下的 ReentrantLock 实现更具可伸缩性。(在未来的 JVM 版本中,synchronized 的争用性能很有可能会获得提高。)这意味着当许多线程都在争用同一个锁时,使用 ReentrantLock 的总体开支通常要比 synchronized 少得多。

reentrant 锁意味着什么呢?简单来说,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放。这模仿了 synchronized 的语义;如果线程进入由线程已经拥有的监控器保护的 synchronized 块,就允许线程继续进行,当线程退出第二个(或者后续) synchronized 块的时候,不释放锁,只有线程退出它进入的监控器保护的第一个 synchronized 块时,才释放锁。

   1. Lock lock = new ReentrantLock(); 
   2. lock.lock(); 
   3. try {  
   4.   // update object state 
   5. } 
   6. finally { 
   7.   lock.unlock();  
   8. } 


7. ConcurrentHashMap

http://www.ibm.com/developerworks/cn/java/java-lo-concurrenthashmap/

/**
     * 散列映射表的默认初始容量为 16,即初始默认为 16 个桶
     * 在构造函数中没有指定这个参数时,使用本参数
     */
    static final int DEFAULT_INITIAL_CAPACITY= 16;

    /**
     * 散列映射表的默认装载因子为 0.75,该值是 table 中包含的 HashEntry 元素的个数与
* table 数组长度的比值
     * 当 table 中包含的 HashEntry 元素的个数超过了 table 数组的长度与装载因子的乘积时,
* 将触发 再散列
     * 在构造函数中没有指定这个参数时,使用本参数
     */
    static final float DEFAULT_LOAD_FACTOR= 0.75f;

    /**
     * 散列表的默认并发级别为 16。该值表示当前更新线程的估计数
     * 在构造函数中没有指定这个参数时,使用本参数
     */
    static final int DEFAULT_CONCURRENCY_LEVEL= 16;


6. Segment  put
V put(K key, int hash, V value, boolean onlyIfAbsent) {
            lock();  //  Segment  ConcurrentHashMap
            try {
                int c = count;

                if (c++ > threshold)     //
                    rehash();              // table

                HashEntry<K,V>[] tab = table;
                //  table  1
                //  table
                int index = hash & (tab.length - 1);
                //
                HashEntry<K,V> first = tab[index];

                HashEntry<K,V> e = first;
                while (e != null && (e.hash != hash || !key.equals(e.key)))
                    e = e.next;

                V oldValue;
                if (e != null) {            //  /
                    oldValue = e.value;
                    if (!onlyIfAbsent)
                        e.value = value;    //  value
                }
                else {                        //  / 
                    oldValue = null;
                    ++modCount;         //  modCont  1 
                    // 
                    tab[index] = new HashEntry<K,V>(key, hash, first, value);
                    count = c;               //  count
                }
                return oldValue;
            } finally {
                unlock();                     //
            }
        }
ConcurrentHashMap 实现高并发的总结
基于通常情形而优化
在实际的应用中,散列表一般的应用场景是:除了少数插入操作和删除操作外,绝大多数都是读取操作,而且读操作在大多数时候都是成功的。正是基于这个前提,

ConcurrentHashMap 针对读操作做了大量的优化。通过 HashEntry 对象的不变性和用 volatile 型变量协调线程间的内存可见性,使得 大多数时候,读操作不需要加锁就可以正

确获得值。这个特性使得 ConcurrentHashMap 的并发性能在分离锁的基础上又有了近一步的提高。

总结
ConcurrentHashMap 是一个并发散列映射表的实现,它允许完全并发的读取,并且支持给定数量的并发更新。相比于 HashTable 和用同步包装器包装的 HashMap

(Collections.synchronizedMap(new HashMap())),ConcurrentHashMap 拥有更高的并发性。在 HashTable 和由同步包装器包装的 HashMap 中,使用一个全局的锁来同步不同

线程间的并发访问。同一时间点,只能有一个线程持有锁,也就是说在同一时间点,只能有一个线程能访问容器。这虽然保证多线程间的安全并发访问,但同时也导致对容器的访

问变成串行化的了。

在使用锁来协调多线程间并发访问的模式下,减小对锁的竞争可以有效提高并发性。有两种方式可以减小对锁的竞争:

1.减小请求 同一个锁的 频率。
2.减少持有锁的 时间。
ConcurrentHashMap 的高并发性主要来自于三个方面:

1.用分离锁实现多个线程间的更深层次的共享访问。
2.用 HashEntery 对象的不变性来降低执行读操作的线程在遍历链表期间对加锁的需求。
3.通过对同一个 Volatile 变量的写 / 读访问,协调不同线程间读 / 写操作的内存可见性。
使用分离锁,减小了请求 同一个锁的频率。

通过 HashEntery 对象的不变性及对同一个 Volatile 变量的读 / 写来协调内存可见性,使得 读操作大多数时候不需要加锁就能成功获取到需要的值。由于散列映射表在实际应

用中大多数操作都是成功的 读操作,所以 2 和 3 既可以减少请求同一个锁的频率,也可以有效减少持有锁的时间。

通过减小请求同一个锁的频率和尽量减少持有锁的时间 ,使得 ConcurrentHashMap 的并发性相对于 HashTable 和用同步包装器包装的 HashMap有了质的提高。
.


8. ConcurrentSkipListMap


9. LinkedHashMap


10. StringBuffer


11. ThreadGroup


12. volatile

volatile说明:
      volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存重新读取该成员的值,而且,当成员变量值发生变化时,强迫将变化的值重新写入共享内存,这样两个不同的线程在访问同一个共享变量的值时,始终看到的是同一个值。
  java 语言规范指出:为了获取最佳的运行速度,允许线程保留共享变量的副本,当这个线程进入或者离开同步代码块时,才与共享成员变量进行比对,如果有变化再更新共享成员变量。这样当多个线程同时访问一个共享变量时,可能会存在值不同步的现象。而volatile这个值的作用就是告诉VM:对于这个成员变量不能保存它的副本,要直接与共享成员变量交互。
     建议:当多个线程同时访问一个共享变量时,可以使用volatile,而当访问的变量已在synchronized代码块中时,不必使用。
     缺点:使用volatile将使得VM优化失去作用,导致效率较低,所以要在必要的时候使用。

M  volatile  count  N  volatile  count

happens-before A appens-before  BC happens-before D

Volatile B happens-before C

happens-before A appens-before  B B appens-before CC happens-before D M  N  volatile  N

N Java  M  volatile  count N  volatile  count

ConcurrentHashMap  Segment  count Segment  HashEntry  volatile



11. transient

http://www.blogjava.net/fhtdy2004/archive/2009/06/20/286112.html
http://zengzhaoshuai.iteye.com/blog/1132288

Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想  
用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。  
transient是Java语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。

1.package com.zzs.tet;  
2. 
3.import java.io.File;  
4.import java.io.FileInputStream;  
5.import java.io.FileNotFoundException;  
6.import java.io.FileOutputStream;  
7.import java.io.IOException;  
8.import java.io.ObjectInput;  
9.import java.io.ObjectInputStream;  
10.import java.io.ObjectOutput;  
11.import java.io.ObjectOutputStream;  
12.import java.io.Serializable;  
13. 
14.public class TransientDemo implements Serializable{  
15.    /** 
16.     *  
17.     */ 
18.    private static final long serialVersionUID = 1L;  
19.    private  transient String name;  
20.    private String password;  
21.      
22.    public String getName() {  
23.        return name;  
24.    }  
25. 
26.    public void setName(String name) {  
27.        this.name = name;  
28.    }  
29. 
30.    public String getPassword() {  
31.        return password;  
32.    }  
33. 
34.    public void setPassword(String password) {  
35.        this.password = password;  
36.    }  
37. 
38.    /** 
39.     * @param args 
40.     * @throws IOException  
41.     * @throws FileNotFoundException  
42.     * @throws ClassNotFoundException  
43.     */ 
44.    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {  
45.        // TODO Auto-generated method stub  
46.        String path="D:"+File.separator+"object.txt";  
47.        File file=new File(path);  
48.        TransientDemo transientDemo=new TransientDemo();  
49.        transientDemo.setName("姓名");  
50.        transientDemo.setPassword("密码");  
51.        ObjectOutput output=new ObjectOutputStream(new FileOutputStream(file));  
52.        output.writeObject(transientDemo);  
53.        ObjectInput input=new ObjectInputStream(new FileInputStream(file));  
54.        TransientDemo demo=(    TransientDemo )input.readObject();  
55.        System.out.println(demo.getName()+demo.getPassword());  
56.    }  
57. 
58.} 

输出结果:

Java代码 
1.null密码 


12. synchronized
http://feuyeux.iteye.com/blog/349528

一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

public class ThreadTest implements Runnable {

public void run() {
synchronized (this) {
for (int i=0 ;i<5; i++) {
System.out.println(Thread.currentThread().getName()+" : loop-"+i);
}
}
}

public static void main (String[] args) {

ThreadTest threadTest = new ThreadTest();
Thread threadA = new Thread(threadTest, "A1");
Thread threadB = new Thread(threadTest, "A2");
threadA.start();
threadB.start();

}
}

A1 : loop-0
A1 : loop-1
A1 : loop-2
A1 : loop-3
A1 : loop-4
A2 : loop-0
A2 : loop-1
A2 : loop-2
A2 : loop-3
A2 : loop-4

二、当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
public class ThreadTest2 {

public void testFucntion1() {
    synchronized (this) {
int i=5;
while(i-->0){
System.out.println(Thread.currentThread().getName()+" : "+i);
}
try{
Thread.sleep(500);
}catch (InterruptedException e) {
// TODO: handle exception
}
}
}

public void testFunction2() {
int i=5;
while(i-->0){
System.out.println(Thread.currentThread().getName()+" : "+i);
}
try{
Thread.sleep(500);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main (String[] args) {
final ThreadTest2 threadTest2 = new ThreadTest2();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
threadTest2.testFucntion1();
}
}, "A1");
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
threadTest2.testFunction2();
}
}, "A2");
thread1.start();
thread2.start();
}
}
A1 : 4
A2 : 4
A1 : 3
A2 : 3
A1 : 2
A2 : 2
A1 : 1
A2 : 1
A1 : 0
A2 : 0

三、当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
public class ThreadTest2 {

public void testFucntion1() {
    synchronized (this) {
int i=5;
while(i-->0){
System.out.println(Thread.currentThread().getName()+" : "+i);
}
try{
Thread.sleep(500);
}catch (InterruptedException e) {
// TODO: handle exception
}
}
}

public void testFunction2() {
int i=5;
while(i-->0){
System.out.println(Thread.currentThread().getName()+" : "+i);
}
try{
Thread.sleep(500);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main (String[] args) {
final ThreadTest2 threadTest2 = new ThreadTest2();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
threadTest2.testFucntion1();
}
}, "A1");
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
threadTest2.testFucntion1();
}
}, "A2");
threadTest2.testFucntion1();
thread2.start();
thread1.start();
}
}
main : 4
main : 3
main : 2
main : 1
main : 0
A2 : 4
A2 : 3
A2 : 2
A2 : 1
A2 : 0
A1 : 4
A1 : 3
A1 : 2
A1 : 1
A1 : 0

四、当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
public class ThreadTest2 {

public void testFucntion1() {
    synchronized (this) {
int i=5;
while(i-->0){
System.out.println(Thread.currentThread().getName()+" : "+i);
}
try{
Thread.sleep(500);
}catch (InterruptedException e) {
// TODO: handle exception
}
}
}

public void testFunction2() {
int i=5;
while(i-->0){
System.out.println(Thread.currentThread().getName()+" : "+i);
}
try{
Thread.sleep(500);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main (String[] args) {
final ThreadTest2 threadTest2 = new ThreadTest2();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
threadTest2.testFucntion1();
}
}, "A1");
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
threadTest2.testFucntion1();
}
}, "A2");
thread1.start();
thread2.start();
}
}
A1 : 4
A1 : 3
A1 : 2
A1 : 1
A1 : 0
A2 : 4
A2 : 3
A2 : 2
A2 : 1
A2 : 0

13. AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference

http://blog.csdn.net/zhengyuquan/article/details/8980277

java多线程用法-使用AtomicInteger
下面通过简单的两个例子的对比来看一下 AtomicInteger 的强大的功能
class Counter {
private volatile int count = 0;

public synchronized void increment() {
count++;  //若要线程安全执行执行count++,需要加锁
}

public int getCount() {
return count;
}
}

class Counter {
private AtomicInteger count = new AtomicInteger();

public void increment() {
count.incrementAndGet();
}
       //使用AtomicInteger之后,不需要加锁,也可以实现线程安全。
public int getCount() {
return count.get();
}
}

从上面的例子中我们可以看出:使用AtomicInteger是非常的安全的
那么为什么不使用记数器自加呢,例如count++这样的,因为这种计数是线程不安全的,高并发访问时统计会有误,而AtomicInteger为什么能够达到多而不乱,处理高并发应付自如呢?
这是由硬件提供原子操作指令实现的。在非激烈竞争的情况下,开销更小,速度更快。Java.util.concurrent中实现的原子操作类包括:
AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference。

//获取当前的值

public final int get()

//取当前的值,并设置新的值

public final int getAndSet(int newValue)

//获取当前的值,并自增

public final int getAndIncrement()

//获取当前的值,并自减

public final int getAndDecrement()

//获取当前的值,并加上预期的值

public final int getAndAdd(int delta)


14. ThreadFactory

在我们一般的使用中,创建一个线程,通常有两种方式:

    * 继承Thread类,覆盖run方法,实现我们需要的业务
    * 继承Runnable接口,实现run方法,实现我们需要的业务,并且调用new Thread(Runnable)方法,将其包装为一个线程执行

设想这样一种场景,我们需要一个线程池,并且对于线程池中的线程对象,赋予统一的线程优先级、统一的名称、甚至进行统一的业务处理或和业务方面的初始化工作,这时工厂方法就是最好用的方法了

http://blog.csdn.net/androidzhaoxiaogang/article/details/8991039

http://blog.163.com/among_1985/blog/static/2750052320126484314989/

class NamedThreadFactory implements ThreadFactory
    {
        private final String name;
        private final AtomicInteger number;
       
        NamedThreadFactory(String name)
        {
            this.name = name;
            number = new AtomicInteger(1);
        }

        @Override
        public Thread newThread(Runnable r)
        {
            return new Thread(r, name + "-" + number.getAndIncrement());
        }
    }


15. Future

http://blog.csdn.net/yangyan19870319/article/details/6093481

http://developer.51cto.com/art/201111/302612.htm

在Java中,如果需要设定代码执行的最长时间,即超时,可以用Java线程池ExecutorService类配合Future接口来实现。 Future接口是Java标准API的一部分,在java.util.concurrent包中。Future接口是Java线程Future模式的实现,可以来进行异步计算。

Future模式可以这样来描述:我有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时间之后,我就便可以从Future那儿取出结果。就相当于下了一张订货单,一段时间后可以拿着提订单来提货,这期间可以干别的任何事情。其中Future 接口就是订货单,真正处理订单的是Executor类,它根据Future接口的要求来生产产品。

Future接口提供方法来检测任务是否被执行完,等待任务执行完获得结果,也可以设置任务执行的超时时间。这个设置超时的方法就是实现Java程序执行超时的关键。

Future接口是一个泛型接口,严格的格式应该是Future<V>,其中V代表了Future执行的任务返回值的类型。 Future接口的方法介绍如下:

    * boolean cancel (boolean mayInterruptIfRunning) 取消任务的执行。参数指定是否立即中断任务执行,或者等等任务结束
    * boolean isCancelled () 任务是否已经取消,任务正常完成前将其取消,则返回 true
    * boolean isDone () 任务是否已经完成。需要注意的是如果任务正常终止、异常或取消,都将返回true
    * V get () throws InterruptedException, ExecutionException  等待任务执行结束,然后获得V类型的结果。InterruptedException 线程被中断异常, ExecutionException任务执行异常,如果任务被取消,还会抛出CancellationException
    * V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 同上面的get功能一样,多了设置超时时间。参数timeout指定超时时间,uint指定时间的单位,在枚举类TimeUnit中有相关的定义。如果计算超时,将抛出TimeoutException


16. FutureTask
http://zheng12tian.iteye.com/blog/991484
http://blog.csdn.net/fx_sky/article/details/11905545



17. Callable

http://auguslee.iteye.com/blog/1292335
http://hi.baidu.com/miss_java/item/98b8c413caa19313e3f986f7

/** *//**   
* Callable 和 Future接口   
* Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。   
* Callable和Runnable有几点不同:   
* (1)Callable规定的方法是call(),而Runnable规定的方法是run().   
* (2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。   
* (3)call()方法可抛出异常,而run()方法是不能抛出异常的。   
* (4)运行Callable任务可拿到一个Future对象,   
* Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。   
* 通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。   
*/  

package com.technicolor.impl;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

public class CallableFuture implements Callable{

private int flag = 0;

public CallableFuture(int flag){
this.flag = flag;
}

public String call() {

if (this.flag == 0 ) {

return "flag = 0";
} else if (this.flag == 1) {
try {
while (true) {
   System.out.println("looping ...");
  
Thread.sleep(2000);
}
   } catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
   }

return "false";
} else {
String message = "";
try {
throw new Exception("Bad flag value!");
} catch (Exception e) {
// TODO Auto-generated catch block
message = e.toString();
}
return "Exception ..."+message;
}
}

public static void main(String[] args){

CallableFuture task1 = new CallableFuture(0);
CallableFuture task2 = new CallableFuture(1);
CallableFuture task3 = new CallableFuture(2);

ExecutorService ex = Executors.newFixedThreadPool(3);
try {
Future future1 = ex.submit(task1);
System.out.println("task1: "+future1.get());

Future future2 = ex.submit(task2);
Thread.sleep(5000);
System.out.println("task2 cancel : "+future2.cancel(true));

Future future3 = ex.submit(task3);
System.out.println("task3: "+future3.get());

}catch (Exception e){
System.out.println(e.toString());
}
ex.shutdownNow();
}

}

18. CompletionService

http://uestzengting.iteye.com/blog/1132242
http://deepfuture.iteye.com/blog/599677
http://blog.csdn.net/hudashi/article/details/7012431

CompletionService相当于Executor加上BlockingQueue,使用场景为当子线程并发了一系列的任务以后,主线程需要实时地取回子线程任务的返回值并同时顺序地处理这些返回值,谁先返回就先处理谁。下面两个类的工作效果相同,一个使用了CompletionService,代码更简捷些,一个直接使用的Executort和BlockingQueue,更复杂一些。其实读CompletionService的源代码,可以发现它内部其实就是对Executor和BlockingQueue的一个封装,不过封装后确定很优雅,用起来很方便。


http://programming.iteye.com/blog/158568
分享到:
评论

相关推荐

    java 多线程并发实例

    在Java编程中,多线程并发是提升程序执行效率、充分利用多核处理器资源的重要手段。本文将基于"java 多线程并发实例"这个主题,深入探讨Java中的多线程并发概念及其应用。 首先,我们要了解Java中的线程。线程是...

    java多线程和并发.pdf

    Java多线程与并发编程是Java语言中用于处理多任务执行的关键技术,它能够帮助开发者设计出能够有效应对高并发请求的应用程序。在现代的线上(Online)和离线(Offline)应用中,合理利用多线程技术可以大幅提高系统...

    Java_多线程与并发编程总结.doc

    Java多线程与并发编程是Java开发中至关重要的一部分,它涉及到如何高效地利用CPU资源,以实现程序的并行执行。在操作系统层面,多任务和多进程是通过分配不同的内存空间来实现的,而线程则共享同一进程的内存,这...

    java 多线程高并发相关资料收集

    在Java编程领域,多线程和高并发是两个至关重要...总的来说,Java多线程和高并发编程是提升系统性能的关键技能。理解并熟练运用这些知识,能够帮助开发者编写出更加健壮、高效的代码,满足现代互联网应用的高并发需求。

    java多线程并发

    ### Java多线程并发知识点详解 #### 一、Java多线程并发简介 在现代软件开发中,特别是在Java这样的主流编程语言中,多线程并发技术是提高程序执行效率、优化资源利用的关键手段之一。本篇文章将深入探讨Java中的...

    深入Java多线程和并发编程

    ### 深入Java多线程与并发编程 在当今高度发展的信息技术领域中,随着硬件技术的进步和软件架构设计的复杂化,多线程与并发编程成为提高程序执行效率、增强系统性能的关键技术之一。本篇文章将围绕Java多线程与并发...

    JAVA多线程并发编程

    但同时,多线程并发也会引入一些问题,如数据竞争和同步问题。 为了解决这些问题,Java提供了多种同步机制。`synchronized`关键字用于控制对共享资源的访问,确保同一时间只有一个线程可以执行特定代码块,从而避免...

    java多线程并发实战和源码

    总结来说,Java多线程并发实战和源码的学习涵盖了线程创建与管理、同步机制、并发容器、内存模型以及并发工具类等多个方面。虽然书中实例不足,但通过结合其他资源,如jcip-examples-src.rar中的代码,可以进一步...

    java多线程处理数据库数据

    通过以上方法,我们可以在Java中有效地利用多线程处理数据库数据,提高程序的并发能力和效率。记得在设计时充分考虑线程间的协作与同步,以及数据库连接的管理和优化,以确保程序的稳定性和性能。

    黑马程序员_张孝祥_Java多线程与并发库 视频+代码+资料

    根据给定文件的信息,我们可以提炼出以下关于Java多线程与并发库的相关知识点: ### Java多线程基础 1. **线程的概念**:在Java中,线程是程序执行流的基本单元。一个标准的Java应用程序至少有一个线程,即主...

    Java多线程高并发篇(一)--重入锁

    在Java多线程高并发编程中,重入锁(ReentrantLock)是一个至关重要的概念,它提供了比Java内置锁(synchronized)更细粒度的控制,并且具有更高的可读性和可扩展性。本篇文章将深入探讨重入锁的相关知识点。 首先...

    Java 多线程与并发编程总结.doc

    Java多线程与并发编程是Java开发中不可或缺的一部分,它涉及到如何高效地利用CPU资源,实现并发执行多个任务。在操作系统层面,多线程是为了提高系统利用率,使得多个任务能够"同时"执行,但实际上,由于CPU的时钟...

    java 多线程操作数据库

    在当今高度并发的应用环境中,Java多线程技术被广泛应用于处理数据库操作,以提升系统的响应速度和处理能力。本文将基于一个具体的Java多线程操作数据库的应用程序,深入探讨其背后的原理、实现细节以及潜在的挑战。...

    java多线程的讲解和实战

    Java多线程是Java编程中的重要概念,尤其在如今的多核处理器环境下,理解并熟练掌握多线程技术对于提高程序性能和响应速度至关重要。本资料详细讲解了Java多线程的原理,并提供了丰富的实战代码,非常适合Java初学者...

    Java多线程与并发系列22道高频面试题(附思维导图和答案解析)

    Java多线程与并发系列知识点 Java多线程与并发系列知识点是Java开发人员面试中经常被问到的一个重要话题。理解多线程与并发编程的知识,可以帮助开发人员更好地编写高效、可靠的Java程序。本文总结了Java多线程与...

    Java多线程并发实战

    ### Java多线程并发实战知识点解析 #### 一、引言 在计算机科学领域,**多线程**和**并发...通过深入理解Java多线程并发的基础知识及其内存管理机制,开发者可以更好地设计和优化多线程应用,提高系统的性能和稳定性。

    java多线程分页查询

    ### Java多线程分页查询知识点详解 #### 一、背景与需求分析 在实际的软件开发过程中,尤其是在处理大量数据时,如何高效地进行数据查询成为了一个关键问题。例如,在一个用户众多的社交平台上,当用户需要查看...

    Java多线程和并发知识整理

    Java多线程和并发知识是Java开发中的重要组成部分,它涉及到如何高效地利用系统资源,尤其是在多核CPU环境下,合理地使用多线程可以显著提升应用程序的性能。 **1. 理论基础** 1.1 为什么需要多线程 多线程的引入...

Global site tag (gtag.js) - Google Analytics