标签(空格分隔): Java 并发 多线程
本文中某些例子参考了鸿洋大神的博客的java并发专题,有需要的可以去看看~~~
CyclicBarrier
CyclicBarrier 类似一个闸门,指定数目的线程都必须到达这个闸门,闸门才会打开。
记得是阻塞线程,不是阻塞操作,在同一个线程使劲掉await是没什么效果的。
下面创建12个线程,模拟每3个线程到达之后进行操作:
CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("Hello CyclicBarrier");
}
});
for (int i = 0; i < 12; i++) {
//短暂休眠,使效果明显
Thread.sleep(100);
Thread s = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "Begin");
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName() + "End");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
s.start();
}
}
CountDownLatch
CountDowmLatch是一种灵活的闭锁实现,包含一个计数器,该计算器初始化为一个正数,表示需要等待事件的数量。countDown方法递减计数器,表示有一个事件发生,而await方法等待计数器到达0,表示所有需要等待的事情都已经完成。
下面创建4个线程,分别依次打印ABCD
public class CountDownLachTest {
CountDownLatch countDownLatch1 = new CountDownLatch(1);
CountDownLatch countDownLatch2 = new CountDownLatch(1);
CountDownLatch countDownLatch3 = new CountDownLatch(1);
CountDownLatch countDownLatch4 = new CountDownLatch(1);
public void test(){
//四个线程准备完毕后,唤醒线程1进行打印
CyclicBarrier cyclicBarrier = new CyclicBarrier(4,new Runnable() {
@Override
public void run() {
// 唤醒t1
countDownLatch1.countDown();
}
});
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
//阻塞,等待四个线程都打印完毕
cyclicBarrier.await();
//阻塞,等待cyclicBarrier来唤醒
countDownLatch1.await();
System.out.println("A");
//重置
countDownLatch1 = new CountDownLatch(1);
//唤醒t2
countDownLatch2.countDown();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
cyclicBarrier.await();
countDownLatch2.await();
System.out.println("B");
countDownLatch2 = new CountDownLatch(1);
countDownLatch3.countDown();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
cyclicBarrier.await();
countDownLatch3.await();
System.out.println("C");
countDownLatch3 = new CountDownLatch(1);
countDownLatch4.countDown();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
Thread t4 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
cyclicBarrier.await();
countDownLatch4.await();
System.out.println("D");
countDownLatch4 = new CountDownLatch(1);
countDownLatch1.countDown();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
t1.start();
t2.start();
t3.start();
t4.start();
}
public static void main(String[] args) {
CountDownLachTest test = new CountDownLachTest();
test.test();
}
}
Semaphore
Semaphore就是操作系统中的信号量,是为了解决资源分配问题,限制同一时刻访问某一资源的线程个数,使用 `new Semaphore(int permits)`创建实例,其中参数`permits`为允许进入线程的个数,此外,还可以使用`new Semaphore(int permits, boolean fair)`构造方法指定`Semaphore`的分配方式是否为FIFO(先进先出),即影响资源可获取时是否分配给最先申请的线程还是随机分给等待线程。
具体使用为:`Semaphore#acquire()`获取资源(会阻塞) -> doSomething() ->`Semaphore#release();`。如果没有许可,那么acquire方法将会一直阻塞直到有许可(或者直到被终端或者操作超时)。
下面例子为模拟打印机打印(模拟只有一台打印机,若有多台,修改new Semaphore(int permits)
中的permits
即可):
public class SemaphoreTest {
/**
* 定义初始值为1的信号量
*/
private final Semaphore semaphore = new Semaphore(1);
/**
* 模拟打印操作
* @param str
* @throws InterruptedException
*/
public void print(String str) throws InterruptedException
{
//请求许可
semaphore.acquire();
//doSomething();
System.out.println(Thread.currentThread().getName()+" enter ...");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "正在打印 ..." + str);
System.out.println(Thread.currentThread().getName()+" out ...");
//释放许可
semaphore.release();
}
public static void main(String[] args)
{
final SemaphoreTest print = new SemaphoreTest();
/**
* 开启10个线程,抢占打印机
*/
for (int i = 0; i < 10; i++)
{
new Thread()
{
public void run()
{
try
{
print.print("helloworld");
} catch (InterruptedException e)
{
e.printStackTrace();
}
};
}.start();
}
}
}
FutureTask
FutureTask 有点类似Runnable,都可以通过Thread来启动,不过FutureTask可以返回执行完毕的数据,并且FutureTask的get方法支持阻塞。
由于FutureTask可以返回执行完毕的数据,并且FutureTask的get方法支持阻塞这两个特性,我们可以用来预先加载一些可能用到资源,然后要用的时候,调用get方法获取(如果资源加载完,直接返回;否则继续等待其加载完成)。
使用FutureTask来预加载稍后要用的的数据:
public class PreLoaderUseFutureTask
{
/**
* 创建一个FutureTask用来加载资源
*/
private final FutureTask<String> futureTask = new FutureTask<String>(
new Callable<String>()
{
@Override
public String call() throws Exception
{
Thread.sleep(3000);
return "加载资源需要3秒";
}
});
public final Thread thread = new Thread(futureTask);
public void start()
{
thread.start();
}
/**
* 获取资源
*
* @return
* @throws ExecutionException
* @throws InterruptedException
*/
public String getRes() throws InterruptedException, ExecutionException
{
return futureTask.get();//加载完毕直接返回,否则等待加载完毕
}
public static void main(String[] args) throws InterruptedException, ExecutionException
{
PreLoaderUseFutureTask task = new PreLoaderUseFutureTask();
/**
* 开启预加载资源
*/
task.start();
// 用户在真正需要加载资源前进行了其他操作了2秒
Thread.sleep(2000);
/**
* 获取资源
*/
System.out.println(System.currentTimeMillis() + ":开始加载资源");
String res = task.getRes();
System.out.println(res);
System.out.println(System.currentTimeMillis() + ":加载资源结束");
}
}
锁机制
锁的机制类似信号量,通过Lock#lock()获取许可或者阻塞 -> 具体操作 -> Lock#unlock()释放
同样实现(创建4个线程,分别依次打印ABCD)
public static int state = 0;
Lock lock = new ReentrantLock();
Runnable run1 = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
lock.lock();
if (state % 4 == 0) {
state++;
System.out.println("A");
}
lock.unlock();
}
}
};
Runnable run2 = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
lock.lock();
if (state % 4 == 1) {
state++;
System.out.println("B");
}
lock.unlock();
}
}
};
Runnable run3 = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
lock.lock();
if (state % 4 == 2) {
state++;
System.out.println("C");
}
lock.unlock();
}
}
};
Runnable run4 = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
lock.lock();
if (state % 4 == 3) {
state++;
System.out.println("D");
}
lock.unlock();
}
}
};
利用信号量同样可以实现,只需要Semaphore semaphore = new Semaphore(1)
,然后将lock.lock()
替换为semaphore.acquire()
,lock.unlock()
替换为semaphore.release()
即可