Java多线程与并发库高级应用-工具类介绍
java.util.concurrent.Lock
1、Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,
锁本身也应该是一个对象。两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象。
lock替代synchronized
class Outputer {
Lock lock = new ReentrantLock();
public void output(String name) {
int len = name.length();
lock.lock();
try{
for (int i = 0; i < len; i++) {
char c = name.charAt(i);
System.out.print(c);
}
}finally{
lock.unlock(); //这里防止内部代码出现异常,即无论如何最后都会释放锁
}
lock.unlock();
System.out.println();
}
}
售票系统
package com.java.juc; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class TestLock { public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(ticket, "窗口1售票").start();
new Thread(ticket, "窗口2售票").start();
new Thread(ticket, "窗口3售票").start();
} } class Ticket implements Runnable {
private int ticket = 100;
private Lock lock = new ReentrantLock(); @Override
public void run() {
while (true) {
lock.lock();
try {
if (ticket > 0) {
Thread.sleep(20);
System.out.println(Thread.currentThread().getName()
+ ",余票量:" + ticket--);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
2、读写锁:
读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。
* 如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;
* 如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。
* 总之,读的时候上读锁,写的时候上写锁!
/* 面试题:3个线程读,3个线程写 同一个数据
*/
public class ReadWriteLockTest {
public static void main(String[] args) {
final Queue3 queue = new Queue3(); for(int i = 0;i<3;i++){
new Thread(new Runnable() {
@Override
public void run() {
while(true){
queue.get();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
queue.set(new Random().nextInt(10000));
}
}).start();
}
} } class Queue3{
private Object data = null; //共享数据 ,只能有一个线程写该数据,但可以有多个线程同时读
ReadWriteLock rwl = new ReentrantReadWriteLock(); //读写锁 public void get(){
try {
rwl.readLock().lock(); //上读锁 可以有多个线程同时读
System.out.println(Thread.currentThread().getName() + " be ready to read data!");
Thread.sleep((long)Math.random() * 1000);
System.out.println(Thread.currentThread().getName() + " have read data : "+ data);
} catch (InterruptedException e) {
}finally{
rwl.readLock().unlock(); //释放读锁
}
}
public void set(Object data){
try {
rwl.writeLock().lock(); //添加写锁,保证只能有一个线程进行写操作
System.out.println(Thread.currentThread().getName() + " be read to write data: "+ data);
Thread.sleep((long)Math.random() * 1000);
this.data = data;
System.out.println(Thread.currentThread().getName() + "has write data");
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
rwl.writeLock().unlock(); //释放写锁
}
}
}
简单的读写锁示例
package com.java.juc; import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; public class TestReadWriteLock {
public static void main(String[] args) {
final ReadWriteLockDemo demo = new ReadWriteLockDemo();
new Thread(new Runnable(){
@Override
public void run() {
demo.set(new Random().nextInt(5000));
}
},"Write").start(); for(int i = 0;i<100;i++){
new Thread(new Runnable() {
@Override
public void run() {
demo.get();
}
}, "Read").start();
}
}
} class ReadWriteLockDemo{
private int number = 0; private ReadWriteLock lock = new ReentrantReadWriteLock(); public void get(){
try{
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() +" "+number);
}finally{
lock.readLock().unlock();
}
}
public void set(int number){
try{
lock.writeLock().lock();
this.number = number;
}finally{
lock.writeLock().unlock();
}
}
}
Hibernate的一个面试题:
User user = session.load(id,User.class);
User user = session.get(id,User.class);
以上两个的却别。
get()方式,直接查询数据库,如果查询到赋值给User对象,如果没有查询到则返回为null
load()方式,实际上是从User的一个代理中获取, User$Proxy中包含有一个真实的User对象,当调用load()时,如果成员变量User为null,则从数据库查询将记录返回并给User赋值,当load()时User不为null,则直接返回User对象
/**
* 面试题: 设计一个缓存系统
* @author Administrator
*
*/
public class CacheDemo { Map<String, Object> cache = new HashMap<String, Object>();
public static void main(String[] args) { } private ReadWriteLock rwl = new ReentrantReadWriteLock();
public Object getData(String key){
rwl.readLock().lock();
Object value = null;
try {
value = cache.get(key);
if(value == null){
rwl.readLock().unlock();
rwl.writeLock().lock();
try {
if(value == null){ //防止后边线程加载数据,使用双端检测机制
value = "xxx"; //queryDB
cache.put(key, value);
}
}finally{
rwl.writeLock().unlock();
}
rwl.readLock().lock();
}
} catch (Exception e) {
}finally{
rwl.readLock().unlock();
}
return value;
} }
ReadWriteLock javaAPI中有缓存的代码:
class CachedData {
Object data;
volatile boolean cacheValid;
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// Must release read lock before acquiring write lock
rwl.readLock().unlock();
rwl.writeLock().lock();
// Recheck state because another thread might have acquired
// write lock and changed state before we did.
if (!cacheValid) {
data = ...
cacheValid = true;
}
// Downgrade by acquiring read lock before releasing write lock
rwl.readLock().lock();
rwl.writeLock().unlock(); // Unlock write, still hold read
} use(data);
rwl.readLock().unlock();
}
}
3、Condition 实现线程通信
传统的线程通信方式
/*
* 传统线程通信
* 主线程和子线程分别打印 100次 和 10次,循环50次
*/
public class TraditionalThreadCommunication2 { public static void main(String[] args) {
final Buiness buiness = new Buiness();
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 1;i<=50;i++){
buiness.sub(i);
}
}
}).start(); for(int i = 1;i<=50;i++){
buiness.main(i);
}
} static class Buiness{
private boolean isShouldSub = false; //主线程先打印
public synchronized void main(int j){ //进行同步,防止在打印时被其他线程干扰
while(isShouldSub){ //这里使用while 防止假唤醒
try {
this.wait(); //wait() 和 notify() 必须出现在synchronized同步中
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for(int i = 1;i<=100;i++){
System.out.println("main thread print "+ i + " loop of " + j);
}
isShouldSub = true;
this.notify();
}
public synchronized void sub(int j){
while(!isShouldSub){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for(int i = 1 ; i<=10;i++){
System.out.println("sub thread print "+ i + " loop of " + j);
}
isShouldSub = false;
this.notify();
}
} }
将上述程序改写为使用Condition
/*
* 传统线程通信
* 主线程和子线程分别打印 100次 和 10次,循环50次
* 改写成使用 Condition 的方式
*/
public class TraditionalThreadCommunication2 { public static void main(String[] args) {
final Buiness buiness = new Buiness();
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 1;i<=50;i++){
buiness.sub(i);
}
}
}).start(); for(int i = 1;i<=50;i++){
buiness.main(i);
}
} /**
* 将程序改写为使用Lock&Condition的方式进行 同步和通信
* @author Administrator
*
*/
static class Buiness{
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
private boolean isShouldSub = false; //主线程先打印
public void main(int j){ //进行同步,防止在打印时被其他线程干扰
lock.lock();
try {
while(isShouldSub){ //这里使用while 防止假唤醒
try {
condition.await();
// this.wait(); //wait() 和 notify() 必须出现在同步监视器内部中
} catch (Exception e) {
e.printStackTrace();
}
}
for(int i = 1;i<=100;i++){
System.out.println("main thread print "+ i + " loop of " + j);
}
isShouldSub = true;
condition.signal();
// this.notify();
} finally {
lock.unlock();
}
}
public void sub(int j){
lock.lock();
try {
while(!isShouldSub){
try {
condition.await();
// this.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
for(int i = 1 ; i<=10;i++){
System.out.println("sub thread print "+ i + " loop of " + j);
}
isShouldSub = false;
condition.signal();
// this.notify();
} finally{
lock.unlock();
}
}
} }
main thread print 1 loop of 1
main thread print 2 loop of 1
main thread print 3 loop of 1
main thread print 4 loop of 1
main thread print 5 loop of 1
main thread print 6 loop of 1
main thread print 7 loop of 1
...
main thread print 99 loop of 1
main thread print 100 loop of 1
sub thread print 1 loop of 1
sub thread print 2 loop of 1
sub thread print 3 loop of 1
sub thread print 4 loop of 1
sub thread print 5 loop of 1
sub thread print 6 loop of 1
sub thread print 7 loop of 1
sub thread print 8 loop of 1
sub thread print 9 loop of 1
sub thread print 10 loop of 1
main thread print 1 loop of 2
main thread print 2 loop of 2
main thread print 3 loop of 2
main thread print 4 loop of 2
main thread print 5 loop of 2
main thread print 6 loop of 2
main thread print 7 loop of 2
main thread print 8 loop of 2
main thread print 9 loop of 2
...
main thread print 99 loop of 2
main thread print 100 loop of 2
sub thread print 1 loop of 2
sub thread print 2 loop of 2
sub thread print 3 loop of 2
sub thread print 4 loop of 2
sub thread print 5 loop of 2
sub thread print 6 loop of 2
sub thread print 7 loop of 2
sub thread print 8 loop of 2
sub thread print 9 loop of 2
sub thread print 10 loop of 2
main thread print 1 loop of 3
main thread print 2 loop of 3
main thread print 3 loop of 3
...
使用Condition比传统的好处
可以实现多路Condition ,在javaAPI中有
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100];
int putptr, takeptr, count; public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
} public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
使用多路Condition,可以扩展上述的一个例子,老大打印完 -> 老二 老二-> 老三 老三-> 老大 老大-> 老二...
/**
* 第一个线程循环100次,第二个线程循环10次,第三个线程循环20次,如此循环50次,请写出程序 这里使用Condition
*
* @author Administrator
*
*/
public class ThreeConditionCommunication { public static void main(String[] args) {
final Business2 business = new Business2();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
business.sub2(i);
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
business.sub3(i);
}
}
}).start(); for (int i = 1; i <= 50; i++) {
business.main(i);
}
} } class Business2 {
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
private int shoudeSub = 1; public void sub2(int i) {
lock.lock();
try {
while (shoudeSub != 2) { // 这里也可以用 if ,用while比较好一些 As in the one argument
// version, interrupts and spurious wakeups are
// possible, and this method should always be
// used in a loop
try { // 防止线程有可能被假唤醒 (while放在这里提现了水准)
condition2.await(); //等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 100; j++) {
System.out.println("sub2 thread sequence of " + j + ", loop of " + i);
}
shoudeSub = 3;
condition3.signal();//唤醒
} finally{
lock.unlock();
}
}
public void sub3(int i) {
lock.lock();
try {
while (shoudeSub != 3) { // 这里也可以用 if ,用while比较好一些 As in the one argument
// version, interrupts and spurious wakeups are
// possible, and this method should always be
// used in a loop
try { // 防止线程有可能被假唤醒 (while放在这里提现了水准)
condition3.await(); //等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 20; j++) {
System.out.println("sub3 thread sequence of " + j + ", loop of " + i);
}
shoudeSub = 1;
condition1.signal();//唤醒
} finally{
lock.unlock();
}
} public void main(int i) {
lock.lock();
try {
while (shoudeSub != 1) {
try {
condition1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 10; j++) {
System.out.println("main thread sequence of " + j + ", loop of "
+ i);
}
shoudeSub = 2;
condition2.signal();
} finally{
lock.unlock();
}
}
/**
*
* synchronized (obj) { 这里的obj与obj.wait必须相同,否则会抛异常 while (<condition does
* not hold>) obj.wait(); ... // Perform action appropriate to condition }
*/
}
Condition的一个例子:
编写一个程序,开启3个线程 ,这三个线程的ID分别为 A,B, C,每个线程将自己的ID 在屏幕上打印10遍,要求输出的结果必须按顺序显示。
如:ABCABCABC.....依次递归
这里实现了一个比题目稍微难得例子,A 打印10次,B打印20次 ,C打印5次依次递归20次。
package com.java.juc; import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class TestAlternative { public static void main(String[] args) {
final Alternative alternative = new Alternative();
new Thread(new Runnable(){
@Override
public void run() {
for(int i = 1;i<=20;i++){
alternative.loopA(i);
}
}
},"A").start();
new Thread(new Runnable(){
@Override
public void run() {
for(int i = 1;i<=20;i++){
alternative.loopB(i);
}
}
},"B").start();
new Thread(new Runnable(){
@Override
public void run() {
for(int i = 1;i<=20;i++){
alternative.loopC(i);
System.out.println("-----------------");
}
}
},"C").start(); } } class Alternative{ private int number = 1;
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition(); void loopA(int outerLoop){
lock.lock();
try{
while(number != 1){
condition1.await();
}
for(int i = 1;i<=10;i++){
System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + outerLoop);
}
number = 2;
condition2.signal();
}catch(Exception e){
}finally {
lock.unlock();
}
} void loopB(int outerLoop){
lock.lock();
try{
while(number != 2){
condition2.await();
}
for(int i = 1;i<=20;i++){
System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + outerLoop);
}
number = 3;
condition3.signal();
}catch(Exception e){ }finally{
lock.unlock();
}
}
void loopC(int outerLoop){
lock.lock();
try{
while(number != 3){
condition3.await();
}
for(int i = 1;i<=5;i++){
System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + outerLoop);
}
number = 1;
condition1.signal();
}catch(Exception e){ }finally{
lock.unlock();
}
}
}
java5的Semaphere同步工具
Semaphore实现信号灯
Semaphore可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。
假设一个文件同时可以被3个人访问,来了5个人,同时只有3个访问。3个中任何一个出来后,等待的就可以进去了。
public class SemaphoreTest { public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final Semaphore sp = new Semaphore(3); //还有一个构造方法,Semaphore(int permits, boolean fair)
fair参数为true表示谁先来谁先进,一种公平的原则
for(int i=0;i<10;i++){
Runnable runnable = new Runnable(){
public void run(){
try {
sp.acquire();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() +
"进入,当前已有" + (3-sp.availablePermits()) + "个并发");
try {
Thread.sleep((long)(Math.random()*10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() +
"即将离开");
sp.release();
//下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元
System.out.println("线程" + Thread.currentThread().getName() +
"已离开,当前已有" + (3-sp.availablePermits()) + "个并发");
}
};
service.execute(runnable);
}
}
}
单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了 "锁",再由另外一个线程释放"锁",这可应用于死锁恢复的一些场合。
java5 的CyclicBarrier同步工具
/**
* 表示大家彼此等待,大家集合好后才开始出发,分散活动后又在指定地点集合碰面,
* 这就好比整个公司的人员利用周末时间集体郊游一样,先各自从家出发到公司集合后,
* 再同时出发到公园游玩,在指定地点集合后再同时开始就餐,…。
* @author Administrator
*
*/
public class CyclicBarrierTest { public static void main(String[] args) {
ExecutorService threadPool = Executors.newCachedThreadPool();
final CyclicBarrier cb = new CyclicBarrier(3);
for (int i = 0; i < 3; i++) {
Runnable runnable = new Runnable() {
public void run() {
try {
Thread.sleep((long) (Math.random() * 10000));
System.out.println("线程"+ Thread.currentThread().getName()
+ "即将到达集合点1,当前已有 "+(cb.getNumberWaiting()+1) +"个已经到达,"+(cb.getNumberWaiting() == 2?"都到齐了,继续走啊":"正在等待"));
cb.await(); Thread.sleep((long) (Math.random() * 10000));
System.out.println("线程"+ Thread.currentThread().getName()
+ "即将到达集合点2,当前已有 "+(cb.getNumberWaiting()+1) +"个已经到达,"+(cb.getNumberWaiting() == 2?"都到齐了,继续走啊":"正在等待"));
cb.await(); Thread.sleep((long) (Math.random() * 10000));
System.out.println("线程"+ Thread.currentThread().getName()
+ "即将到达集合点3,当前已有 "+(cb.getNumberWaiting()+1) +"个已经到达,"+(cb.getNumberWaiting() == 2?"都到齐了,继续走啊":"正在等待"));
cb.await();
} catch (Exception e) {
// TODO: handle exception
}
}
};
threadPool.execute(runnable);
}
threadPool.shutdown();
} }
java5的CountDownLatch同步工具
CountDownLatch : 闭锁,在完成某些运算时,只有其他所有线程的运算全部完成,当前运算才继续执行
CountDownLatch应用1:比如要统计5个线程并发的运行时间,即线程的开始时间与最后一个线程的运行结束时间的间隔时间。
package com.java.juc; import java.util.concurrent.CountDownLatch; public class TestCountDownLatch2 { public static void main(String[] args) { CountDownLatch latch = new CountDownLatch(5);
LatchDemo2 ld = new LatchDemo2(latch); long start = System.currentTimeMillis();
for(int i = 0;i<5;i++){
new Thread(ld).start();
}
try {
latch.await(); //先执行完成的线程需要等待还没有执行完的线程
} catch (InterruptedException e) {
e.printStackTrace();
} long end = System.currentTimeMillis();
System.out.println("cost: "+ (end - start));
} } class LatchDemo2 implements Runnable{
private CountDownLatch latch; public LatchDemo2(CountDownLatch latch) {
this.latch = latch;
} @Override
public void run() { try {
synchronized(this){
for(int i = 0;i<50000;i++){ //找出50000以内的所有偶数
if(i % 2 == 0){
System.out.println(i);
}
}
}
} finally{
latch.countDown(); //为了让这一句一定执行可以放在finally中
}
}
}
还可以应用于计算所有种类商品的平均销售总和,平均销售时间等,如果使用单线程计算效率非常低,相当于是串行计算。可以使用并行计算,按照商品种类进行区分并行的计算。可以将最终的每个线程的计算结果在进行汇总,可以得出最终的的总的销售数据,这就可以使用CountDownLatch进行操作,可以大幅度提高效率。(京东)
应用:运动员跑步比赛,得到最终的排名需要在所有运动员都完成之后,公布最终的结果。
/**
* 犹如倒计时计数器,调用CountDownLatch对象的countDown方法就将计数器减一,
* 当计数器到达0时,则所有等待者或单个等待者开始执行。
* 可以实现一个人(也可以是多个人)等待其他所有人都来通知他,可以实现一个人通知多个人的效果,
* 类似裁判一声口令,运动员同时开始奔跑,或者所有运动员都跑到
* 终点后裁判才可以公布结果。还可以实现一个计划需要多个领导都签字后
* 才能继续向下实施的情况
* @author Administrator
*
*/
public class CountDownLetchTest { public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
final CountDownLatch cdOrder = new CountDownLatch(1); //计数器初始值 1
final CountDownLatch cdAnswer = new CountDownLatch(3);
for(int i = 0;i<3;i++){
Runnable runnable = new Runnable() { @Override
public void run() {
try {
System.out.println("线程"+Thread.currentThread().getName()
+"正准备接受命令");
cdOrder.await();
System.out.println("线程"+Thread.currentThread().getName()
+"已接受命令");
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程"+Thread.currentThread().getName()
+"回应命令处理结果");
cdAnswer.countDown();
} catch (Exception e) {
// TODO: handle exception
}
}
};
executorService.execute(runnable);
}
try {
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程"+Thread.currentThread().getName()
+"即将发布命令");
cdOrder.countDown(); //计数器数值减 1
System.out.println("线程"+Thread.currentThread().getName()
+"已发送命令,正在等待结果");
cdAnswer.await();
System.out.println("线程"+Thread.currentThread().getName()
+"已收到所有响应结果");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
executorService.shutdown();
}
}
java 中CycliBarriar 和 CountDownLatch 有什么区别?
这两个的区别是CyclicBarrier 可以重复使用已经通过的障碍,而 CountdownLatch 不能重复使用。
java5的Exchanger同步工具
/**
* 用于实现两个人之间的数据交换,每个人在完成一定的事物后想与对方交换数据,第一个先拿出数据的人将
* 一直等待第二个人拿着数据到来时,才能彼此交换数据。
* @author Administrator
*
*/
public class ExchangerTest { public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
final Exchanger exchanger = new Exchanger();
executorService.execute(new Runnable() { @Override
public void run() {
try {
String data1 = "aaa";
System.out.println("线程" + Thread.currentThread().getName()
+ "正在把数据" + data1 + "换出去");
Thread.sleep((long) (Math.random() * 10000));
String data2 = (String) exchanger.exchange(data1);
System.out.println("线程" + Thread.currentThread().getName()
+ "换回的数据为 " + data2);
} catch (Exception e) {
// TODO: handle exception
}
}
});
executorService.execute(new Runnable() { @Override
public void run() {
try {
String data1 = "bbb";
System.out.println("线程" + Thread.currentThread().getName()
+ "正在把数据" + data1 + "换出去");
Thread.sleep((long) (Math.random() * 10000));
String data2 = (String) exchanger.exchange(data1);
System.out.println("线程" + Thread.currentThread().getName()
+ "换回的数据为 " + data2);
} catch (Exception e) {
// TODO: handle exception
}
}
});
} }
打印结果为:
线程 pool-1-thread-1正把数据 aaa 换出去
线程 pool-1-thread-2正把数据 bbb 换出去
线程 pool-1-thread-2换回的数据为 aaa
线程 pool-1-thread-1换回的数据为 bbb
Java多线程与并发库高级应用-工具类介绍的更多相关文章
- Java多线程与并发库高级应用-同步集合
ArrayBlockingQueue LinkedBlockingQueue 数组是连续的一片内存 链表是不连续的一片内存 传统方式下用Collections工具类提供的synchronizedCo ...
- Java多线程与并发库高级应用-java5线程并发库
java5 中的线程并发库 主要在java.util.concurrent包中 还有 java.util.concurrent.atomic子包和java.util.concurrent.lock子包 ...
- Java多线程与并发库高级应用-线程池
线程池 线程池的思想 线程池的概念与Executors类的应用 > 创建固定大小的线程池 > 创建缓存线程池 > 创建单一线程池(如何实现线程死掉后重新启动?) 关闭线程池 > ...
- Java多线程与并发库高级应用-传统线程机制回顾
1.传统线程机制的回顾 1.1创建线程的两种传统方式 在Thread子类覆盖的run方法中编写运行代码 // 1.使用子类,把代码放到子类的run()中运行 Thread thread = new T ...
- Java多线程与并发库高级应用-Callable与Future的应用
Callable这种任务可以返回结果,返回的结果可以由Future去拿 >Future取得的结果类型和Callable返回的结果类型必须一致,这是通过泛型来实现的. >Completion ...
- Java多线程与并发库高级应用-面试题
第一题:现有的程序代码模拟产生了16个日志对象,并且需要运行16秒才能打印完这些日志,请在程序中增加4个线程去调用parseLog()方法来分头打印这16个日志对象,程序只需要运行4秒即可打印完这些日 ...
- Java多线程与并发库高级应用-可阻塞的队列
ArrayBlockQueue 可阻塞的队列 > 队列包含固定长度的队列和不固定长度的队列. > ArrayBlockQueue > 看BlockingQueue类的帮助文档,其中有 ...
- Java多线程与并发库高级应用-传统线程同步通信技术
面试题: 子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着又 主线程循环100次,如此循环50次,请写出程序 /** * 子线程循环10次,接着主线程循环100次,接着又回到 ...
- Java多线程与并发库高级应用-传统线程互斥技术
线程安全问题: 多个线程操作同一份数据的时候,有可能会出现线程安全问题.可以用银行转账来解释. 模拟线程安全问题 /** * 启动两个线程分别打印两个名字,名字按照字符一个一个打印 * * @aut ...
随机推荐
- APIO2015泛做
可以在UOJ上提交也可以在bzoj上提交(权限) A. Bali Sculptures 对于前72%的数据,按位考虑,然后跑一点沙茶dp就行了. dp:用f[x][y]表示前x位分为y段是否满足条件. ...
- SAE上安装第三方模块
当sae上没有自己所需要的第三方模块时,可以使用saecloud install package [package...]将所需要的模块安装到本地应用文件夹下,然后在index.wsgi下添加如何代码 ...
- JSTL中的TLD配置和使用。
一,JSTL介绍: JSTL标签库,是日常开发经常使用的,也是众多标签中性能最好的.把常用的内容,放在这里备份一份,随用随查.尽量做到不用查,就可以随手就可以写出来.这算是Java程序员的基本功吧,一 ...
- [转]Python 命令行参数和getopt模块详解
FROM : http://www.tuicool.com/articles/jaqQvq 有时候我们需要写一些脚本处理一些任务,这时候往往需要提供一些命令行参数,根据不同参数进行不同的处理,在Pyt ...
- adb logcat 基本用法
入门android ,至少需要了解 adb 吧,那么打 log 也是必不可少的了. 下面简单介绍一下 adb logcat 基本用法: Usage: logcat [options] [filters ...
- IE8中给HTML标签负值报错问题
当通过JS给一个HTML标签设置高宽为负值的时候,会爆出一个“参数无效”的错误 chrome下不会报错,但是元素不会做任何关于负值的改变
- canvas 2d 贴图技术实践
最近在公司内部的技术协会论坛里闲逛的时候,无意中发现了一篇手淘前端大牛岑安两年前写的博文,讲述了canvas的2d贴图技术.看到后觉得相当神奇.于是就自己实现了一下.不过岑安前辈的那篇博文也只是大概讲 ...
- .Net分布式异常报警系统-简介
系统简介 分布式异常报警系统就是收集系统运行过程中产生的未处理异常,检查系统运行的状态,并将异常信息统一发送到服务端,由服务端将信息通知到相关的责任人. 问题 我们在项目开发中可能遇到以下几个问题: ...
- MyBatis学习--高级映射
简介 前面说过了简单的数据库查询和管理查询,在开发需求中有一些一对一.一对多和多对多的需求开发,如在开发购物车的时候,订单和用户是一对一,用户和订单是一对多,用户和商品是多对多.这些在Hibernat ...
- Android 之px于dp在Java代码中的转换
现在由于用到了,使用代码进行动态布局,所以需要进行px于dp之间的转换. 现将其封装为方法,以便于调用. public int DpToPx(Context context,float dp){ fl ...