Java核心技术 卷1 多线程----线程安全的集合(4)

  如果多线程要并发的修改一个数据结构,例如散列表,那么很容易会破坏这个数据结构。一个线程可能要开始向表中插入一个新元素。假定在调整散列表各个桶之间的链接关系的过程中,被剥夺了控制权。如果另一个线程也开始遍历同一个链表,可能使用无效的链接并造成混乱,会抛出异常或者陷入死循环。

  可以通过提供锁来保护共享数据结构,但是选择线程安全的实现作为替代可能更容易些。上一篇讨论的阻塞队列就是线程安全的集合。接下来讨论java类库提供的另外一些线程安全的集合。

  高效的映射表、集合和队列

  java.util.concurrent包提供了映射表、有序集合和队列的高效实现:ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet和ConcurrentLinkedQueue。

java.util.concurrent.ConcurrentLinkedQueue<E>
  • ConcurrentLinkedQueue<E>() 构造一个可以被多线程安全访问的无边界非阻塞的队列。
  • ConcurrentSkipListSet<E>(Comparateor<? super E> comp)    构造一个可以被多线程安全访问的有序集
java.util.concurrent.ConcurrentSkipListMap<K,V>
  • ConcurrentHashMap<K,V>()
  • ConcurrentHashMap<K,V>(int initialCapacity)
  • ConcurrentHashMap<K,V>(int initialCapacity,float loadFactor,int concurrencyLevel)

  参数:initialCapacity 集合的初始容量。默认值16

       loadFactor      控制调整:如果每一个桶的平均负载超过这个因子,表的大小会被重新调整。默认值为0.75.

     concurrencyLevel 并发写者结束 的估计数目。

  • ConcurrentSkipListMap<K,V>
  • ConcurrentSkipListSet<K,V>(Comparator<? super k) comp)

  构造一个可以被多线程安全访问的有序的映象表。第一个构造器要求键实现Comparable接口。

  • V putIfAbsent(K key,V value)

  如果该键没有在映像表中出现,则将给定的值同给定的键关联起来,并返回null。否则返回与该键关键的现有值。

  • boolean remove(K key,V value)

  如果给定的键与给定的值关联,删除给定的键与值并返回真。否则,返回false.

  • boolean replace(K key,V oldValue,V newValue)

  如果给定的键当前与oldvalue相关联,用它与newValue关联。否则返回false

写数组的拷贝

  CopyOnWriteArrayList和CopyOnWriteArraySet是线程安全的集合。其中所有的修改线程对底层数组进行复制。如果在集合上进行迭代的线程数超过修改线程数,这样的安排是很有用的。当构建一个迭代器的时候,它包含一个对当前数组的引用。如果数组后来被修改了,迭代器仍然引用旧数组,但是,集合的数组已经被替换了。因为旧的迭代器拥有一致的视图,访问无须任何同步开销。

Callable与Future

Runnable封装了一个异步的任务,可以把它想像成为一个没有参数和返回值的异步方法。Callable与Runnable类似,但是有返回值。Callable接口是一个参数化的类型,只有一个方法call。

public interface Callable<V>
{ V call() throws Exception;
}

  类型参数是返回值的类型。例如:Callable<Integer>表示一个最终返回Integer对象的异步计算。

  Future保存异步计算的结果。可以启动一个计算,将Future对象交给某个线程,然后忘掉它。Future对象的所有者在结果计算 好之后就可以获得它。

  Future接口具有下面的方法:

public interface Future<V>
{V get() throws ...;V get(long timeout,TimeUnit unit) throws ...;void cancel(boolean mayInterrupt);boolean isCancelled();boolean isDone();    
}    

  第一个get方法的调用被阻塞,直到计算完成。如果在计算完成之前,第二个方法的调用超时,抛出一个TimeoutException异常。如果运行该计算的线程被中断,两个方法都将抛出InterruptedException。 如果计算完成,那么get方法立即返回。

  如果计算还在进行,isDone方法返回false;如果完成了,则么回true.

  可以用cancel方法取消该计算。如果计算还没有开始,它被取消且不再开始。如果计算处于运行中,那么如果mayInterrupt参数为true,它就被中断。

  FutureTask包装器是一种非常便利的机制,可以将Callable转换成Future转换成Future和Runnable,它同时实现二者的接口。

  以下这个例子与上一篇寻找包含指定关键字的文件的例子相似。然而,现在我们仅仅计算匹配的文件数目。因此,我们有了一个需要长时间运行的任务,它产生一个数例,一个Callable<Integer>的例子。

class MatchCouner implements Callable<Integer>
{public MatchCounter(Fille directory,String keyWord){...}public Integer call(){...} //返回匹配文件的数
}    

  然后我们利用MatchCounter创建一个FutureTask对象,并用来启动一个线程。

FutureTask<Integer> task =new FutureTask<Integer>(counter);
Thread t=new Thread(task);
t.start();

  最后,打印结果: 

System.out.println(task.get()+" mathcing files.");

  当然对get的调用会发生阻塞,直到有可获得的结果为止。

  在call方法内部,使用相同的递归机制。对于每一个子目录,产生一个新的MatchCounter并为它启动一个线程。把FutureTask对象隐藏在ArrayList<Future<Integer>>中。最后所有结果加起来:

for(Future<Integer> result:results)count+=result.get()

  每一次对get的调用都会发生阻塞直到结果可获得为止。当然线程是并行运行的,因此,很可能在大致相同的时候所有的结果都可获得。完整代码如下

 1 package test.Future;
 2 
 3 import java.io.File;
 4 import java.util.Scanner;
 5 import java.util.concurrent.ExecutionException;
 6 import java.util.concurrent.FutureTask;
 7 
 8 /**
 9  * Created by Administrator on 2017/11/23.
10  */
11 public class FutureTest {
12     public static void main(String[] args) {
13         Scanner in =new Scanner(System.in);
14         System.out.print("输入查询路径:");
15         String directory=in.nextLine();
16         System.out.print("输入关键字:");
17         String keyword=in.nextLine();
18 
19         MatchCounter counter=new MatchCounter(new File(directory),keyword);
20         FutureTask<Integer> task=new FutureTask<>(counter);
21         Thread t=new Thread(task);
22         t.start();
23         try {
24             System.out.println(task.get()+" 匹配文件");
25         } catch (InterruptedException e) {
26             e.printStackTrace();
27         } catch (ExecutionException e) {
28             e.printStackTrace();
29         }
30 
31     }
32 }
View Code
 1 package test.Future;
 2 
 3 import java.io.*;
 4 import java.util.ArrayList;
 5 import java.util.List;
 6 import java.util.Scanner;
 7 import java.util.concurrent.Callable;
 8 import java.util.concurrent.ExecutionException;
 9 import java.util.concurrent.Future;
10 import java.util.concurrent.FutureTask;
11 
12 /**
13  * 此任务对包含给定关键字的目录和子目录中的文件进行计数
14  */
15 public class MatchCounter implements Callable<Integer> {
16     private File directory;
17     private String keyword;
18     private int count;
19 
20     /**
21      * @param directory 开始搜索目录
22      * @param keyword   寻找关键字
23      */
24     public MatchCounter(File directory, String keyword) {
25         this.directory = directory;
26         this.keyword = keyword;
27     }
28 
29     @Override
30     public Integer call() throws Exception {
31         count = 0;
32         try {
33             File[] files = directory.listFiles();
34             if (files == null) {
35                 return count;
36             }
37             List<Future<Integer>> results = new ArrayList<>();
38             for (File file : files
39                     ) {
40                 if (file.isDirectory()) {
41                     MatchCounter counter = new MatchCounter(file, keyword);
42                     FutureTask<Integer> task = new FutureTask<>(counter);
43                     results.add(task);
44                     Thread t = new Thread(task);
45                     t.start();
46                 } else {
47                     if (search(file)) {
48                         count++;
49                     }
50                 }
51                 for (Future<Integer> result : results) {
52                     try {
53                         count += result.get();
54                     } catch (ExecutionException e) {
55                         e.printStackTrace();
56                     }
57                 }
58             }
59         } catch (InterruptedException e) {
60         }
61         return count;
62     }
63 
64     /**
65      * 搜索一个给定关键字的文件
66      *
67      * @param file
68      * @return
69      */
70     public boolean search(File file) {
71         try{
72             try(Scanner in= new Scanner(file,"gbk")){
73                 boolean found=false;
74                 while (!found&&in.hasNextLine()){
75                     String line=in.nextLine();
76                     if (line.contains(keyword)){
77                         found=true;
78                         System.out.println(file.toString());
79                     }
80                 }
81                 return found;
82             }
83         }
84         catch (IOException e){
85             return false;
86         }
87     }
88 }
View Code
java.util.concurrent.Callable<V>

 

  •  V call() 运行将产生结果的任务
  •  V get()
  •  V get(long time,TimeUnit unit)

  获取结果,如果没有结果可用,则阻塞直到得到结果超过指定的时间为止。如果不成功,第二个方法会抛出TimeoutException异常。

  • boolean cancel(boolean mayInterrupt)

  尝试取消这一任务的运行。如果任务已经开始,并且mayInterrupt参数值为true,它就会被中断如果成功执行取消操作,返回true。

  • boolean isCancelled() 如果任务在完成前被取消了,则返回true。
  • boolean isDone() 如果任务结束,无论是正常结束、中途取消或发生异常,都返回true。
java.util.concurrent.FutureTask<V>
  • FutureTask(Callable<V> task)
  • FutureTask(Runnable task,V result) 构造一个既是Future<V>又是Runnable的对象。

  

 

转载于:https://www.cnblogs.com/gousheng107/p/7885681.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/354536.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何快速实现 Wordpress 博客域名更换?

如题&#xff0c;如何快速更换使用 Wordpress 搭建的网站、博客的域名&#xff0c;除了在域名服务商那更换域名的解析和 web服务器端的配置外&#xff0c;还应该从数据库端做些什么&#xff1f;熟悉 Wordpress 的用户都知道在 Wordpress 后台&#xff0c;设置 --> 常规 里有…

浮动—春联(文字竖直排列)

<div id"main"><div class"top">李白</div><div class"left">明月几时有</div><div class"right">把酒问青天</div> </div> 1 #main{2 height: 540px;3 …

了解下广告计费模式CPC、CPA和CPM

目前各大广告平台最常见的广告计费模式分别有CPC、CPA、CPM。例如知乎、头条、百度、腾讯等各类平台投放广告&#xff0c;基本都离不开这几种广告计费方式。由于博客流量日渐见好&#xff0c;最近也申请了谷歌的广告的流量主&#xff0c;所以有必要了解下这几种广告模式&#x…

JUnit 5 –设置

2015年11月&#xff0c; JUnit Lambda团队展示了他们的原型 。 此后&#xff0c;该项目更名为JUnit 5&#xff0c;并于2016年2月发布了Alpha版本。我们将在一系列简短文章中对其进行探讨&#xff1a; 设定 基本 建筑 条件 注射 … 本章讨论JUnit 5的设置&#xff0c;以便…

如何在 VS Code 中创建自己的代码片段

在项目开发中&#xff0c;我们经常需要新建文件&#xff0c;而这些初始化这些文件又需要敲出很多相同的代码&#xff0c;比如我们新建一个 .vue 的文件&#xff0c;需要我们在写正式的功能代码之前&#xff0c;完成以下初始化代码&#xff1a; <script setup langts> &l…

如何使用 Apifox 来管理测试你的接口

日常开发&#xff0c;你是使用 Postman 来测试接口&#xff0c;还是用接口文档生成工具 Swagger&#xff0c;最近发现了一个很好用的工具 Apifox&#xff0c;集API 文档、API 调试、API Mock、API 自动化测试功能为一体&#xff0c;兼客户端和 Web 端的强大的功能。 主要界面如…

完善系统的最后一公里,增加系统日志功能

当我们在开发一个系统的时候&#xff0c;随着规划的功能越来越多&#xff0c;按照复杂度和稳定性相反的原则&#xff0c;为了保证系统能够按照我们设想的目标运行&#xff0c;我们需要对系统的运行状况进行监控。 那么什么时候介入监控比较好&#xff1f;在系统功能开发的前期…

java泛型面试_Java泛型面试问题

java泛型面试Java面试中的通用面试问题在相当长的时间内在Java 5周围越来越普遍&#xff0c;许多应用程序都转移到Java 5上&#xff0c;并且几乎所有新的Java开发都发生在Tiger&#xff08;Java 5的代号&#xff09;上。 泛型和Java 5功能&#xff08;例如Enum&#xff09;的重…

Vue3 实现网页背景水印功能

经常有一些公司和组织出于系统文件或信息安全保密的需要&#xff0c;需要在系统网页上增加带有个人标识&#xff08;系统账号或个人信息&#xff09;的水印&#xff0c;可以简单防止截图外传首先我们来看这样一个水印功能的实现思路&#xff0c;通常是在我们原有的网页上附上一…

部署微服务– Spring Boot fatjar到Amazon Elastic Beanstalk

最近&#xff0c;我正在研究概念验证的Web应用程序&#xff0c;我想将其部署到公共云以进行快速演示。 我决定使用Amazon&#xff0c;因为我已经有过使用它的经验。 亚马逊提供了几种不同的方式来部署Java Web应用程序。 EC2使我们可以灵活地在机箱上安装和配置任何我们想要的…

在线答卷系统的前端设计与数据库系统的设计与实现

如果要你实现一个在线的答题系统&#xff0c;你能想到它该具有哪些功能&#xff1f;当我接到这样一个需求的时候&#xff0c;脑海中立马能想到的就是它有录入题库的功能&#xff0c;创建试卷后可以从题库选择试题&#xff0c;并且可以针对试题进行分数的设置和排序。试卷发布后…

jgroups传输消息_使用JGroups进行ElasticMQ消息复制

jgroups传输消息ElasticMQ是一个消息服务器&#xff0c;具有Scala&#xff0c;Java和与Amazon SQS兼容的接口。 它通过跨服务器群集复制消息来支持有保证的消息传递&#xff0c;并通过日志记录实现消息持久性。 消息复制是ElasticMQ的核心功能之一。 但是&#xff0c;如果您看一…

使用 Element 组件搭建在线学习的课程卡片设计

假如我们要做一个在线课程学习的系统&#xff0c;其中我们需要做的一个功能就是课程信息流的一个展示&#xff0c;以等高卡片列表或者瀑布流的方式呈现。首先我们来罗列下这个卡片内应该包括哪些信息点&#xff1a;课程名称课程简介分类信息课程评分等级课程文件课时观看学习人…

12个很棒的Spring数据教程来启动您的数据项目

Spring Data的任务是为数据访问提供一个熟悉且一致的&#xff0c;基于Spring的编程模型&#xff0c;同时仍保留基础数据存储的特​​殊特征。 它使使用数据访问技术&#xff0c;关系和非关系数据库&#xff0c;map-reduce框架以及基于云的数据服务变得容易。 这是一个总括项目…

python多线程编程(3): 使用互斥锁同步线程

问题的提出 上一节的例子中&#xff0c;每个线程互相独立&#xff0c;相互之间没有任何关系。现在假设这样一个例子&#xff1a;有一个全局的计数num&#xff0c;每个线程获取这个全局的计数&#xff0c;根据num进行一些处理&#xff0c;然后将num加1。很容易写出这样的代码&am…

如何防止水印被恶意删除或者隐藏?

继上篇 Vue3 实现网页背景水印功能 我们了解了常见的网页水印功能是如何实现的&#xff0c;懂原理的都知道水印是通过在网页中添加代码绘制 DOM 元素覆盖在原有的网页上而来的&#xff0c;一旦你打开浏览器中的元素审查&#xff0c;可以通过删除元素或者在元素的样式上操作属性…

jmc线程转储_如何分析线程转储– IBM VM

jmc线程转储本文是我们的线程转储分析系列的第4部分&#xff0c;它将为您提供什么是IBM VM的JVM线程转储以及您将找到的不同线程和数据点的概述。 您将看到和学习​​到&#xff0c;IBM VM Thread Dump格式是不同的&#xff0c;但是提供了更多现成的故障排除数据。 在这一点上&…

Java 8:使用交替接口公开的类型安全地图生成器

动态展示您的课程 当我是Java新手时&#xff0c;我记得当时想过应该有一种方法可以删除或隐藏我不想公开的类中的方法。 就像用private方法或类似方法覆盖public方法一样&#xff08;哪种情况是不可能的&#xff0c;也不应该是不可能的&#xff09;。 显然&#xff0c;今天&…

nodejs面试题

1、为什么用Nodejs,它有哪些缺点&#xff1f; 事件驱动&#xff0c;通过闭包很容易实现客户端的生命活期。不用担心多线程&#xff0c;锁&#xff0c;并行计算的问题V8引擎速度非常快对于游戏来说&#xff0c;写一遍游戏逻辑代码&#xff0c;前端后端通用当然Nodejs也有一些缺点…

sts-bundle的使用_使用WS-Trust / STS采样器扩展JMeter

sts-bundle的使用JMeter没有对WS-Security或WS-Trust的任何内置支持&#xff0c;这使我为JMeter开发了此STS采样器–可以在负载测试STS时使任何人的生活变得更好。 首先&#xff0c;您需要拥有Apache JMeter发行版。 我正在使用v2.7。 然后&#xff0c;您可以从此处下载sts.sam…