------- android培训、java培训、期待与您交流! ----------
java5的线程锁技术
Lock&Condition实现线程同步通信Lock比传统的synchronized方式更加面向对象,两个线程执行的代码块要实现同步互斥,必须持有同一个Lock对象。
ReadWriteLock,多个读锁不互斥,读锁与写锁互斥。如果需要多线程同时读,但不能同时写,加读锁;如果代码修改数据,为改代码加写锁,写锁是线程独占的。
在等待 Condition 时,允许发生“虚假唤醒”,Condition 应该总是在一个循环中被等待,并测试正被等待的状态。
Condition condition = lock.newCondition();
使用读写锁的缓存功能
- class CachedData {
- Object data;
- volatile boolean cacheValid;
- ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
- void processCachedData() {
- rwl.readLock().lock();
- if (!cacheValid) {//如果缓存中没有data
- // 在使用写锁前必须释放读锁
- rwl.readLock().unlock();
- rwl.writeLock().lock();
- // 获取写锁后在检查
- if (!cacheValid) {
- data = ...//写入data
- cacheValid = true;
- }
- // 写入数据后,上读锁,在释放写锁
- rwl.readLock().lock();
- rwl.writeLock().unlock();
- }
- use(data);
- rwl.readLock().unlock();
- }
- }
class CachedData {
Object data;
volatile boolean cacheValid;
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {//如果缓存中没有data
// 在使用写锁前必须释放读锁
rwl.readLock().unlock();
rwl.writeLock().lock();
// 获取写锁后在检查
if (!cacheValid) {
data = ...//写入data
cacheValid = true;
}
// 写入数据后,上读锁,在释放写锁
rwl.readLock().lock();
rwl.writeLock().unlock();
}
use(data);
rwl.readLock().unlock();
}
}
Semaphore 计数信号灯
限制可以访问某些资源的线程数目。可进入同一段代码的线程数目。Semaphore(int permits) permits允许的并发线程数.
acquire() 从此信号量获取一个许可.
void release() 释放一个许可。
单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”。
- public class SemaphoreTest {
- public static void main(String[] args) {
- ExecutorService service = Executors.newCachedThreadPool();
- final Semaphore sp = new Semaphore(3);//创建一个可以允许3个线程并发访问的信号灯
- 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); //将任务交给线程池
- }
- }
- }
public class SemaphoreTest {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final Semaphore sp = new Semaphore(3);//创建一个可以允许3个线程并发访问的信号灯
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); //将任务交给线程池
}
}
}
CyclicBarrier
一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点。- public class CyclicBarrierTest {
- public static void main(String[] args) {
- ExecutorService service = Executors.newCachedThreadPool();//创建一个线程池
- final CyclicBarrier cb = new CyclicBarrier(3);//创建一个循环barrier,线程数目为3
- for(int i=0;i<3;i++){//新建3个线程,交给线程池执行
- 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) {
- e.printStackTrace();
- }
- }
- };
- service.execute(runnable);
- }
- service.shutdown();
- }
- }
public class CyclicBarrierTest {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();//创建一个线程池
final CyclicBarrier cb = new CyclicBarrier(3);//创建一个循环barrier,线程数目为3
for(int i=0;i<3;i++){//新建3个线程,交给线程池执行
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) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
service.shutdown();
}
}
CountDownLatch
CountDownLatch(int count) 构造一个用给定计数初始化的 CountDownLatch。调用countDown方法就将计数器减1,当计数到达0时,则所有等待者或单个等待者开始执行。
- public class CountdownLatchTest {
- public static void main(String[] args) {
- ExecutorService service = Executors.newCachedThreadPool();
- final CountDownLatch cdOrder = new CountDownLatch(1);
- final CountDownLatch cdAnswer = new CountDownLatch(3);
- for(int i=0;i<3;i++){
- Runnable runnable = new Runnable(){//创建3个线程
- public void run(){
- try {
- System.out.println("线程" + Thread.currentThread().getName() +
- "正准备接受命令");
- cdOrder.await();//等待主线程控制cdOrder开始任务
- System.out.println("线程" + Thread.currentThread().getName() +
- "已接受命令");
- Thread.sleep((long)(Math.random()*10000));
- System.out.println("线程" + Thread.currentThread().getName() +
- "回应命令处理结果");
- cdAnswer.countDown();//任务完成通知主线程
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- };
- service.execute(runnable);
- }
- try {
- Thread.sleep((long)(Math.random()*10000));
- System.out.println("线程" + Thread.currentThread().getName() +
- "即将发布命令");
- cdOrder.countDown();//将count归0,开始执行任务
- System.out.println("线程" + Thread.currentThread().getName() +
- "已发送命令,正在等待结果");
- cdAnswer.await();//等待子线程完成
- System.out.println("线程" + Thread.currentThread().getName() +
- "已收到所有响应结果");
- } catch (Exception e) {
- e.printStackTrace();
- }
- service.shutdown();
- }
- }
public class CountdownLatchTest {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final CountDownLatch cdOrder = new CountDownLatch(1);
final CountDownLatch cdAnswer = new CountDownLatch(3);
for(int i=0;i<3;i++){
Runnable runnable = new Runnable(){//创建3个线程
public void run(){
try {
System.out.println("线程" + Thread.currentThread().getName() +
"正准备接受命令");
cdOrder.await();//等待主线程控制cdOrder开始任务
System.out.println("线程" + Thread.currentThread().getName() +
"已接受命令");
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() +
"回应命令处理结果");
cdAnswer.countDown();//任务完成通知主线程
} catch (Exception e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
try {
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() +
"即将发布命令");
cdOrder.countDown();//将count归0,开始执行任务
System.out.println("线程" + Thread.currentThread().getName() +
"已发送命令,正在等待结果");
cdAnswer.await();//等待子线程完成
System.out.println("线程" + Thread.currentThread().getName() +
"已收到所有响应结果");
} catch (Exception e) {
e.printStackTrace();
}
service.shutdown();
}
}
Exchanger
实现持有同一Exchanger对象的两个线程间的数据交换。V exchange(V x) 方法交换数据。
可阻塞队列
public class ArrayBlockingQueue<E>extends AbstractQueue<E>implements BlockingQueue<E>, Serializable按 FIFO(先进先出)原则对元素进行排序。
生产者使用put方法放入数据,消费者使用take方法取出数据。
新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。
size() 返回此队列中元素的数量。
可以由两个只有一个元素的同步队列对象实现线程通信功能。
空中网题目:
Test类中的代码在不断地产生数据,然后交给TestDo.doSome()方法去处理,就好像生产者在不断地产生数据,消费者在不断消费数据。程序有10个线程来消费生成者产生的数据,这些消费者都调用TestDo.doSome()方法去进行处理,故每个消费者都需要一秒才能处理完,程序应保证这些消费者线程依次有序地消费数据,只有上一个消费者消费完后,下一个消费者才能消费数据,下一个消费者是谁都可以,但要保证这些消费者线程拿到的数据是有顺序的。
- public class Test {
- public static void main(String[] args) {
- final Semaphore semaphore = new Semaphore(1);
- final SynchronousQueue<String> queue = new SynchronousQueue<String>();
- for(int i=0;i<10;i++){
- new Thread(new Runnable(){
- @Override
- public void run() {
- try {
- semaphore.acquire();
- String input = queue.take();//保证按顺序取出数据
- String output = TestDo.doSome(input);
- System.out.println(Thread.currentThread().getName()+ ":" + output);
- semaphore.release();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }).start();
- }
- System.out.println("begin:"+(System.currentTimeMillis()/1000));
- for(int i=0;i<10;i++){ //这行不能改动
- String input = i+""; //这行不能改动
- try {
- queue.put(input);//主线程的循环产生数据,并将数据放入同步队列中,由子线程对队列进行操作
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- //不能改动此TestDo类
- class TestDo {
- public static String doSome(String input){
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- String output = input + ":"+ (System.currentTimeMillis() / 1000);
- return output;
- }
- }
public class Test {
public static void main(String[] args) {
final Semaphore semaphore = new Semaphore(1);
final SynchronousQueue<String> queue = new SynchronousQueue<String>();
for(int i=0;i<10;i++){
new Thread(new Runnable(){
@Override
public void run() {
try {
semaphore.acquire();
String input = queue.take();//保证按顺序取出数据
String output = TestDo.doSome(input);
System.out.println(Thread.currentThread().getName()+ ":" + output);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
System.out.println("begin:"+(System.currentTimeMillis()/1000));
for(int i=0;i<10;i++){ //这行不能改动
String input = i+""; //这行不能改动
try {
queue.put(input);//主线程的循环产生数据,并将数据放入同步队列中,由子线程对队列进行操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//不能改动此TestDo类
class TestDo {
public static String doSome(String input){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String output = input + ":"+ (System.currentTimeMillis() / 1000);
return output;
}
}
同步集合
传统方式下的Collection在迭代集合时,不允许对集合进行修改。同步集合类:ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、CopyOnWriteArrayList 和 CopyOnWriteArraySet。当期望许多线程访问一个给定 collection 时,ConcurrentHashMap 通常优于同步的 HashMap,ConcurrentSkipListMap 通常优于同步的 TreeMap。当期望的读数和遍历远远大于列表的更新数时,CopyOnWriteArrayList 优于同步的 ArrayList。
空中网面试题:
如果有几个线程调用TestDo.doSome(key, value)方法时,传递进去的key相等(equals比较为true),则这几个线程应互斥排队输出结果,即当有两个线程的key都是"1"时,它们中的一个要比另外其他线程晚1秒输出结果,如下所示:4:4:1258199615
1:1:1258199615
3:3:1258199615
1:2:1258199616
- import java.util.Iterator;
- import java.util.concurrent.CopyOnWriteArrayList;
- //不能改动此Test类
- public class Test extends Thread{
- private TestDo testDo;
- private String key;
- private String value;
- public Test(String key,String key2,String value){
- this.testDo = TestDo.getInstance();
- /*常量"1"和"1"是同一个对象,下面这行代码就是要用"1"+""的方式产生新的对象,
- 以实现内容没有改变,仍然相等(都还为"1"),但对象却不再是同一个的效果*/
- this.key = key+key2;
- this.value = value;
- }
- public static void main(String[] args) throws InterruptedException{
- Test a = new Test("1","","1");
- Test b = new Test("1","","2");
- Test c = new Test("3","","3");
- Test d = new Test("4","","4");
- System.out.println("begin:"+(System.currentTimeMillis()/1000));
- a.start();
- b.start();
- c.start();
- d.start();
- }
- public void run(){
- testDo.doSome(key, value);
- }
- }
- class TestDo {
- private TestDo() {}
- private static TestDo _instance = new TestDo();
- public static TestDo getInstance() {
- return _instance;
- }
- //因为在执行doSome时,需要对key进行判断和添加,需要使用同步集合
- private CopyOnWriteArrayList keys = new CopyOnWriteArrayList();
- public void doSome(Object key, String value) {
- Object o = key;//以key做同步互斥锁对象
- if(!keys.contains(o)){
- keys.add(o);
- }else{
- for(Iterator iter=keys.iterator();iter.hasNext();){
- try {
- Thread.sleep(20);//测试代码,让对集合操作的动作多执行一会
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- Object oo = iter.next();
- if(oo.equals(o)){
- o = oo;
- break;
- }
- }
- }
- synchronized(o)
- // 以大括号内的是需要局部同步的代码,不能改动!
- {
- try {
- Thread.sleep(1000);
- System.out.println(key+":"+value + ":"
- + (System.currentTimeMillis() / 1000));
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
//不能改动此Test类
public class Test extends Thread{
private TestDo testDo;
private String key;
private String value;
public Test(String key,String key2,String value){
this.testDo = TestDo.getInstance();
/*常量"1"和"1"是同一个对象,下面这行代码就是要用"1"+""的方式产生新的对象,
以实现内容没有改变,仍然相等(都还为"1"),但对象却不再是同一个的效果*/
this.key = key+key2;
this.value = value;
}
public static void main(String[] args) throws InterruptedException{
Test a = new Test("1","","1");
Test b = new Test("1","","2");
Test c = new Test("3","","3");
Test d = new Test("4","","4");
System.out.println("begin:"+(System.currentTimeMillis()/1000));
a.start();
b.start();
c.start();
d.start();
}
public void run(){
testDo.doSome(key, value);
}
}
class TestDo {
private TestDo() {}
private static TestDo _instance = new TestDo();
public static TestDo getInstance() {
return _instance;
}
//因为在执行doSome时,需要对key进行判断和添加,需要使用同步集合
private CopyOnWriteArrayList keys = new CopyOnWriteArrayList();
public void doSome(Object key, String value) {
Object o = key;//以key做同步互斥锁对象
if(!keys.contains(o)){
keys.add(o);
}else{
for(Iterator iter=keys.iterator();iter.hasNext();){
try {
Thread.sleep(20);//测试代码,让对集合操作的动作多执行一会
} catch (InterruptedException e) {
e.printStackTrace();
}
Object oo = iter.next();
if(oo.equals(o)){
o = oo;
break;
}
}
}
synchronized(o)
// 以大括号内的是需要局部同步的代码,不能改动!
{
try {
Thread.sleep(1000);
System.out.println(key+":"+value + ":"
+ (System.currentTimeMillis() / 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
本文深入探讨Java中多种线程同步技术,包括Lock与Condition的使用、读写锁的实现原理及其应用场景、Semaphore计数信号灯的工作机制、CyclicBarrier和CountDownLatch的使用案例、Exchanger的数据交换方式以及可阻塞队列的特性。此外,还通过具体实例展示了如何利用这些技术解决实际编程中的线程同步问题。
822

被折叠的 条评论
为什么被折叠?



