当前位置: 首页 > article >正文

多线程14(哈希表与文件操作IO)

CountDownLatch

将一个大任务分成多个子任务,使用多线程执行子任务,从而提高效率。

那么如何衡量这些子任务都完成了呢?这就要用到CountDownLatch

CountDownLatch

1:指定指定的参数,描述拆分的任务数量

2:每个线程都执行完毕后,都执行一次CountDownLatch,当执行次数等于指定的参数时候,确认执行完毕。

public class Demo45 {

    public static void main(String[] args) throws InterruptedException {
        //将任务拆分成十个独立部分执行,每个任务都视为一个子任务
        //我们可以安排十个线程,也可以使用线程池

        CountDownLatch latch = new CountDownLatch(10);//十个任务个数

        ExecutorService executor = Executors.newFixedThreadPool(4);//线程池数量为4
        for(int i = 0;i <10;i++){
            int id = i;
            executor.submit(()->{
                System.out.println("子任务开始执行:" + id);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("子任务执行结束:" + id);
                latch.countDown();//执行完毕一次子任务就CountDownLatch一次
            });

        }
        latch.await();//阻塞等待所有任务完毕
        System.out.println("执行完毕");
        executor.shutdown();//结束线程池
    }
}

多线程下ArrayList的使用

1:自行加锁,使用synchronized和ReentrantLock

2:使用Collections.synchronizedList();,返回的方法是带有synchronization封装的。

3:使用CopyOnWriteArrayList。CopyOnWriteArrayList不会加锁。

        这是一种编程常见的思想方法:写时拷贝,在修改不同变量和读变量不会出现线程安全。

        在多线程进行写修改的时候,会将原来的数据进行复制,在复制过程中,有其他线程在读,会读取旧的数据。随后将引用指向复制后的数据,进行修改。

        CopyOnWriteArrayList在进行复制操作并不是原子性,会导致会消耗一定的时间,但不影响线程的读取。在复制完毕之后,引用指向复制后的数据进行修改。

        这样确使读操作要么是新数据要么是旧数据,不会出现读取一半新数据一半旧数据的情况,确保了“原子性”。

        CopyOnWriteArrayList在这过程中是没有加锁的,所以不会产生阻塞。

        CopyOnWriteArrayList缺点

                1:因为需要复制,效率会比较低效,不适合较大的数组。

                2:多个线程同时修改,容易出问题。

        适用场所 

                服务器重新加载。在服务器还在运行的时候,更改配置文件,我们一般需要重写启动服务器才会生效。原因是:配置其实是读取服务器中的内存,以哈希/数组的方式存储,服务器其他的线程会读取这些哈希/数组。

                此时,我们手动更改配置并使用,服务器就会加载新的哈希/数组来加载新的配置。在加载完毕后,就会使用新的配置取代旧的配置。

多线程中哈希表的使用

1:HashMap 线程不安全,不使用

2:Hashtable 线程安全,但不推荐使用,因为有更好的替代品(给public方法都加上了synchronization)

3:ConcurrentHashMap:按照桶级级别加锁,而不是给一个整个哈希表加全局锁,降低锁冲突的概率。

        Hashtable其实是给this加锁。我们可以发现,如果修改的元素属于两个链表上,是不会出现线程安全问题,如果修改同一个链表上,有可能会出现线程安全问题(链表上连续的两个元素,修改时会更改前一个元素该指向谁,会出现竞争)。

        ConcurrentHashMap的做法是针对不同的锁对象(链表)加锁,在修改不同线程的时候是不会产生竞争,只有在同一个链表上修改的时候才会出现锁竞争。

        哈希表里只有一个size,在同时插入元素的时候,发现上述操作没有给size加锁,似乎会出现线程安全问题。实际上,在ConcurrentHashMap使用了原子类,避免这种问题出现。

        针对扩容,ConcurrentHashMap也做出了优化,在遇到争对同一个链表进行插入大量元素时候,会进行拆分操作,多次插入,确保每次加锁的时间不要太长。

文件操作和IO 

我们想要找到一个文件,从树根开始到我们要找到的文件这期间,我们会点开很多文件夹(目录),我们把这些文件夹记录下来,就是路径,路径一般用“/”来分割。

特别提醒,Windows支持反斜杠,默认反斜杠。

绝对路径:从盘级开始,逐级表现出来。

相对路径:前面部分以“.”省略,但需要一个基准

public class Demo {
    public static void main(String[] args) {
        String path = "./test.txt";
    }
}

 在这,我们没有定义基准路径,所以默认执行的是项目目录的路径。

在那个目录下执行,那么这个目录就是基准路径。

"..": 返回上一级的路径。

操作系统会通过路径识别到具体的文件

文件的种类

二十一点文件都是由二进制构成,这是基本条件。

二进制文件与文本文件

二进制文件的数据恰好都能在表上查找到,并且翻译过来的字符都是能构成有意义的信息。

判断方法也简单,直接记事本打开,不是乱码出现有意义的信息就是了。像word、docx这些文件都是二进制文件;.c、.java、txt纯文本,都是典型文本文件。

Java提供的操作文件

1、文件系统操作:用于文件的创建、删除、重命名等。

2、文件内容操作:针对一个文件的读和写。

File

File(File parent,String child)  //根据父目录和子目录路径创建一个新的File实列

File(String pathname)        //根据文件路径创建一个新的File实例

File(String parent,String chile)//根据父目录和子目录路径创建一个新的File实列,父目录用路径表示。

public class Demo1 {
    public static void main(String[] args) throws IOException {
//        File file = new File("D:/1234/111.txt");
        File file = new File("./111.txt");
        System.out.println(file.getParent());//返回file的父目录文件路径
        System.out.println(file.getName());//返回file对象的文件名
        System.out.println(file.getPath());//返回file对象的文件路径
        System.out.println(file.getAbsolutePath());//返回file对象的绝对路径
        System.out.println(file.getCanonicalPath());//返回file对象修饰过的绝对路径

    }
}

上面两种file的用法分别是绝对路径与相对路径。

这是./111.txt的执行结果(相对路径),在没有指定基准目录的情况下,默认用执行项目的路径。

这是D:/1234/111.txt的执行结果(绝对路径)

下面的方法可以检测文件是否存在、是否为目录、是否是文件,有则输出true,没有返回false 

public class Demo2 {
    public static void main(String[] args) {
        File file = new File("./111.txt");
        System.out.println(file.exists());//判断对象是否存在
        System.out.println(file.isFile());//判断File对象代表的文件是否是个目录
        System.out.println(file.isDirectory());//判断File对象代表的文件是否是个普通文件
    }
}

 我们可以手动找到路径创建文件/目录,也可以通过代码创建:

file.createNewFile();

 这里就不放截图结果了,大家可以自己去试验一下。


http://www.kler.cn/a/596457.html

相关文章:

  • 数据结构(排序(上)):冒泡、选择、插入
  • Vue.js 模板语法全解析:从基础到实战应用
  • Java8 流式分组(groupingBy)与分区(partitioningBy)深度解析
  • 复现关于图片重构方向的项目
  • 在线生成自定义二维码
  • 【Linux】Hadoop-3.4.1的伪分布式集群的初步配置
  • mysql——第二课
  • spring MVC 介绍
  • Java实体类(Javabean)-编程规范
  • AI Agent设计模式 四种高层次模式以及更具体的九种模式
  • CSS 文档流:元素排列的底层逻辑与布局控制
  • Android Studio最后一个绑定JDK8的版本,但是官方下载是最新的,怎么下载Android Studio历史版本包,这篇文章帮你解决。
  • 2025年消防设施操作员考试题库及答案
  • centos 7 搭建FTP user-list用户列表
  • Spring AOP实战指南:面向切面编程精髓
  • C语言:循环控制结构习题
  • 从 0 到 1:深度学习模型,重构世界的数字蓝图
  • solana增加流动性和删除流动性
  • 练习:运动计划
  • CUDA 学习(2)——CUDA 介绍