
要实现这个情况,必须知道以下几点
2、在main线程(主线程)中,需要得到所有线程的引用。
3、知道jdk提供的CountDownLatch的用法
例子如下:
public static void main(String[] args) throws InterruptedException
{
//CountDownLatch作为计数器纪录有几个线程,例如有2个线程
CountDownLatch latch=new CountDownLatch(2);
Worker worker1=new Worker( latch);
Worker worker2=new Worker(latch);
worker1start();// 启动线程
worker2start();//
//等待所有工人完成工作
latchawait();
Systemoutprintln(;all work done at ;+sdfformat(new Date()));
}
class Worker extends Thread
{
private CountDownLatch latch;
public Worker(CountDownLatch latch)
{
thislatch = latch;
}
public void run()
{
xxxxx
//在run方法结束之前,讲线程计数器减一
latchcountDown();
}
}
oncurrent包里面的CountDownLatch其实可以把它看作一个计数器,只不过这个计数器的 *** 作是原子 *** 作,同时只能有一个线程去 *** 作这个计数器,也就是同时只能有一个线程去减这个计数器里面的值。 CountDownLatch的一个非常典型的应用场景是:有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行。假如我们这个想要继续往下执行的任务调用一个CountDownLatch对象的await()方法,其他的任务执行完自己的任务后调用同一个CountDownLatch对象上的countDown()方法,这个调用await()方法的任务将一直阻塞等待,直到这个CountDownLatch对象的计数值减到0为止。
出现这种情况是因为主线程没有等到子线程计算完毕就就把它们的结果相加输出,答案肯定不对,所以要做的是等待子线程计算完成主线程再把它们的结果相加并输出,可以使用 CountDownLatch,代码如下:
package application;import javautilconcurrent;
class QiuheThread extends Thread{
int index; //线程编号
int sum=0; //保存每个线程所求的20个数的和值
CountDownLatch countDownLatch;
QiuheThread(int nindex, CountDownLatch countDownLatch){
index=nindex;
thiscountDownLatch = countDownLatch;
}
public void run() { //线程执行体
for(int i=0;i<20;i++) {
sum=sum+index20+i+1;
}
// 标记计算结束
countDownLatchcountDown();
}
}
public class test1 {
public static void main(String args[]) {
CountDownLatch countDownLatch = new CountDownLatch(5);
QiuheThread q[]=new QiuheThread[5];
for(int i=0;i<5;i++){
q[i]=new QiuheThread(i, countDownLatch);
q[i]start();
}
try {
// 等待子线程结束
countDownLatchawait();
} catch (InterruptedException e) {
}
Systemoutprintln("sum="+(q[0]sum+q[1]sum+q[2]sum+q[3]sum+q[4]sum));
}
}
ArrayList和Vector有什么区别?HashMap和HashTable有什么区别?StringBuilder和StringBuffer有什么区别?这些都是Java面试中常见的基础问题 面对这样的问题 回答是 ArrayList是非线程安全的 Vector是线程安全的 HashMap是非线程安全的 HashTable是线程安全的 StringBuilder是非线程安全的 StringBuffer是线程安全的 因为这是昨晚刚背的《Java面试题大全》上面写的 此时如果继续问 什么是线程安全?线程安全和非线程安全有什么区别?分别在什么情况下使用?这样一连串的问题 一口老血就喷出来了…
非线程安全的现象模拟
这里就使用ArrayList和Vector二者来说明
下面的代码 在主线程中new了一个非线程安全的ArrayList 然后开 个线程分别向这个ArrayList里面添加元素 每个线程添加 个元素 等所有线程执行完成后 这个ArrayList的size应该是多少?应该是 个?
[java]
public class Main
{
public static void main(String[] args)
{
// 进行 次测试
for(int i = ; i < ; i++)
{
test();
}
}
public static void test()
{
// 用来测试的List
List<Object> list = new ArrayList<Object>();
// 线程数量( )
int threadCount = ;
// 用来让主线程等待threadCount个子线程执行完毕
CountDownLatch countDownLatch = new CountDownLatch(threadCount);
// 启动threadCount个子线程
for(int i = ; i < threadCount; i++)
{
Thread thread = new Thread(new MyThread(list countDownLatch));
thread start();
}
try
{
// 主线程等待所有子线程执行完成 再向下执行
countDownLatch await();
}
catch (InterruptedException e)
{
e printStackTrace();
}
// List的size
System out println(list size());
}
}
class MyThread implements Runnable
{
private List<Object> list;
private CountDownLatch countDownLatch;
public MyThread(List<Object> list CountDownLatch countDownLatch)
{
this list = list;
untDownLatch = countDownLatch;
}
public void run()
{
// 每个线程向List中添加 个元素
for(int i = ; i < ; i++)
{
list add(new Object());
}
// 完成一个子线程
untDown();
}
}
public class Main
{
public static void main(String[] args)
{
// 进行 次测试
for(int i = ; i < ; i++)
{
test();
}
}
public static void test()
{
// 用来测试的List
List<Object> list = new ArrayList<Object>();
// 线程数量( )
int threadCount = ;
// 用来让主线程等待threadCount个子线程执行完毕
CountDownLatch countDownLatch = new CountDownLatch(threadCount);
// 启动threadCount个子线程
for(int i = ; i < threadCount; i++)
{
Thread thread = new Thread(new MyThread(list countDownLatch));
thread start();
}
try
{
// 主线程等待所有子线程执行完成 再向下执行
countDownLatch await();
}
catch (InterruptedException e)
{
e printStackTrace();
}
// List的size
System out println(list size());
}
}
class MyThread implements Runnable
{
private List<Object> list;
private CountDownLatch countDownLatch;
public MyThread(List<Object> list CountDownLatch countDownLatch)
{
this list = list;
untDownLatch = countDownLatch;
}
public void run()
{
// 每个线程向List中添加 个元素
for(int i = ; i < ; i++)
{
list add(new Object());
}
// 完成一个子线程
untDown();
}
}
上面进行了 次测试(为什么要测试 次?因为非线程安全并不是每次都会导致问题)
输出结果
上面的输出结果发现 并不是每次测试结果都是 有好几次测试最后ArrayList的size小于 甚至时不时会抛出个IndexOutOfBoundsException异常 (如果没有这个现象可以多试几次)
这就是非线程安全带来的问题了 上面的代码如果用于生产环境 就会有隐患就会有BUG了
再用线程安全的Vector来进行测试 上面代码改变一处 test()方法中
[java]
List<Object> list = new ArrayList<Object>();
List<Object> list = new ArrayList<Object>();改成
[java]
List<Object> list = new Vector<Object>();
List<Object> list = new Vector<Object>();
再运行程序
输出结果
再多跑几次 发现都是 没有任何问题 因为Vector是线程安全的 在多线程 *** 作同一个Vector对象时 不会有任何问题
再换成LinkedList试试 同样还会出现ArrayList类似的问题 因为LinkedList也是非线程安全的
二者如何取舍
非线程安全是指多线程 *** 作同一个对象可能会出现问题 而线程安全则是多线程 *** 作同一个对象不会有问题
线程安全必须要使用很多synchronized关键字来同步控制 所以必然会导致性能的降低
所以在使用的时候 如果是多个线程 *** 作同一个对象 那么使用线程安全的Vector 否则 就使用效率更高的ArrayList
非线程安全!=不安全
有人在使用过程中有一个不正确的观点 我的程序是多线程的 不能使用ArrayList要使用Vector 这样才安全
非线程安全并不是多线程环境下就不能使用 注意我上面有说到 多线程 *** 作同一个对象 注意是同一个对象 比如最上面那个模拟 就是在主线程中new的一个ArrayList然后多个线程 *** 作同一个ArrayList对象
如果是每个线程中new一个ArrayList 而这个ArrayList只在这一个线程中使用 那么肯定是没问题的
线程安全的实现
线程安全是通过线程同步控制来实现的 也就是synchronized关键字
在这里 我用代码分别实现了一个非线程安全的计数器和线程安全的计数器Counter 并对他们分别进行了多线程测试
非线程安全的计数器
[java]
public class Main
{
public static void main(String[] args)
{
// 进行 次测试
for(int i = ; i < ; i++)
{
test();
}
}
public static void test()
{
// 计数器
Counter counter = new Counter();
// 线程数量( )
int threadCount = ;
// 用来让主线程等待threadCount个子线程执行完毕
CountDownLatch countDownLatch = new CountDownLatch(threadCount);
// 启动threadCount个子线程
for(int i = ; i < threadCount; i++)
{
Thread thread = new Thread(new MyThread(counter countDownLatch));
thread start();
}
try
{
// 主线程等待所有子线程执行完成 再向下执行
countDownLatch await();
}
catch (InterruptedException e)
{
e printStackTrace();
}
// 计数器的值
System out println(counter getCount());
}
}
class MyThread implements Runnable
{
private Counter counter;
private CountDownLatch countDownLatch;
public MyThread(Counter counter CountDownLatch countDownLatch)
{
unter = counter;
untDownLatch = countDownLatch;
}
public void run()
{
// 每个线程向Counter中进行 次累加
for(int i = ; i < ; i++)
{
counter addCount();
}
// 完成一个子线程
untDown();
}
}
class Counter
{
private int count = ;
public int getCount()
{
return count;
}
public void addCount()
{
count++;
}
}
public class Main
{
public static void main(String[] args)
{
// 进行 次测试
for(int i = ; i < ; i++)
{
test();
}
}
public static void test()
{
// 计数器
Counter counter = new Counter();
// 线程数量( )
int threadCount = ;
// 用来让主线程等待threadCount个子线程执行完毕
CountDownLatch countDownLatch = new CountDownLatch(threadCount);
// 启动threadCount个子线程
for(int i = ; i < threadCount; i++)
{
Thread thread = new Thread(new MyThread(counter countDownLatch));
thread start();
}
try
{
// 主线程等待所有子线程执行完成 再向下执行
countDownLatch await();
}
catch (InterruptedException e)
{
e printStackTrace();
}
// 计数器的值
System out println(counter getCount());
}
}
class MyThread implements Runnable
{
private Counter counter;
private CountDownLatch countDownLatch;
public MyThread(Counter counter CountDownLatch countDownLatch)
{
unter = counter;
untDownLatch = countDownLatch;
}
public void run()
{
// 每个线程向Counter中进行 次累加
for(int i = ; i < ; i++)
{
counter addCount();
}
// 完成一个子线程
untDown();
}
}
class Counter
{
private int count = ;
public int getCount()
{
return count;
}
public void addCount()
{
count++;
}
}
上面的测试代码中 开启 个线程 每个线程对计数器进行 次累加 最终输出结果应该是
但是上面代码中的Counter未进行同步控制 所以非线程安全
输出结果
稍加修改 把Counter改成线程安全的计数器
[java]
class Counter
{
private int count = ;
public int getCount()
{
return count;
}
public synchronized void addCount()
{
count++;
}
}
class Counter
{
private int count = ;
public int getCount()
{
return count;
}
public synchronized void addCount()
{
count++;
}
}
上面只是在addCount()方法中加上了synchronized同步控制 就成为一个线程安全的计数器了 再执行程序
输出结果
lishixinzhi/Article/program/Java/gj/201311/27519
1、使用线程类自带的join方法,将子线程加入到主线程,在子线程执行完之后,在执行主线程逻辑。
例如
[java] view plain copy
public static void joinDemo()
throws InterruptedException
{
Systemoutprintln("=========Test with join=====");
JoinWorker worker1 = new JoinWorker("worker1");
JoinWorker worker2 = new JoinWorker("worker2");
worker1start();
worker2start();
worker1join();
worker2join();
doSuperWork();
}
2、使用JDK的并发包中的CountDownLatch类, 使用CountDownLatch,每个线程调用其countDown方法使计数器-1,主线程调用await方法阻塞等待,直到CountDownLatch计数器为0时继续执行,例如
首先,定义子线程
[java] view plain copy
static class CountDownLatchWorker extends Thread
{
String workerName;
CountDownLatch latch;
public CountDownLatchWorker(String workerName, CountDownLatch latch)
{
thisworkerName = workerName;
thislatch = latch;
}
public void run()
{
Systemoutprintln("Sub Worker " + workerName + " do work begin at "
+ sdfformat(new Date()));
new ThreadWaitDemo()doSomeWork();// 做实际工作
Systemoutprintln("Sub Worker " + workerName + " do work complete at "
+ sdfformat(new Date()));
latchcountDown();// 完成之后,计数器减一
}
}
主线程中调研await方法阻塞等待,直到所有线程完成
[html] view plain copy
public static void countDownLatchDemo()
throws InterruptedException
{
Systemoutprintln("=========Test with CountDownLatch=====");
CountDownLatch latch = new CountDownLatch(2);
CountDownLatchWorker worker1 = new CountDownLatchWorker("worker1", latch);
CountDownLatchWorker worker2 = new CountDownLatchWorker("worker2", latch);
worker1start();
worker2start();
//主线程阻塞等待
latchawait();
doSuperWork();
}
3、使用JDK并发包CyclicBarrier,CyclicBarrier类似于CountDownLatch也是个计数器, 不同的是CyclicBarrier的await()方法没被调用一次,计数便会减少1,并阻塞住当前线程。当计数减至0时,阻塞解除,所有在此 CyclicBarrier 上面阻塞的线程开始运行。 在这之后,如果再次调用 await()方法,计数就又会变成 N-1,新一轮重新开始CyclicBarrier初始时还可带一个Runnable的参数,此Runnable任务在CyclicBarrier的数目达到后,所有其它线程被唤醒前被执行。
示例如下
[java] view plain copy
public static void cyclicBarrierDemo()
throws InterruptedException, BrokenBarrierException
{
Systemoutprintln("=========Test with CyclicBarrier=====");
CyclicBarrier cb = new CyclicBarrier(2, new Runnable()
{
// 将主线程业务放到CyclicBarrier构造方法中,所有线程都到达Barrier时执行
@SuppressWarnings("static-access")
public void run()
{
new ThreadWaitDemo()doSuperWork();
}
});// 设定需要等待两个线程
ExecutorService executor = ExecutorsnewFixedThreadPool(2);
CyclicBarrierWorker worker1 = new CyclicBarrierWorker("worker1", cb);
CyclicBarrierWorker worker2 = new CyclicBarrierWorker("worker2", cb);
executorexecute(worker1);
executorexecute(worker2);
executorshutdown();
}
4、使用JDK并发包中的Executors框架,ExecutorService的的invokeAll方法调研callable集合,批量执行多个线程,在invokeAll方法结束之后,再执行主线程其他业务逻辑
示例如下
[java] view plain copy
public static void callableDemo()
throws InterruptedException
{
Systemoutprintln("=========Test with Callable=====");
List<Callable<Integer>> callList = new ArrayList<Callable<Integer>>();
ExecutorService exec = ExecutorsnewFixedThreadPool(2);
// 采用匿名内部类实现
callListadd(new Callable<Integer>()
{
public Integer call()
throws Exception
{
Systemoutprintln("Sub Worker worker1 do work begin at " + sdfformat(new Date()));
new ThreadWaitDemo()doSomeWork();// 做实际工作
Systemoutprintln("Sub Worker worker1 do work complete at "
+ sdfformat(new Date()));
return 0;
}
});
callListadd(new Callable<Integer>()
{
public Integer call()
throws Exception
{
Systemoutprintln("Sub Worker worker2 do work begin at " + sdfformat(new Date()));
new ThreadWaitDemo()doSomeWork();// 做实际工作
Systemoutprintln("Sub Worker worker2 do work complete at "
+ sdfformat(new Date()));
return 0;
}
});
execinvokeAll(callList);
execshutdown();
doSuperWork();
}
5、这种过于恶心,只简单说一下方法,主线程创建一个线程List,将每个子线程保存到列表中,然后定期轮询列表中子线程状态,当所有线程都完成之后,再执行主线程逻辑主要内容:
进程是资源分配的最小单位,每个进程都有独立的代码和数据空间,一个进程包含 1 到 n 个线程。线程是 CPU 调度的最小单位,每个线程有独立的运行栈和程序计数器,线程切换开销小。
Java 程序总是从主类的 main 方法开始执行,main 方法就是 Java 程序默认的主线程,而在 main 方法中再创建的线程就是其他线程。在 Java 中,每次程序启动至少启动 2 个线程。一个是 main 线程,一个是垃圾收集线程。每次使用 Java 命令启动一个 Java 程序,就相当于启动一个 JVM 实例,而每个 JVM 实例就是在 *** 作系统中启动的一个进程。
多线程可以通过继承或实现接口的方式创建。
Thread 类是 JDK 中定义的用于控制线程对象的类,该类中封装了线程执行体 run() 方法。需要强调的一点是,线程执行先后与创建顺序无关。
通过 Runnable 方式创建线程相比通过继承 Thread 类创建线程的优势是避免了单继承的局限性。若一个 boy 类继承了 person 类,boy 类就无法通过继承 Thread 类的方式来实现多线程。
使用 Runnable 接口创建线程的过程:先是创建对象实例 MyRunnable,然后将对象 My Runnable 作为 Thread 构造方法的入参,来构造出线程。对于 new Thread(Runnable target) 创建的使用同一入参目标对象的线程,可以共享该入参目标对象 MyRunnable 的成员变量和方法,但 run() 方法中的局部变量相互独立,互不干扰。
上面代码是 new 了三个不同的 My Runnable 对象,如果只想使用同一个对象,可以只 new 一个 MyRunnable 对象给三个 new Thread 使用。
实现 Runnable 接口比继承 Thread 类所具有的优势:
线程有新建、可运行、阻塞、等待、定时等待、死亡 6 种状态。一个具有生命的线程,总是处于这 6 种状态之一。 每个线程可以独立于其他线程运行,也可和其他线程协同运行。线程被创建后,调用 start() 方法启动线程,该线程便从新建态进入就绪状态。
NEW 状态(新建状态) 实例化一个线程之后,并且这个线程没有开始执行,这个时候的状态就是 NEW 状态:
RUNNABLE 状态(就绪状态):
阻塞状态有 3 种:
如果一个线程调用了一个对象的 wait 方法, 那么这个线程就会处于等待状态(waiting 状态)直到另外一个线程调用这个对象的 notify 或者 notifyAll 方法后才会解除这个状态。
run() 里的代码执行完毕后,线程进入终结状态(TERMINATED 状态)。
线程状态有 6 种:新建、可运行、阻塞、等待、定时等待、死亡。
我们看下 join 方法的使用:
运行结果:
我们来看下 yield 方法的使用:
运行结果:
线程与线程之间是无法直接通信的,A 线程无法直接通知 B 线程,Java 中线程之间交换信息是通过共享的内存来实现的,控制共享资源的读写的访问,使得多个线程轮流执行对共享数据的 *** 作,线程之间通信是通过对共享资源上锁或释放锁来实现的。线程排队轮流执行共享资源,这称为线程的同步。
Java 提供了很多同步 *** 作(也就是线程间的通信方式),同步可使用 synchronized 关键字、Object 类的 wait/notifyAll 方法、ReentrantLock 锁、无锁同步 CAS 等方式来实现。
ReentrantLock 是 JDK 内置的一个锁对象,用于线程同步(线程通信),需要用户手动释放锁。
运行结果:
这表明同一时间段只能有 1 个线程执行 work 方法,因为 work 方法里的代码需要获取到锁才能执行,这就实现了多个线程间的通信,线程 0 获取锁,先执行,线程 1 等待,线程 0 释放锁,线程 1 继续执行。
synchronized 是一种语法级别的同步方式,称为内置锁。该锁会在代码执行完毕后由 JVM 释放。
输出结果跟 ReentrantLock 一样。
Java 中的 Object 类默认是所有类的父类,该类拥有 wait、 notify、notifyAll 方法,其他对象会自动继承 Object 类,可调用 Object 类的这些方法实现线程间的通信。
除了可以通过锁的方式来实现通信,还可通过无锁的方式来实现,无锁同 CAS(Compare-and-Swap,比较和交换)的实现,需要有 3 个 *** 作数:内存地址 V,旧的预期值 A,即将要更新的目标值 B,当且仅当内存地址 V 的值与预期值 A 相等时,将内存地址 V 的值修改为目标值 B,否则就什么都不做。
我们通过计算器的案例来演示无锁同步 CAS 的实现方式,非线程安全的计数方式如下:
线程安全的计数方式如下:
运行结果:
线程安全累加的结果才是正确的,非线程安全会出现少计算值的情况。JDK 15 开始,并发包里提供了原子 *** 作的类,AtomicBoolean 用原子方式更新的 boolean 值,AtomicInteger 用原子方式更新 int 值,AtomicLong 用原子方式更新 long 值。 AtomicInteger 和 AtomicLong 还提供了用原子方式将当前值自增 1 或自减 1 的方法,在多线程程序中,诸如 ++i 或 i++ 等运算不具有原子性,是不安全的线程 *** 作之一。 通常我们使用 synchronized 将该 *** 作变成一个原子 *** 作,但 JVM 为此种 *** 作提供了原子 *** 作的同步类 Atomic,使用 AtomicInteger 做自增运算的性能是 ReentantLock 的好几倍。
上面我们都是使用底层的方式实现线程间的通信的,但在实际的开发中,我们应该尽量远离底层结构,使用封装好的 API,例如 JUC 包(javautilconcurrent,又称并发包)下的工具类 CountDownLath、CyclicBarrier、Semaphore,来实现线程通信,协调线程执行。
CountDownLatch 能够实现线程之间的等待,CountDownLatch 用于某一个线程等待若干个其他线程执行完任务之后,它才开始执行。
CountDownLatch 类只提供了一个构造器:
CountDownLatch 类中常用的 3 个方法:
运行结果:
CyclicBarrier 字面意思循环栅栏,通过它可以让一组线程等待至某个状态之后再全部同时执行。当所有等待线程都被释放以后,CyclicBarrier 可以被重复使用,所以有循环之意。
相比 CountDownLatch,CyclicBarrier 可以被循环使用,而且如果遇到线程中断等情况时,可以利用 reset() 方法,重置计数器,CyclicBarrier 会比 CountDownLatch 更加灵活。
CyclicBarrier 提供 2 个构造器:
上面的方法中,参数 parties 指让多少个线程或者任务等待至 barrier 状态;参数 barrierAction 为当这些线程都达到 barrier 状态时会执行的内容。
CyclicBarrier 中最重要的方法 await 方法,它有 2 个重载版本。下面方法用来挂起当前线程,直至所有线程都到达 barrier 状态再同时执行后续任务。
而下面的方法则是让这些线程等待至一定的时间,如果还有线程没有到达 barrier 状态就直接让到达 barrier 的线程执行任务。
运行结果:
CyclicBarrier 用于一组线程互相等待至某个状态,然后这一组线程再同时执行,CountDownLatch 是不能重用的,而 CyclicBarrier 可以重用。
Semaphore 类是一个计数信号量,它可以设定一个阈值,多个线程竞争获取许可信号,执行完任务后归还,超过阈值后,线程申请许可信号时将会被阻塞。Semaphore 可以用来 构建对象池,资源池,比如数据库连接池。
假如在服务器上运行着若干个客户端请求的线程。这些线程需要连接到同一数据库,但任一时刻只能获得一定数目的数据库连接。要怎样才能够有效地将这些固定数目的数据库连接分配给大量的线程呢?
给方法加同步锁,保证同一时刻只能有一个线程去调用此方法,其他所有线程排队等待,但若有 10 个数据库连接,也只有一个能被使用,效率太低。另外一种方法,使用信号量,让信号量许可与数据库可用连接数为相同数量,10 个数据库连接都能被使用,大大提高性能。
上面三个工具类是 JUC 包的核心类,JUC 包的全景图就比较复杂了:
JUC 包(javautilconcurrent)中的高层类(Lock、同步器、阻塞队列、Executor、并发容器)依赖基础类(AQS、非阻塞数据结构、原子变量类),而基础类是通过 CAS 和 volatile 来实现的。我们尽量使用顶层的类,避免使用基础类 CAS 和 volatile 来协调线程的执行。JUC 包其他的内容,在其他的篇章会有相应的讲解。
Future 是一种异步执行的设计模式,类似 ajax 异步请求,不需要同步等待返回结果,可继续执行代码。使 Runnable(无返回值不支持上报异常)或 Callable(有返回值支持上报异常)均可开启线程执行任务。但是如果需要异步获取线程的返回结果,就需要通过 Future 来实现了。
Future 是位于 javautilconcurrent 包下的一个接口,Future 接口封装了取消任务,获取任务结果的方法。
在 Java 中,一般是通过继承 Thread 类或者实现 Runnable 接口来创建多线程, Runnable 接口不能返回结果,JDK 15 之后,Java 提供了 Callable 接口来封装子任务,Callable 接口可以获取返回结果。我们使用线程池提交 Callable 接口任务,将返回 Future 接口添加进 ArrayList 数组,最后遍历 FutureList,实现异步获取返回值。
运行结果:
上面就是异步线程执行的调用过程,实际开发中用得更多的是使用现成的异步框架来实现异步编程,如 RxJava,有兴趣的可以继续去了解,通常异步框架都是结合远程 >
主线程抛出一个子线程异步处理一些东西,这时主线程要等待子线程运行完成再完成(其实我是为了统计运行时间的)。
这里抛出的子线程可能递归的调用自己,就是再抛一个他的子线程出来,但是到底一共抛多少,事先是不知道的。
应用场景:1)多线程扫描文件夹内的文件,遇到文件夹内有子文件夹,要递归调用扫描线程的,等到全部扫描完成后,返回结果,显示;
2)多线程快速排序,第一次肯定是单线程的,第一次排序完成后,会分两半,这两半多线程排,递归调用了这个排序线程,这两半很有可能,极大有可能再各分两半,也就是会有4个子线程的子线程再排序。
我试过网上的那个 CountDownLatch ,但是他只能实现定义好子线程的数量,但是在以上两种情景下,事先你是不知道会有多少个子线程的!
PS:在某种需求中,比如一个大型的任务,常常需要分配好多子任务去执行,只有当所有子任务都执行完成时候,才能执行主任务,这时候,就可以选择CyclicBarrier了。这个貌似也要在开始的时候设定总线程数:CyclicBarrier(int parties)
这个和countDownLatch就差不多了呢!
你觉得呢 问题补充:niuzai 写道亲,CyclicBarrier可能是你想要的。
PS:在某种需求中,比如一个大型的任务,常常需要分配好多子任务去执行,只有当所有子任务都执行完成时候,才能执行主任务,这时候,就可以选择CyclicBarrier了。我再来看看~~试试看! 问题补充:niuzai 写道亲,CyclicBarrier这个东东是可以动态重置个数的,而countDownLatch是一次性的。只不过大多数例子CyclicBarrier初始化了个数罢了,实质上它是可以动态改变的~ 嗯 我试了下,多线程快排,小数据量还好,顺利执行了,但是数多了后,会建N多线程等待,会outofmemory,呵呵!
不过证明这个方法是可以的! 问题补充:niuzai 写道亲,那你就结合线程池 *** 作,设置线程数目上限。不要每个任务就产生一个线程咯~ 产生新的线程是很耗内存的,线程太多当然就内存溢出咯~嗯 你说的很对!要结合线程池的!
以上就是关于当handler通知主线程时 主线程会挂起当前任务吗全部的内容,包括:当handler通知主线程时 主线程会挂起当前任务吗、android countdownlatch能控制主线程吗、Java用五个线程求1到100的和,怎么答案不对 求大神帮忙改正等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)