线程的使用在开发中可以说是无处不在,场景特别多;当然也是很难控制的。当然你要玩的好,也是很好的。
简单的讲,线程本质上不能加快程序的运行(当然多cpu的机器例外了),只不过优化时间调度而已,在我们看来整体上快了点;但搞不好由于在线程间的切换消耗太多精力导致整个程序运行效率降低也很有可能,所以为何多线程的情况下就要不断尝试,找到最优线程数,也就是这个道理了。不过多线程运行有一个明显的好处啦(不管程序是变快了还是变慢了),那就是对于用户来说,减少对用户的等待时间,不然单线程跑任务,用户可能面对的就是一个时刻“卡死”的程序了(特别在界面上的处理)。
下面就用代码来描述线程的一些基本知识,这里描述的比较多,但基本能覆盖工作当中的应用了。
package com.wxshi.thread;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 线程的介绍,这里做个笔记 下面会从简单到高级介绍线程
*
* @author wxshi
*
*/
public class TraditionalThread {
// -----------演示线程的创建方法,及内部方法体的调用原理-----------
/**
* 第一种创建线程 :public class ChildThread extends Thread(){}
* 通过Thread子类覆盖Thread的run()方法,以实现线程运行自己的业务
*/
public void thread_init1() {
Thread thread = new Thread() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
thread.start();
}
/**
* 第二种方法创建线程:Thread thread = new Thread(Runnable runnable);
* 将线程运行的run()方法宿主到Runnable对象,以实现面向对象思想。
* Thread在构造的时候会将Runnable内的run()方法作为自己的目标,在启动时会运行
*
*/
public void thread_init2() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
}
/**
* 注意:ruannable()宿主里面的run方法会赋给Thread(父类),所以上面两种方法同时出现时
* 只会调用子类线程实现方式的run方法,因为覆盖了Thread(父类)的run方法,管你怎么传,都会被覆盖。
* */
// -----------演示线程定时任务,quartz的使用-----------
/**
* 线程之定时任务 具体定时需求更多的可以调用quartz时间api进行配置定时,这里不做多介绍
*
**/
public void thread_traditionTimerTask() {
Timer task = new Timer();
// 10s以后启动定时任务,只执行一次
task.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("task start");
}
}, 10000);
// 10s以后启动定时任务,以后每5s执行一次
task.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("task start");
}
}, 10000, 5000);
}
// -----------演示线程互斥,锁的使用-----------
OutPrint out = new OutPrint();
/**
* 线程间的互斥,下面的方法仅仅代表在被多线程调用时,会实现线程间的互斥 多个线程使用同一把锁会实现方法的同步,若使用的不是同一把锁,就不能实现同步
*
*/
public void thread_sync1() {
// 10个线程线程调用打印方法,保证每个线程都打印完整的字符串
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500);
out.print1("shiwenxue");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
/**
* 内部类。保证程序完整打印str字符串
*/
static class OutPrint {
String lock = ""; // 这里定义只当锁,没有其他作用。
/**
* 方法体 只锁住部分代码段,该保护的代码被保护,这部分代码只能一次被一个线程共享 此处用的锁是该对象
* 适用于一个方法部分代码共享,部分代码受保护
*
* @param str
*/
public void print1(String str) {
// 锁住方法主体,使得没车只供一个县线程调用
// this可以是一个公共变量,代表锁栓,不一定是当前对象,这里用当前对象当锁栓
// synchronized (lock) lock在方法体外定义的一个对象,也可以充当锁栓。
synchronized (this) {
for (char c : str.toCharArray()) {
System.out.print(c);
}
System.out.println();
}
}
/**
* 锁住整个方法,该保护的代码被保护,这部分代码只能一次被一个线程共享 此处用的锁是该对象
* 注意:由于此方法的锁也是该对象,与print1是同一个锁,因此这两个方法本身也互斥(锁在谁手上,谁就有权利调用) 如果,print1
* 用的锁是lock(定义的string),而print2用的是this,则不互斥
*
* @param str
*/
public synchronized void print2(String str) {
for (char c : str.toCharArray()) {
System.out.println(c);
}
}
/**
* 若添加静态方法,并且想与print1,print2两个方法同步,所有方法必须使用OutPrint.class对象作为锁
* 因为静态方法属于类本身,而类本身字节码(OutPrint.class)就是静态方法所属对象。
*
* @param str
*/
public static synchronized void print3(String str) {
for (char c : str.toCharArray()) {
System.out.println(c);
}
}
}
/**
* java5中提供了另外一种锁Lock,下面简单说明一下(这种锁更加面向对象)
*/
class output2 {
// 定义锁
Lock lock = new ReentrantLock();
/**
* 下面的方法会上锁
*
* @param str
*/
public void print1(String str) {
lock.lock(); // 给核心代码段上锁
try {
for (char c : str.toCharArray()) {
System.out.println(c);
}
} finally { // 若核心代码段执行异常了 ,当然要释放锁
lock.unlock();// 释放锁,
}
}
}
/**
* 以一个自定义的缓存器来演示读写锁:
* 读锁:允许多个线程同时读取,但不能写;
* 写锁:只允许当前线程写,不允许其他操作
*/
class MyCache {
// 缓存map
private Map myCache = new HashMap();
// 读写锁
private ReadWriteLock rwLock = new ReentrantReadWriteLock();
// 获取数据,这里允许多人同时获取,但在写时只能一个人操作
public Object getData(String key) {
rwLock.readLock().lock(); // 上读锁,允许多人读,但此时不允许写
Object value = null;
try {
value = myCache.get(key);
// 若缓存中没有数据,就从数据库或其他地方获取,并加入缓存
// 但此时释放读锁,加写锁,保证只有一个人能操作且只操作一次
if (null == value) {
rwLock.readLock().unlock();// 释放读锁
rwLock.writeLock().lock();// 加写锁
try {
if (null == value) { // 这里再判断一次是防止其他线程也走到这一步再次获取
value = ""; // 从其他地方获取
}
} finally {
rwLock.writeLock().unlock();// 获取了数据之后,释放写锁,再加读锁
}
}
} finally {
rwLock.readLock().unlock();// 释放读锁
}
return value;
}
}
// -----------演示线程间通信-----------
/**
* 线程间通信,这里以一个示例演示:子线程运行10次,主线程运行20次,交替运行,各自执行100次
*/
public void thread_communication() {
final Business business = new Business();
// 定义子线程
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
business.subBusiness(); // 子线程业务
}
}
}).start();
// 方法体本身作为主线程
for (int i = 0; i < 100; i++) {
business.mainBusiness(); // 主线程业务
}
}
/**
* 内部类,里面包含主线程与子线程业务实现, 这样容易管理任务,实现互斥与通信
*
* @author wxshi
*
*/
class Business {
// 唤醒标志,第一次执行为子线程,保证交替执行
private boolean isTimeForSub = true;
/**
* 子线程业务,运行10次 加锁保证每次要么子线程运行,要么主线程运行
*/
public synchronized void subBusiness() {
// 若为false,则子线程等待
while (!isTimeForSub) {
try {
this.wait();// 当前线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 若为true,则执行代码
for (int i = 0; i < 10; i++) {
System.out.println("sub loop times: " + i);
}
isTimeForSub = false; // 执行完10次,唤醒标志为false
this.notify(); // 唤醒其他线程(此处为主线程)
}
/**
* 主线程业务,运行20次
*/
public synchronized void mainBusiness() {
// 若为true,则主线程等待
while (isTimeForSub) {
try {
this.wait();// 当前线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 若为false,则执行代码
for (int i = 0; i < 20; i++) {
System.out.println("main loop times: " + i);
}
isTimeForSub = true; // 执行完20次,唤醒标志为true
this.notify(); // 唤醒其他线程
}
}
/**
* 内部类,里面包含主线程与子线程业务实现, 这样容易管理任务,实现互斥与通信
* 此类的演示完全与上面Business一样,只不过这里使用Condition来实现线程间的通信,取代传统的 wait() 和 notify();
* 注意:Condition与Lock成对使用
*
* @author wxshi
*
*/
class Business2 {
// 唤醒标志,第一次执行为子线程,保证交替执行
private boolean isTimeForSub = true;
// 定义锁
Lock lock = new ReentrantLock();
// 获取Condition
Condition condition = lock.newCondition();
/**
* 子线程业务,运行10次 加锁保证每次要么子线程运行,要么主线程运行
*/
public void subBusiness() {
// 若为false,则子线程等待
while (!isTimeForSub) {
try {
// this.wait();// 当前线程等待
condition.await(); // 此处用await();代表等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 若为true,则执行代码
for (int i = 0; i < 10; i++) {
System.out.println("loop times: " + i);
}
isTimeForSub = false; // 执行完10次,唤醒标志为false
// this.notify(); // 唤醒其他线程(此处为主线程)
condition.signal(); // 唤醒其他线程(此处为主线程)
}
/**
* 主线程业务,运行20次
*/
public synchronized void mainBusiness() {
// 若为true,则主线程等待
while (isTimeForSub) {
try {
// this.wait();// 当前线程等待
condition.await(); // 此处用await();代表等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 若为false,则执行代码
for (int i = 0; i < 20; i++) {
System.out.println("loop times: " + i);
}
isTimeForSub = true; // 执行完20次,唤醒标志为true
// this.notify(); // 唤醒其他线程
condition.signal(); // 唤醒其他线程(此处为主线程)
}
}
/**
* 这里演示利用Condition实现的自定义的队列 利用Condition创建不同的对象实现不同效果的通信,而不相互影响
* 下面的代码直接取javaAPI的代码,因为这里思想确实很好,实现也简单
* 这里演示的是自定义缓冲队列
*/
class MyQueue {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
// 缓冲队列,缓冲大小伟100
final Object[] queue = new Object[100];
int putIndex, getIndex, count;
// 存值
public void put(Object x) throws InterruptedException {
lock.lock();
try {
// 若缓冲区已满,则notFull锁等待
while (count == queue.length) {
notFull.await();
}
if (++putIndex == queue.length) {
putIndex = 0; // 若放到最后一个区间了,则再往第一个区间放
}
count++; // 代表放了一个数,唤醒取值线程去取
notEmpty.signal();
} finally {
lock.unlock();
}
}
// 取值
public Object get() throws InterruptedException {
lock.lock();
try {
// 若缓冲区没有数据满,则notEmpty锁等待
while (count == 0) {
notEmpty.await();
}
Object x = queue[getIndex]; // 取值
if (++getIndex == queue.length) {
getIndex = 0; // 若取到最后一个区间了,则再从第一个区间取
}
count--; // 代表取放了一个数,唤醒放值线程
notFull.signal(); // 唤醒放值线程,可以放值了
return x;
} finally {
lock.unlock();
}
}
}
// -----------演示线程间变量共享,而保持内部数据独立-----------
/**
* 定义一个全局ThreadLocal对象,注意:有多少个变量需要处理,就要定义多少个ThreadLocal
* 即一个ThreadLocal只能管理一个变量,当然设计是你自己的问题(比如很多变量封装成一个对象,再用一个ThreadLocal来存放对象)
*/
private static ThreadLocal threadData = new ThreadLocal();
/**
* 这里主要演示ThreadLocal类的使用, 当然我们实现线程变量共享又保持数据相对独立,可以使用全局map为每个线程保存自己的那一份数据
* 但这里直接使用ThreadLocal操作更加简便,并且线程终止时会自动释放内存,方便管理
*/
public void thread_dataLocal() {
// 定义两个线程,分别存放自己的随机值,然后分别调用取值函数去取
// 测试是否自己取出来的是自己存放的数据
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
int data = new Random().nextInt();
System.out.println(Thread.currentThread().getName() + " put data: " + data);
threadData.set(data);// 将数据交给ThreadLocal管理,内部跟map原理相似
new GetA().get();
new GetB().get();
}
}).start();
}
}
/**
* 定义获取线程的值
*/
static class GetA {
public void get() {
int data = threadData.get();// 会直接根据当前调用线程取出
System.out.println(Thread.currentThread().getName() + " get data of A:" + data);
}
}
/**
* 同上
*/
static class GetB {
public void get() {
int data = threadData.get();
System.out.println(Thread.currentThread().getName() + " get data of B:" + data);
}
}
/**
* 这里介绍封装多个变量数据的方法,这种设计很好,在对象内部封装ThreadLocal
*/
static class MyThreadScopeData {
// 构造方法私有化
private MyThreadScopeData() {
};
// 以线程为key放置数据对象
private static ThreadLocal myThreadData = new ThreadLocal();
// 单个线程内单例,好就好在这,每个线程独自单例
public static MyThreadScopeData getInstanceForThread() {
MyThreadScopeData scopeData = myThreadData.get();
if (null == scopeData) {
scopeData = new MyThreadScopeData();
myThreadData.set(scopeData);
}
return scopeData;
}
}
// -----------演示线程原子性-----------
/**
* 将普通基本变量封装成原执性,实现在线程间互斥
*/
public void thread_atomic() {
// 定义原子性int变量,并初始化为0
AtomicInteger atomicInt = new AtomicInteger(0);
atomicInt.addAndGet(2); // +2
atomicInt.addAndGet(-2); // -2
atomicInt.decrementAndGet(); // -1
}
// -----------演示线程池-----------
/**
* 线程池的使用 创建线程池的方法大概有下面几种,这里简单介绍下
*/
public void thread_pool() {
// 创建一个拥有5个线程的线程池(固定线程池)
ExecutorService threadPool = Executors.newFixedThreadPool(5);
// 创建一个缓冲线程池,这样会随着任务增多,线程池中线程会自动变更
ExecutorService threadPool2 = Executors.newCachedThreadPool();
// 特殊线程池,内部始终仅保持一个也仅有一个线程(死了会自动新创建一个)
ExecutorService threadPool3 = Executors.newSingleThreadExecutor();
// 定时任务线程池,会调用内部线程中的一个或几个去定时执行任务,调用schedule()方法会执行任务
ExecutorService threadPool4 = Executors.newScheduledThreadPool(6);
// 定义10个任务
for (int i = 0; i < 10; i++) {
final int task = i; // 供任务内部调用
// 执行任务,这里会随机去5个线程中的一个执行
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("第" + task + "个任务被" + Thread.currentThread().getName() + "执行");
}
});
}
threadPool.shutdown();// 执行完任务关闭线程池
threadPool.shutdownNow(); // 不管有没有执行完,立马关闭
}
// -----------演示线程回调任务执行结果-----------
/**
* Callable 与 Future 成对使用,一个返回回调结果,一个获取结果 更多操作查看API
*/
public void thread_callableAndFuture() {
ExecutorService threadPool = Executors.newSingleThreadExecutor();
// 通过线程submit的执行任务并返回回调结果,结果保存在Future中
Future future = threadPool.submit(new Callable() {
@Override
public String call() throws Exception {
return "call回调返回的值";
}
});
try {
String result = future.get(); // 获取回调结果
System.out.println("回调结果:" + result);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* CompletionService 用于提交一组callable任务,其take方法用于返回一个已完成的callable任务
*/
public void thread_completionService() throws InterruptedException, ExecutionException {
// 创建一个拥有5个线程的线程池(固定线程池,CompletionService会使用线程池中的线程去执行任务)
ExecutorService threadPool = Executors.newFixedThreadPool(5);
CompletionService completionService = new ExecutorCompletionService(threadPool);
for (int i = 0; i < 10; i++) {
final int task = i;
completionService.submit(new Callable() {
@Override
public String call() throws Exception {
return Thread.currentThread().getName() + "回调结果:" + task;
}
});
}
// 拿10遍结果
for (int i = 0; i < 10; i++) {
System.out.println(completionService.take().get());
completionService.take().get();// 捕获结果
}
}
// -----------演示线程信号灯的使用----------
/**
* 线程之间的信号灯的使用,可以实现一个事件在同一时刻被多少个线程访问。
*
* @param args
*/
public void thread_semaphore() {
// 线程池
ExecutorService service = Executors.newCachedThreadPool();
// 定义4个信号灯
final Semaphore semaphore = new Semaphore(4);
// final Semaphore semaphore = new Semaphore(4,true); //true:是否保证先来先得
// 定义10个任务,但每次只能有三个任务被同时执行(信号灯控制)
for (int i = 0; i < 20; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
semaphore.acquire(); // 点亮信号灯,代表一个坑被占
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() + "进入,当前已有" + (3 - semaphore.availablePermits()) + "个并发");
try {
Thread.sleep((long) (Math.random() * 10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() + "即将离开");
semaphore.release(); // 熄灭信号灯,代表一个坑被释放
System.out.println("线程" + Thread.currentThread().getName() + "已离开,当前已有" + (3 - semaphore.availablePermits()) + "个并发");
}
};
service.execute(runnable);
}
try {
Thread.sleep((long) (Math.random() * 100000));
service.shutdown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// -----------演示线程“同步”进行任务----------
/**
* CyclicBarrier同步工具 表示要求一定数量线程同时到达某一状态才继续一起往下执行,否则就等待其他行程 中间可以设置多个状态
* 如:完成各自的任务后集合吃饭,吃晚饭后又各自做自己的事
*/
public void thread_cyclicBarrier() {
ExecutorService service = Executors.newCachedThreadPool();
// 定义CyclicBarrier同步工具,表示要有5个线程同时到达某一状态才继续一起往下执行,否则就等待其他行程
final CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
for (int i = 0; i < 5; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
Thread.sleep((long) (Math.random() * 10000));
System.out.println("线程" + Thread.currentThread().getName() + "即将到达集合地点1,当前已有" + (cyclicBarrier.getNumberWaiting() + 1) + "个已经到达,"
+ (cyclicBarrier.getNumberWaiting() == 4 ? "都到齐了,可以走了" : "等候其他线程"));
// (第一个集合点)没到达就要等候
cyclicBarrier.await();
Thread.sleep((long) (Math.random() * 10000));
System.out.println("线程" + Thread.currentThread().getName() + "即将到达集合地点2,当前已有" + (cyclicBarrier.getNumberWaiting() + 1) + "个已经到达,"
+ (cyclicBarrier.getNumberWaiting() == 4 ? "都到齐了,可以走了" : "等候其他线程"));
// (第二个集合点)没到达就要等候
cyclicBarrier.await();
Thread.sleep((long) (Math.random() * 10000));
System.out.println("线程" + Thread.currentThread().getName() + "即将到达集合地点3,当前已有" + (cyclicBarrier.getNumberWaiting() + 1) + "个已经到达,"
+ (cyclicBarrier.getNumberWaiting() == 4 ? "都到齐了,可以走了" : "等候其他线程"));
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
service.shutdown();
// try {
// Thread.sleep((long)(Math.random()*10000));
// // service.shutdown();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
// -----------演示线程“倒计时”命令----------
/**
* CountDownLatch可以实现倒计时
* CountDownLatch.countDown()可以将时间减1,直到0时,所有线程可以进行下去,否则阻塞。 下面实现裁判员跟运动员的关系
*
* @param args
*/
public void thread_countDownLatch() {
ExecutorService service = Executors.newCachedThreadPool();
// 倒计时1:初始化为1,当裁判员发动命令时,便调用countDown()减到0,这时促使运动员执行
final CountDownLatch cdOrder = new CountDownLatch(1);
// 倒计时2:初始化为3,代表三个运动员,但每个运动员执行完,计数器便减1,当所有运动员执行完,便告知裁判员已完成
final CountDownLatch cdAnswer = new CountDownLatch(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() + "正准备接受命令");
cdOrder.await();// 等待裁判员命令,只要计数器不为0,就一直等待
System.out.println("线程" + Thread.currentThread().getName() + "已接受命令");
Thread.sleep((long) (Math.random() * 10000));
System.out.println("线程" + Thread.currentThread().getName() + "执行完");
cdAnswer.countDown(); // 每执行完一个,裁判员计数器便减1
} catch (Exception e) {
e.printStackTrace();
}
}
};
service.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();// 等待计数器为0,即等待所有运动员全部执行完
System.out.println("线程" + Thread.currentThread().getName() + "已收到所有响应结果");
} catch (Exception e) {
e.printStackTrace();
}
service.shutdown();
}
// -----------演示线程间的数据交换----------
/**
* Exchanger可以实现线程间的数据交换,只要有一方数据没到,另一方就一直等待,但同时到达时才进行数据交换
*
* @param args
*/
public void thread_exchanger() {
ExecutorService service = Executors.newCachedThreadPool();
// 交换器
final Exchanger exchanger = new Exchanger();
// 运行第一个线程
service.execute(new Runnable() {
public void run() {
try {
String data1 = "线程1的数据";
System.out.println("线程" + Thread.currentThread().getName() + "正在把数据:" + data1 + " 换出去");
Thread.sleep((long) (Math.random() * 10000));
// 线程1数据到了
String data2 = (String) exchanger.exchange(data1);
System.out.println("线程" + Thread.currentThread().getName() + "换回的数据为:" + data2);
} catch (Exception e) {
}
}
});
// 运行第二个线程
service.execute(new Runnable() {
public void run() {
try {
String data1 = "线程2的数据";
System.out.println("线程" + Thread.currentThread().getName() + "正在把数据:" + data1 + " 换出去");
Thread.sleep((long) (Math.random() * 10000));
// 线程2数据到了
String data2 = (String) exchanger.exchange(data1);
System.out.println("线程" + Thread.currentThread().getName() + "换回的数据为:" + data2);
} catch (Exception e) {
}
}
});
try {
Thread.sleep((long) (Math.random() * 100000));
} catch (Exception e) {
e.printStackTrace();
}
}
// -----------演示同步集合----------
/**
* 同步集合 无可厚非,同步集合当然表示集合的读写安全 这样不用自己去处理,很方便管理,但场合是否真正需要要自己把握
*/
public void thread_syncCollection() {
// 同步Map
Map syncMap = new ConcurrentHashMap();
// 方式2
Map map = new HashMap();
Map syncMap2 = new ConcurrentHashMap(map);
// 同步List
List syncList = new CopyOnWriteArrayList();
// 方式2
List list = new CopyOnWriteArrayList();
List syncList2 = new CopyOnWriteArrayList(list);
// 同步Set
Set syncSet = new CopyOnWriteArraySet();
// 方式2
Set set = new CopyOnWriteArraySet();
Set syncSet2 = new CopyOnWriteArraySet(set);
}
}