多线程中的两个问题探讨
Posted dengyu的博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程中的两个问题探讨相关的知识,希望对你有一定的参考价值。
问题提出
问题1 多线程中如何保证主线程等待所有子线程运行完毕再执行?
问题2 多个线程同时对一个资源文件进行读/写操作,是如何保证有序进行的呢?
解答思路及源码
对于第一个问题,我想了两种方法。下面就进行介绍吧。
第一种方法采用join()
1public class Main {
2 public static void main(String[] args){
3 for (int i = 0; i < 3 ; i++) {
4 int currentIndex = i+1;
5 Thread t = new Thread(()->{
6 try {
7 System.out.println("Thread " + currentIndex + " Enter");
8 Thread.sleep(2000);
9 System.out.println("Thread " + currentIndex + " Out");
10 } catch (InterruptedException e) {
11 e.printStackTrace();
12 }
13 });
14 t.start();
15 try {
16 t.join();//等待子线程全部执行完成
17 } catch (InterruptedException e) {
18 e.printStackTrace();
19 }
20 }
21 System.out.println("Main Thread"); //最后执行main线程
22 }
23}
第二种方法采用JUC包中的倒计时器CountDownLatch和信号量Semaphore工具类实现
1import java.util.concurrent.CountDownLatch;
2import java.util.concurrent.Semaphore;
3import java.util.concurrent.atomic.AtomicInteger;
4public class Main {
5 private static Semaphore semaphore = new Semaphore(3);//设置并发信号量为3
6 private static CountDownLatch countDownLatch = new CountDownLatch(3);
7 private static AtomicInteger atomicInteger = new AtomicInteger(0);//声明原子操作整数,初始化为0
8 public static void main(String[] args) throws InterruptedException {
9 for (int i = 0; i < 3 ; i++) {
10 new Thread(()->{
11 try {
12 semaphore.acquire(); //获取第一把锁
13 int current = atomicInteger.getAndIncrement();//保证原子性
14 System.out.println("Thread " + current + " Enter");
15 Thread.sleep(2000);
16 System.out.println("Thread " + current + " Out");
17 countDownLatch.countDown();//减少一次 这行代码必须是子线程的核心业务全部完成之后再写
18 semaphore.release(); //释放锁
19 } catch (InterruptedException e) {
20 e.printStackTrace();
21 }
22 }).start();
23 }
24 countDownLatch.await();//主线程等待,当countDownLatch线程数量为0时,主线程执行
25 System.out.println(" Main Thread ");
26 }
27}
输出结果
Thread 1 Enter
Thread 1 Out
Thread 2 Enter
Thread 2 Out
Thread 3 Enter
Thread 3 Out
Main Thread
第二个问题,思路是线程间的通信原理,可以使用等待/唤醒机制wait/notify notifyAll 或者锁Lock的await/signal实现。主要是存在一个共享文件的锁的同步。
例如:有两个线程同时读或写操作一个txt文件,假设我们考虑2个写线程
线程1 :写入 hello world
线程2 :写入 concurrent thread
保证线程1和线程2是交替执行,写入到hello.txt文件中。
步骤1 先创建一个WRFile类,包含2个方法write1(),write2()
1public class WRFile {
2 boolean flag = false;
3 public WRFile(){
4 }
5 public void write1(){
6 synchronized (this){
7 //如果flag = true
8 if(this.flag){
9 try {
10 this.wait(); //进入等待
11 } catch (InterruptedException e) {
12 e.printStackTrace();
13 }
14 }
15 RandomAccessFile randomAccessFile = null;
16 try {
17 randomAccessFile = new RandomAccessFile("hello.txt","rw");
18 randomAccessFile.seek(randomAccessFile.length());
19 randomAccessFile.writeBytes("hello world");
20 randomAccessFile.writeBytes("\r\n");
21 } catch (FileNotFoundException e) {
22 e.printStackTrace();
23 } catch (IOException e) {
24 e.printStackTrace();
25 }finally {
26 //关闭
27 try {
28 randomAccessFile.close();
29 } catch (IOException e) {
30 e.printStackTrace();
31 }
32 }
33 //修改标记
34 this.flag = true;
35 this.notifyAll();
36 }
37 }
38 public void write2(){
39 synchronized (this){
40 //如果flag = false
41 if(!this.flag){
42 try {
43 this.wait(); //进入等待
44 } catch (InterruptedException e) {
45 e.printStackTrace();
46 }
47 }
48 RandomAccessFile randomAccessFile = null; //既可以写又可以读
49 try {
50 randomAccessFile = new RandomAccessFile("hello.txt","rw");
51 randomAccessFile.seek(randomAccessFile.length());
52 randomAccessFile.writeBytes("concurrent thread");
53 randomAccessFile.writeBytes("\r\n");
54 } catch (FileNotFoundException e) {
55 e.printStackTrace();
56 } catch (IOException e) {
57 e.printStackTrace();
58 }finally {
59 //关闭
60 try {
61 randomAccessFile.close();
62 } catch (IOException e) {
63 e.printStackTrace();
64 }
65 }
66 //修改标记
67 this.flag = false;
68 this.notifyAll();
69 }
70 }
71}
步骤2 创建新建2个线程类,FirstThread,SecondThread。一个线程专门写入hello world,另一个线程专门写入 concurrent thread
1public class FirstThread implements Runnable {
2 private WRFile wrFile;
3 public FirstThread(WRFile wrFile){
4 this.wrFile = wrFile;
5 }
6 @Override
7 public void run() {
8 while(true){
9 wrFile.write1();
10 }
11 }
12}
13public class SecondThread implements Runnable {
14 private WRFile wrFile;
15 public SecondThread(WRFile wrFile){
16 this.wrFile = wrFile;
17 }
18 @Override
19 public void run() {
20 while(true){
21 wrFile.write2();
22 }
23 }
24}
步骤3 编写一个Main函数,创建2个线程对象和一个WRFile对象,同时他们共享同一个WRFile对象锁
1public class Main {
2 public static void main(String[] args){
3 //交替把2个线程的数据写入到一个txt文本中
4 final WRFile wrFile = new WRFile();
5 Thread t1 = new Thread(new FirstThread(wrFile));
6 Thread t2 = new Thread(new SecondThread(wrFile));
7 t1.start();
8 t2.start();
9 }
10}
最后 启动2个线程。
输出结果:
hello world
concurrent thread
hello world
concurrent thread
…
总结:
java中的多线程是难掌握的一项技术,只有多加练习和深入源码剖析才能掌握它的精髓,其次还要看书,理解它的原理。
参考书目
1zhangpan_soft博客http://blog.csdn.net/zhangpan_soft/article/details/52415238
2飘杨……博客 https://www.cnblogs.com/tony-yang-flutter/p/3506421.html
3Doug Lea Java并发编程实现 机械工业出版社
以上是关于多线程中的两个问题探讨的主要内容,如果未能解决你的问题,请参考以下文章