您的位置:首页 > 其它

并发问题和主线程等待所有子线程运行完毕再执行

2016-09-02 17:57 274 查看
问题引出:

我们对数据库的操作是一个耗时过程,假如我们需要让数据库批量操作完成之后,再跳转到另外一个页面,注意:是批量操作完成之后再跳转。

分析:以上需求我们遇到2个难点,

第一个难点是怎么控制并发问题,

第二个难点是怎么使主线程等待所有子线程完成之后再执行。

首先,我们先解决并发问题,其实,在jdk1.5的时候,java大牛Doug Lea线程已经解决了这个问题,我们今天就借用Doug Lea先生的API来解决这个问题。如下是详细解释和源码:

<span style="font-size:12px;">package com.bzjy.thread;

import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadTest {

private static Semaphore semaphore = new Semaphore(5);// 设置并发信号量为5
private static AtomicInteger atomicInteger = new AtomicInteger(0);// 声明原子操作整数,初始化为9

public static void main(String[] args) {
<span style="color:#009900;">// 首先介绍一个并发工具包,Semphore(信号量)
// 这里我们需要用到它的代参构造Semaphore semaphore = new Semaphore(permits);
// 参数permits的意思是同时可以存在多少个信号,或者说是同时可以并发多少个信号
// 比如说:我有100个线程,同时并发只并发5个,则设置permits为5
// 说到这里又涉及到一个问题就是,</span><span style="color:#ff0000;">线程是越多越好么</span><span style="color:#009900;">?
// 答案自然是否定的,这里Google官方给出的最佳并发数是当前服务器内核数+1
// 也就是说,pertits = cpu(内核数) + 1;假如是4核的则推荐最佳并发数是5
// 因为我的电脑是4核的,所以这里就举例并发为5,这里不再进行代码的封装,
// 只是举一个简单的例子</span>

//Semaphore semaphore = new Semaphore(5);// 设置并发信号量为5

// 这里我们已经创建好并发数了,那么我们怎么使用呢?
// 这里介绍两个核心方法,
// 第一个:semaphore.acquire(); 获取信号或者说获取一把锁
// 第二个:semaphore.release(); 释放信号或者说释放一把锁

// 示例代码如下:

for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {

@Override
public void run() {
// <span style="color:#ff0000;">注意</span>:在刚进入方法就需要获得一把锁,<span style="color:#ff0000;">再次强调,是刚进入方法就获得一把锁</span>

try {
semaphore.acquire();// 获取一把锁,因为是在匿名内部类中使用,所以需要将其声明为成员变量

// 这里我想对线程进行计数,但是是多线程并发,所以不能直接用i++来计数,
// 因此我们需要进行原子操作,为此,我们再次介绍一个工具包,“原子”,Atomic
// 这里我们使用整形的AtomicInteger,因为需要在内部类中使用,所以声明为成员变量,设置初始化为0

int current = atomicInteger.getAndIncrement();
System.out.println("第" + current +"进来了");// 打印日志

Thread.sleep(1000);// 让当前线程睡1秒,模仿耗时操作

System.out.println("第" + current +"出去了");

semaphore.release();// 释放一把锁,<span style="color:#ff0000;">必须释放,必须是方法最后一步</span>

} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}

}
}</span>
如此,我们第一个问题就解决了,注释写的很详细,相信0基础也能看懂,代码100%运行通过,这里不再贴运行结果图片。

好,第一个问题解决了,呢么我们怎么控制使主线程等待所有子线程运行结束之后再运行呢?

这里我们需要再次介绍一工具类,示例代码如下:有详细注释 

/**
* 注意:类很多代码在上面已经书写过详细注释,这里写过的注释不再书写
* @author xiyang
*
*/
public class ThreadWaitTest {

private static Semaphore semaphore = new Semaphore(5);// 设置并发信号量为5
private static AtomicInteger atomicInteger = new AtomicInteger(0);// 声明原子操作整数,初始化为9

// 这句代码详细注释见主函数,另外我这里为了方便直接设置参数为100,实际开发可以根据需求实例化
private static CountDownLatch cdl = new CountDownLatch(100);

public static void main(String[] args) throws InterruptedException {

// 因为我们需要处理主线程等待所有子线程运行结束之后再运行,
// 因此为了方便,科学,我们再次介绍一个线程并发工具包CountDownLatch,
// 同样,我们需要它的一个有参构造,CountDownLatch cdl = new CountDownLatch(count);
// 注意,这里参数,必须和线程池的最大数一致,也就是说,这里的参数必须是你线程运行的最大数
// 比如我这里要运行100个线程,并发5个,那么,这里的count==100
// 因为我们需要在内部类中使用,所以声明为成员变量

for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {

@Override
public void run() {
// 注意:在刚进入方法就需要获得一把锁,再次强调,是刚进入方法就获得一把锁

try {
semaphore.acquire();// 获取一把锁,因为是在匿名内部类中使用,所以需要将其声明为成员变量

int current = atomicInteger.getAndIncrement();
System.out.println("第" + current +"进来了");// 打印日志

Thread.sleep(1000);// 让当前线程睡1秒,模仿耗时操作

System.out.println("第" + current +"出去了");

<span style="color:#ff0000;">cdl.countDown();</span>// 此方法的意思是线程数量-1,要在当前线程执行完毕之后书写,或者说子线程核心代码执行完毕之后
semaphore.release();// 释放一把锁,必须释放,必须是方法最后一步

} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}

<span style="color:#ff0000;">cdl.await();</span>// 主线程等待,当线程数量为0时主线程执行

System.out.println("主线程执行");
// 如此就解决了线程并发问题和主线程等待所有子线程运行完毕之后运行

}
}

如此以上两个需求就解决了,这里只是示例,大家可以根据具体需求具体操作,最好是可以进行封装调用,最好
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: