Java线程:新特征-原子量
 
所谓的原子量即操作变量的操作是“原子的”,该操作不可再分,因此是线程安全的。
 
为何要使用原子变量呢,原因是多个线程对单个变量操作也会引起一些问题。在Java5之前,可以通过volatile、synchronized关键字来解决并发访问的安全问题,但这样太麻烦。
Java5之后,专门提供了用来进行单变量多线程并发安全访问的工具包java.util.concurrent.atomic,其中的类也很简单。
 
下面给出一个反面例子(切勿模仿):
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.atomic.AtomicLong;

/** 
* Java线程:新特征-原子量 

* @author leizhimin 2009-11-6 9:53:11 
*/ 
public class Test { 
        public static void main(String[] args) { 
                ExecutorService pool = Executors.newFixedThreadPool(2); 
                Runnable t1 = new MyRunnable("张三", 2000); 
                Runnable t2 = new MyRunnable("李四", 3600); 
                Runnable t3 = new MyRunnable("王五", 2700); 
                Runnable t4 = new MyRunnable("老张", 600); 
                Runnable t5 = new MyRunnable("老牛", 1300); 
                Runnable t6 = new MyRunnable("胖子", 800); 
                //执行各个线程 
                pool.execute(t1); 
                pool.execute(t2); 
                pool.execute(t3); 
                pool.execute(t4); 
                pool.execute(t5); 
                pool.execute(t6); 
                //关闭线程池 
                pool.shutdown(); 
        } 
}

class MyRunnable implements Runnable { 
        private static AtomicLong aLong = new AtomicLong(10000);        //原子量,每个线程都可以自由操作 
        private String name;                //操作人 
        private int x;                            //操作数额

MyRunnable(String name, int x) { 
                this.name = name; 
                this.x = x; 
        }

public void run() { 
                System.out.println(name + "执行了" + x + ",当前余额:" + aLong.addAndGet(x)); 
        } 
}

 
运行结果:
李四执行了3600,当前余额:13600 
王五执行了2700,当前余额:16300 
老张执行了600,当前余额:16900 
老牛执行了1300,当前余额:18200 
胖子执行了800,当前余额:19000 
张三执行了2000,当前余额:21000

Process finished with exit code 0

 
张三执行了2000,当前余额:12000 
王五执行了2700,当前余额:18300 
老张执行了600,当前余额:18900 
老牛执行了1300,当前余额:20200 
胖子执行了800,当前余额:21000 
李四执行了3600,当前余额:15600

Process finished with exit code 0

 
张三执行了2000,当前余额:12000 
李四执行了3600,当前余额:15600 
老张执行了600,当前余额:18900 
老牛执行了1300,当前余额:20200 
胖子执行了800,当前余额:21000 
王五执行了2700,当前余额:18300

Process finished with exit code 0

 
从运行结果可以看出,虽然使用了原子量,但是程序并发访问还是有问题,那究竟问题出在哪里了?
 
这里要注意的一点是,原子量虽然可以保证单个变量在某一个操作过程的安全,但无法保证你整个代码块,或者整个程序的安全性。因此,通常还应该使用锁等同步机制来控制整个程序的安全性。
 
下面是对这个错误修正:
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.locks.Lock; 
import java.util.concurrent.locks.ReentrantLock; 
import java.util.concurrent.atomic.AtomicLong;

/** 
* Java线程:新特征-原子量 

* @author leizhimin 2009-11-6 9:53:11 
*/ 
public class Test { 
        public static void main(String[] args) { 
                ExecutorService pool = Executors.newFixedThreadPool(2); 
                Lock lock = new ReentrantLock(false); 
                Runnable t1 = new MyRunnable("张三", 2000,lock); 
                Runnable t2 = new MyRunnable("李四", 3600,lock); 
                Runnable t3 = new MyRunnable("王五", 2700,lock); 
                Runnable t4 = new MyRunnable("老张", 600,lock); 
                Runnable t5 = new MyRunnable("老牛", 1300,lock); 
                Runnable t6 = new MyRunnable("胖子", 800,lock); 
                //执行各个线程 
                pool.execute(t1); 
                pool.execute(t2); 
                pool.execute(t3); 
                pool.execute(t4); 
                pool.execute(t5); 
                pool.execute(t6); 
                //关闭线程池 
                pool.shutdown(); 
        } 
}

class MyRunnable implements Runnable { 
        private static AtomicLong aLong = new AtomicLong(10000);        //原子量,每个线程都可以自由操作 
        private String name;                //操作人 
        private int x;                            //操作数额 
        private Lock lock;

MyRunnable(String name, int x,Lock lock) { 
                this.name = name; 
                this.x = x; 
                this.lock = lock; 
        }

public void run() { 
                lock.lock(); 
                System.out.println(name + "执行了" + x + ",当前余额:" + aLong.addAndGet(x)); 
                lock.unlock(); 
        } 
}

 
执行结果:
张三执行了2000,当前余额:12000 
王五执行了2700,当前余额:14700 
老张执行了600,当前余额:15300 
老牛执行了1300,当前余额:16600 
胖子执行了800,当前余额:17400 
李四执行了3600,当前余额:21000

Process finished with exit code 0

 
这里使用了一个对象锁,来控制对并发代码的访问。不管运行多少次,执行次序如何,最终余额均为21000,这个结果是正确的。
 
有关原子量的用法很简单,关键是对原子量的认识,原子仅仅是保证变量操作的原子性,但整个程序还需要考虑线程安全的。
 

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/222541

Java线程:新特征-障碍器
 
Java5中,添加了障碍器类,为了适应一种新的设计需求,比如一个大型的任务,常常需要分配好多子任务去执行,只有当所有子任务都执行完成时候,才能执行主任务,这时候,就可以选择障碍器了。
 
障碍器是多线程并发控制的一种手段,用法很简单。下面给个例子:
 
import java.util.concurrent.BrokenBarrierException; 
import java.util.concurrent.CyclicBarrier;

/** 
* Java线程:新特征-障碍器 

* @author leizhimin 2009-11-6 10:50:10 
*/ 
public class Test { 
        public static void main(String[] args) { 
                //创建障碍器,并设置MainTask为所有定数量的线程都达到障碍点时候所要执行的任务(Runnable) 
                CyclicBarrier cb = new CyclicBarrier(7, new MainTask()); 
                new SubTask("A", cb).start(); 
                new SubTask("B", cb).start(); 
                new SubTask("C", cb).start(); 
                new SubTask("D", cb).start(); 
                new SubTask("E", cb).start(); 
                new SubTask("F", cb).start(); 
                new SubTask("G", cb).start(); 
        } 
}

/** 
* 主任务 
*/ 
class MainTask implements Runnable { 
        public void run() { 
                System.out.println(">>>>主任务执行了!<<<<"); 
        } 
}

/** 
* 子任务 
*/ 
class SubTask extends Thread { 
        private String name; 
        private CyclicBarrier cb;

SubTask(String name, CyclicBarrier cb) { 
                this.name = name; 
                this.cb = cb; 
        }

public void run() { 
                System.out.println("[子任务" + name + "]开始执行了!"); 
                for (int i = 0; i < 999999; i++) ;    //模拟耗时的任务 
                System.out.println("[子任务" + name + "]开始执行完成了,并通知障碍器已经完成!"); 
                try { 
                        //通知障碍器已经完成 
                        cb.await(); 
                } catch (InterruptedException e) { 
                        e.printStackTrace(); 
                } catch (BrokenBarrierException e) { 
                        e.printStackTrace(); 
                } 
        } 
}

 
运行结果:
[子任务E]开始执行了! 
[子任务E]开始执行完成了,并通知障碍器已经完成! 
[子任务F]开始执行了! 
[子任务G]开始执行了! 
[子任务F]开始执行完成了,并通知障碍器已经完成! 
[子任务G]开始执行完成了,并通知障碍器已经完成! 
[子任务C]开始执行了! 
[子任务B]开始执行了! 
[子任务C]开始执行完成了,并通知障碍器已经完成! 
[子任务D]开始执行了! 
[子任务A]开始执行了! 
[子任务D]开始执行完成了,并通知障碍器已经完成! 
[子任务B]开始执行完成了,并通知障碍器已经完成! 
[子任务A]开始执行完成了,并通知障碍器已经完成! 
>>>>主任务执行了!<<<<

Process finished with exit code 0

 
从执行结果可以看出,所有子任务完成的时候,主任务执行了,达到了控制的目标。

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/222738

(转)Java线程:新特征-原子量,障碍器的更多相关文章

  1. Java线程新特征——Java并发库

    一.线程池   Sun在Java5中,对Java线程的类库做了大量的扩展,其中线程池就是Java5的新特征之一,除了线程池之外,还有很多多线程相关的内容,为多线程的编程带来了极大便利.为了编写高效稳定 ...

  2. Java多线程-新特征-原子量

    所谓的原子量即操作变量的操作是“原子的”,该操作不可再分,因此是线程安全的. 为何要使用原子变量呢,原因是多个线程对单个变量操作也会引起一些问题.在Java5之前,可以通过volatile.synch ...

  3. Java多线程-新特征-阻塞队列ArrayBlockingQueue

    阻塞队列是Java5线程新特征中的内容,Java定义了阻塞队列的接口java.util.concurrent.BlockingQueue,阻塞队列的概念是,一个指定长度的队列,如果队列满了,添加新元素 ...

  4. Java多线程-新特征-阻塞栈LinkedBlockingDeque

    对于阻塞栈,与阻塞队列相似.不同点在于栈是“后入先出”的结构,每次操作的是栈顶,而队列是“先进先出”的结构,每次操作的是队列头. 这里要特别说明一点的是,阻塞栈是Java6的新特征.. Java为阻塞 ...

  5. Java线程新特性--- Lock

    在Java5中,专门提供了锁对象,利用锁可以方便的实现资源的封锁,用来控制对竞争资源并发访问的控制,这些内容主要集中在java.util.concurrent.locks包下面,里面有三个重要的接口C ...

  6. java 线程 新类库中的构件 countDownLatch 使用

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGlhbmdydWkxOTg4/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...

  7. Java多线程-新特征-锁(上)

    在Java5中,专门提供了锁对象,利用锁可以方便的实现资源的封锁,用来控制对竞争资源并发访问的控制,这些内容主要集中在java.util.concurrent.locks 包下面,里面有三个重要的接口 ...

  8. Java多线程-新特征-锁(下)

    在上文中提到了Lock接口以及对象,使用它,很优雅的控制了竞争资源的安全访问,但是这种锁不区分读写,称这种锁为普通锁.为了提高性能,Java提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控 ...

  9. Java多线程-新特征-锁

    Java中读写锁有个接口java.util.concurrent.locks.ReadWriteLock,也有具体的实现ReentrantReadWriteLock,详细的API可以查看JavaAPI ...

随机推荐

  1. MVC分层含义与开发方式

    真正的服务层是面向数据的,假想一切数据都是从参数获得 控制层是接受页面层数据,再传给服务层,然后将结果返回给页面层的(客户) 页面层是提交格式化的数据的(容易小混乱,无格式,所以要格式化,可以在中间加 ...

  2. ssh代理上网

    背景: 公司开发机没有外网,但可以通过ssh连接到另一台可以上公网的机器,所以想通过ssh代理的方式上网,简单又方便,而且需要的时候上,不需要的时候也可以不上 配置: 超级简单 在开发机上建立ssh隧 ...

  3. Eclipse汉化后如何还原为EN英文(实用技巧) --转

    自从那天脑袋短路后,下了个汉化包将Eclipse给汉化了,用的我真TMD的不习惯,一直想还原为EN文,试了好多办法,删文件,汉化包,改eclipse.ini文件中的"-nl zh" ...

  4. go 测试sort性能

    package main import "fmt" import "os" import "flag" import "bufio ...

  5. jQuery.validate 的form校验

    jQuery验证框架 : 基本html代码: <script src="js/jquery-1.9.1.js"></script> <script s ...

  6. 用 Docker Machine 创建 Azure 虚拟主机

    搭建环境向来是一个重复造轮子的过程,Docker Machine 则把用户搭建 Docker 环境的各种方案汇集在了一起.笔者在<Docker Machine 简介>一文中演示了使用 Do ...

  7. RabbitMQ windows安装官方文档翻译!

    RabbitMQ Windows安装和配置 下载地址 官网windows下载地址: http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.10/r ...

  8. 【ASP.NET MVC 牛刀小试】 URL Route

    例子引入 先看看如下例子,你能完全明白吗? using System; using System.Collections.Generic; using System.Linq; using Syste ...

  9. 实现自己的.NET Core配置Provider之EF

    <10分钟就能学会.NET Core配置>里详细介绍了.NET Core配置的用法,另外我还开源了自定义的配置Provider:EF配置Provider和Yaml配置Provider.本文 ...

  10. [leetcode-623-Add One Row to Tree]

    Given the root of a binary tree, then value v and depth d, you need to add a row of nodes with value ...