多线程中的两个问题探讨

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并发编程实现  机械工业出版社


以上是关于多线程中的两个问题探讨的主要内容,如果未能解决你的问题,请参考以下文章

多线程

Java多线程与并发库高级应用-工具类介绍

Java多线程与并发库高级应用-工具类介绍

多个用户访问同一段代码

Java多线程与并发库7.多个线程之间共享数据的方式探讨

多个请求是多线程吗