在并行系统中,为了避免数据竞赛的产生,我们可以:
- 使用非同步的数据结构并自行添加同步机制代码
- 使用Java concurrency API 提供的数据结构
通常第二种方法是我们所推荐的。因为Java对这些数据结构已经做了优化
阻塞和非阻塞数据结构
Java concurrency API 提供了两种并行数据结构:
- 阻塞数据结构
- 非阻塞数据结构
并行数据结构实现的接口
BlockingQueue
Queue是一个线性数据结构,允许你往队列尾部插入数据并从头部取出数据。它是一个先进先出的数据结构。
Queue接口提供了以下方法:
- 往队列尾部插入元素
- 从队列头部读取元素并删除元素
- 从队列头部读取元素但不删除元素
接口定义了这些方法的两种版本,这两个版本在操作无法满足时具有不同的行为:
- 方法抛出异常
- 方法返回特殊的值,如 false 或 null
以下表格列出具有这些不同行为的方法
Operation | Exception | Special Value |
Insert | add() | offer() |
Retrieve and remove | remove() | poll() |
Retrieve but don't remove | element() | peek() |
BlockingDeque 接口继承了 Queue 接口,并添加了当操作无法满足时阻塞调用线程的方法,这些方法有:
Operation | Blocks |
Insert | put() |
Retrieve and remove | take() |
Retrieve without removing | N/A |
BlockingDeque
Deque 和 queue 一样,但允许你往两头添加和删除元素。Deque 接口继承了 Queue 接口。除了 Queue 接口提供的方法外,它提供了往队列两头添加,读取并删除,读取但不删除等方法:
Operation | Exception | Special Value |
Insert | addFirst(), addLast() |
offerFirst(), offerLast() |
Retrieve and remove |
removeFirst(), removeLast() |
pollFirst(), pollLast() |
Retrieve without removing | getFirst(), getLast() | peekFirst(), peekLast() |
以下方法当操作无法满足时将阻塞调用线程:
Operation | Blocks |
Insert | putFirst(), putLast() |
Retrieve and remove | takeFirst(), takeLast() |
Retrieve without removing | N/A |
ConcurrentMap
Map 接口在 Java 8 里提供了一些新的方法:
- forEach():该方法对每一个元素调用参数里的函数
- compute(), computeIfAbsent(), 和 computeIfPresent():这些方法允许你传递一个方法来计算和key关联的值
- merge():该方法允许你把一对键值合并到map里,当键名不在map里时,直接插入键值。如果存在则调用参数里的方法来生成新的值
ConcurrentMap 继承了 Map 接口提供了并行操作
TransferQueue
该接口继承了 BlockingQueue 接口并添加了从生产者线程传递数据给消费者线程的方法。生产者线程可以等待直到消费者线程取走了队列中的元素。接口定义了如下方法:
- transfer():生产者把数据放入队列中然后生产者线程阻塞直到消费者线程把队列中的元素取走
- tryTransfer():如果有消费者线程正在等待读取数据,生产者则传递数据给消费者,否则该方法返回 fase 值,且数据不会被添加到队列中。
// 生产者线程 class Producer implements Runnable { private TransferQueue<String> transferQueue; private String name; private Integer numberOfMessagesToProduce; public AtomicInteger numberOfProducedMessages = new AtomicInteger(); @Override public void run() { for (int i = 0; i < numberOfMessagesToProduce; i++) { try { boolean added = transferQueue.tryTransfer("A" + i, 4000, TimeUnit.MILLISECONDS); if(added){ numberOfProducedMessages.incrementAndGet(); } } catch (InterruptedException e) { e.printStackTrace(); } } } // standard constructors } // 消费者线程 class Consumer implements Runnable { private TransferQueue<String> transferQueue; private String name; private int numberOfMessagesToConsume; public AtomicInteger numberOfConsumedMessages = new AtomicInteger(); @Override public void run() { for (int i = 0; i < numberOfMessagesToConsume; i++) { try { String element = transferQueue.take(); longProcessing(element); } catch (InterruptedException e) { e.printStackTrace(); } } } private void longProcessing(String element) throws InterruptedException { numberOfConsumedMessages.incrementAndGet(); Thread.sleep(500); } // standard constructors }
并行数据结构实现的类
LinkedBlockingQueue
该类实现了BlockingQueue接口,提供了一些阻塞方法,且允许限定队列中能保存的元素个数。 它也实现了 Queue, Collection 和 Iterable 接口。
ConcurrentLinkedQueue
该方法实现了 Queue 接口并提供了线程安全的不限元素个数的队列。它使用一个非阻塞算法确保程序不会出现数据竞赛
LinkedBlockingDeque
该类实现了 BlockingDeque 接口,提供了具有阻塞方法的deque,且允许限制队列里元素的个数。它比LinkedBlockingQueue提供了更多的方法,但同时也具有更高的延迟,因此该类只适合当你需要 deque 性能时使用。
ConcurrentLinkedDeque
该类实现了 Deque 接口,并提供了线程安全的操作,允许你往队列的两头添加或删除数据。同样的,它比 ConcurrentLinkedQueue 具有更多的方法,却也具有更高的延迟。
ArrayBlockingQueue
该类实现了BlockingQueue接口,提供了基于数组的具有有限元素个数的blocking queue的实现。同时它实现了Queue, Collection和 Iterable 接口。和基于数组的非并行数据结构 (如 ArrayList 和 ArrayDeque) 相比,ArrayBlockingQueue在初始化时分配了数组的大小且从不改变大小。
DelayQueue
该队列里的元素必须实现 Delayed 接口,所以元素必须实现 getDelay() 方法。如果方法返回一个负数或零,说明延迟到期,队列的元素可以被取出。队列头部的第一个元素是拥有最大负延迟数的那个元素
LinkedTransferQueue
该类实现了 TransferQueue 接口。它可以用作生产者和消费者的通信通道,通过它,生产者可以等待消费者处理队列中的数据
PriorityBlockingQueue
该列中的对象根据一定的规则排序
ConcurrentHashMap
该类提供了一个线程安全的哈希表。除了 Map 接口定义方法外,该类还提供了以下方法:
- search(), searchEntries(), searchKeys() 和 searchValues():该方法允许你对哈希表中的键值对,主键或值调用搜索方法。搜索方法可以是 lambda 表达式,当搜索方法返回一个非空值时,搜索结束。该非空值即是搜索方法的输出。
- reduce, reduceEntries(), reduceKeys() 和 reduceValues():这些方法允许你使用对键值对,entry, keys 和 values做 reduce() 操作
// 以下输出结果为 9 ConcurrentHashMap<String, Integer> reducedMap = new ConcurrentHashMap<>(); reducedMap.put("One", 1); reducedMap.put("Two", 2); reducedMap.put("Three", 3); System.out.println("reduce example => " +reducedMap.reduce(2, (k, v) -> v + 1, (total, elem) -> total + elem));
使用新特性
以下我们来看看 Java 8 引入的并行数据结构的新特性
ConcurrentHashMap的第一个例子
forEach() 方法
search() 方法
该方法接收两个参数:parallelismThreashold (如果map里的元素比指定的元素多,那么该方法将使用并行处理);searchFunction:该搜索方法是一个 BiFunction 接口的实现
// 以下代码搜索第一本含有词语”java“的书 ExtendedProduct firstProduct=productsByBuyer.search(100, (id, products) -> { for (ExtendedProduct product: products) { if (product.getTitle() .toLowerCase().contains("java")) { return product; } } return null; }); if (firstProduct!=null) { System.out.println(firstProduct.getBuyer()+":"+ firstProduct.getTitle()); }
另外还有其它的一些方法:
- searchEntries(parallelismThreshold, searchFunction)
- searchKeys(parallelismThreashold, searchFunction)
- searchValues(parallelismThreashold, searchFunction)
reduce() 方法
该方法类似于 Stream 框架的 reduce() 方法,它接收三个参数:
- parallelismThreshold:如果 ConcurrentHashMap 里的元素比指定的这个参数还多,那么这个方法就使用并行处理。
- transformer:该参数是一个 lambda 函数表示的 BiFunction 接口,它接收一对键名和对应的值,然后返回对键值转换后的对象。
- reducer:该参数也是一个 lambda 函数表示的 BiFunction 接口,它接收两个 transformer 方法的运行结果作为参数。该参数的目的是把两个参数合并处理成一个参数返回
compute() 方法
该方法接收两个参数,一个参数是元素的 key, 另一个参数是一个 lambda 函数表示的 BiFunction 接口。该lambda方法接收元素的key 和 value (如果key不存在则接收的value 参数为 null)。如果key存在,该方法会用lambda函数的运行结果替换掉原来的值,如果key不存在,则插入lambda方法的运行结果。如果lambda方法的运行结果为null,则从map里删除该key。
请注意在 BiFunction 运行中,一个或多个 map entries 可能被上锁。因此你的 BiFunction 不应该运行太长,也不该更改任何其他的entries。否则死锁就会产生。
merge() 方法
merge() 方法允许你把一对键值合并入 map 中。如果 key 不存在,则插入指定的值。如果 key 存在,那么最后一个 lambda 方法的运行结果将成为新的值。该方法接收三个参数:
- key
- value
- 用 lambda 表达式表示的 BiFunction 接口实现。
ConcurrentLinkedDeque类
removeIf() 方法
该方法接收一个返回值为 true 或 false 的 Predicate 接口的实现作为参数,每一个元素将调用参数里的方法,如果参数里的方法对一个元素的运行结果为 true,那么该元素将被删除。例如:
// 删除salesrank这个属性值高于 1000 的所有的产品 System.out.println("Products: "+productList.size()); productList.removeIf(product -> product.getSalesrank() > 1000); System.out.println("Products; "+productList.size()); productList.forEach(product -> { System.out.println(product.getTitle()+": "+product.getSalesrank()); });
Atomic 变量
Atomic 变量提供了针对 integer, long, boolean, reference 和 Array 对象的原子操作。他们提供了一些方法来 增加值,减少值,设值,返回值或是如果当前值和预定义的值相等时则设置新值。
Java 8 提供了四个新的类。分别是 DoubleAccumulator, DoubleAdder, LongAccumulator 和 LongAdder。LongAdder 提供了和 AtomicLong 类相似的功能,但是当需要从不同线程频繁更改这个数却只需要在操作最后访问该值时,LongAdder 具有更好的性能。
LongAccumulator 和 DoubleAccumulator 类非常相似,它们的构造函数都接收两个参数:
- 内部计数器的初始值
- 用来累加新值的方法
LongAccumulator accumulator=new LongAccumulator((x,y) -> x*y, 1); IntStream.range(1, 10).parallel().forEach(x -> accumulator.accumulate(x)); System.out.println(accumulator.get());
这里总结一下几个用作同步的类
公共类供以下例子使用
public class CommonTask { public static void doTask() { long duration = ThreadLocalRandom.current().nextLong(10); System.out.printf("%s-%s: Working %d seconds\n", new Date(), Thread.currentThread().getName(), duration); try { TimeUnit.SECONDS.sleep(duration); } catch (InterruptedException e) { e.printStackTrace(); } } }
Lock 接口
public class LockTask implements Runnable { private static ReentrantLock lock = new ReentrantLock(); private String name; public LockTask(String name) { this.name = name; } @Override public void run() { try { lock.lock(); System.out.println("Task: " + name + "; Date: " + new Date() + ": Running the task"); CommonTask.doTask(); System.out.println("Task: " + name + "; Date: " + new Date() + ": The execution has finished"); } finally { lock.unlock(); } } } public class LockMain { public static void main(String[] args) { ThreadPoolExecutor executor=(ThreadPoolExecutor) Executors.newCachedThreadPool(); for (int i=0; i<10; i++) { executor.execute(new LockTask("Task "+i)); } executor.shutdown(); try { executor.awaitTermination(1, TimeUnit.DAYS); } catch (InterruptedException e) { e.printStackTrace(); } } }
Semaphore类
public class SemaphoreTask implements Runnable{ private Semaphore semaphore; public SemaphoreTask(Semaphore semaphore) { this.semaphore=semaphore; } @Override public void run() { try { semaphore.acquire(); CommonTask.doTask(); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } } } public static void main(String[] args) { Semaphore semaphore=new Semaphore(2); ThreadPoolExecutor executor=(ThreadPoolExecutor) Executors.newCachedThreadPool(); for (int i=0; i<10; i++) { executor.execute(new SemaphoreTask(semaphore)); } executor.shutdown(); try { executor.awaitTermination(1, TimeUnit.DAYS); } catch (InterruptedException e) { e.printStackTrace(); } }
CountDownLatch 类
public class CountDownTask implements Runnable { private CountDownLatch countDownLatch; public CountDownTask(CountDownLatch countDownLatch) { this.countDownLatch=countDownLatch; } @Override public void run() { CommonTask.doTask(); countDownLatch.countDown(); } } public static void main(String[] args) { CountDownLatch countDownLatch=new CountDownLatch(10); ThreadPoolExecutor executor=(ThreadPoolExecutor) Executors.newCachedThreadPool(); System.out.println("Main: Launching tasks"); for (int i=0; i<10; i++) { executor.execute(new CountDownTask(countDownLatch)); } try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } executor.shutdown(); }
CyclicBarrier 类
public class BarrierTask implements Runnable { private CyclicBarrier barrier; public BarrierTask(CyclicBarrier barrier) { this.barrier=barrier; } @Override public void run() { System.out.println(Thread.currentThread().getName()+":Phase 1"); CommonTask.doTask(); try { barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+":Phase 2"); } } public class FinishBarrierTask implements Runnable { @Override public void run() { System.out.println("FinishBarrierTask: All the tasks have finished"); } } public static void main(String[] args) { CyclicBarrier barrier=new CyclicBarrier(10,new FinishBarrierTask()); ThreadPoolExecutor executor=(ThreadPoolExecutor) Executors.newCachedThreadPool(); for (int i=0; i<10; i++) { executor.execute(new BarrierTask(barrier)); } executor.shutdown(); try { executor.awaitTermination(1, TimeUnit.DAYS); } catch (InterruptedException e) { e.printStackTrace(); } }
CompletableFuture 类
CompletableFuture允许你实现和任务相关的事件驱动模型。CompletableFuture代表了一个异步计算的结果,但是其结果可以被任何一个线程所创建。可以通过它的 complete() 方法在当计算顺利完成时创建运算结果,或使用 completeExceptionally() 方法当计算抛出异常时。如果有两个线程调用同一个 CompletableFuture 的 complete() 或 completeExceptionally(),只有第一个调用有效。
你可以使用构造函数创建一个 CompletableFuture,然后使用 complete() 方法获取最终结果。你也可以使用 runAsync() 或 supplyAsync() 方法创建 CompletableFuture。runAsync() 方法运行一个 Runnable 对象并返回 CompletableFuture<Void> 所以你的运行结果不能有返回值。supplyAsync() 方法运行一个 Supplier接口实现。它可以有返回值。CompletableFuture类提供了很多方法允许你组织任务的运行顺序,这些方法包括:
- thenApplyAsync():该方法接收一个用 lambda 函数表达的 Function 接口实现作为参数,参数中的 lambda 函数在 CompletableFuture 调用结束后运行。该方法返回另一个 CompletableFuture 用以获取函数的运行结果
- thenComposeAsync():该方法和 thenApplyAsync() 方法类似。但是在参数中的 lambda 表达式返回的值也是 CompletableFuture 时非常有用。
// 假设有如下两个方法 getUserInfo 和 getUserRating // 它们都返回一个 CompletableFuture public CompletableFuture<UserInfo> userInfo = getUserInfo(userId) public CompletableFuture<UserRating> getUserRating(UserInfo) // 如果使用thenApplyAsync,返回结果是这样 CompletableFuture<CompletableFuture<UserRating>> f = userInfo.thenApplyAsync(this::getUserRating); // 但是如果使用 thenComposeAsync,返回结果则简单很多 // thenComposeAsync 有点像flatMap() 方法,能够把元素展开来 CompletableFuture<UserRating> relevanceFuture = userInfo.thenComposeAsync(this::getUserRating);
- thenAcceptAsync():该方法的参数是一个用 lambda 函数表达的 Consumer 接口实现,因此它没有返回值
- thenRunAsync():该方法和上一个类似,但是参数里函数是一个 Runnable 对象
- thenCombineAsync(): 该方法接收两个参数,第一个参数为另一个 CompletableFuture 实例,第二个参数为一个 BiFunction 接口实现。当两个 CompletableFuture 运行结束后,BiFunction 会被执行。该方法返回一个 CompletableFuture 用来获取 BiFunction 的运行结果
- runAfterBothAsync():和上一个方法类似,但是第二个参数是一个 Runnable 对象,该 Runnable 在两个 CompletableFuture 运行结束后被调用
- runAfterEitherAsync():该方法和上一个方法类似,但是第二个参数 Runnable 任务在两个 CompletableFuture 的其中一个运行结束后被调用
- allOf():该方法接收多个 CompletableFuture 对象。当所有的CompletableFuture对象运行结束后它返回一个 CompletableFuture<Void> 对象
- anyOf():该方法接收多个 CompletableFuture 对象。当任何一个 CompletableFuture 对象运行结束后它返回运行结果的 CompletableFuture 对象
最后你通过 get() 或 join() 方法获取 CompletableFuture 的运行结果。两个方法都阻塞了调用线程直到 CompletableFuture 运行结束并返回结果。两个方法的不同之处在于 get() 方法抛出 ExecutionException (checked exception), join() 方法则抛出RuntimeException (unchecked exception)。因此在不抛异常的 lambdas (如 Supplier, Consumer 或 Runnable 里) 使用 join() 更为方便。
那些方法名以 Async 结尾的意味着这些方法将使用 ForkJoinPool.commonPool 实例并行运行。方法名没有 Async 的则使用串行方法运行。
以下是如何使用CompletableFuture的代码示例:
public class LoadTask implements Supplier<List<Product>> { private Path path; public LoadTask (Path path) { this.path = path; } @Override public List<Product> get() { List<Product> productList=null; try { productList = Files.walk(path, FileVisitOption.FOLLOW_LINKS) .parallel() .filter(f -> f.toString().endsWith(".txt")) .map(ProductLoader::load).collect (Collectors.toList()); } catch (IOException e) { e.printStackTrace(); } return productList; } } public class SearchTask implements Function<List<Product>, List<Product>> { private String query; public SearchTask(String query) { this.query=query; } @Override public List<Product> apply(List<Product> products) { System.out.println(new Date()+": CompletableTask: start"); List<Product> ret = products.stream() .filter(product -> product.getTitle() .toLowerCase().contains(query)) .collect(Collectors.toList()); System.out.println(new Date()+": CompletableTask: end:"+ret.size()); return ret; } } public class WriteTask implements Consumer<List<Product>> { @Override public void accept(List<Product> products) { // implementation is omitted } } public class CompletableMain { public static void main(String[] args) { Path file = Paths.get("data","category"); System.out.println(new Date() + ": Main: Loading products"); LoadTask loadTask = new LoadTask(file); CompletableFuture<List<Product>> loadFuture = CompletableFuture.supplyAsync(loadTask); System.out.println(new Date() + ": Main: Then apply for search"); CompletableFuture<List<Product>> completableSearch = loadFuture.thenApplyAsync(new SearchTask("love")); CompletableFuture<Void> completableWrite = completableSearch.thenAcceptAsync(new WriteTask()); completableWrite.exceptionally(ex -> { System.out.println(new Date() + ": Main: Exception " + ex.getMessage()); return null; }); System.out.println(new Date() + ": Main: Then apply for users"); CompletableFuture<List<String>> completableUsers = loadFuture.thenApplyAsync(resultList -> { System.out.println(new Date() + ": Main: Completable users: start"); List<String> users = resultList.stream() .flatMap(p -> p.getReviews().stream()) .map(review -> review.getUser()) .distinct() .collect(Collectors.toList()); System.out.println(new Date() + ": Main: Completable users: end"); return users; }); System.out.println(new Date() + ": Main: Then apply for best rated product...."); CompletableFuture<Product> completableProduct = loadFuture .thenApplyAsync(resultList -> { Product maxProduct = null; double maxScore = 0.0; System.out.println(new Date() + ": Main: Completable product: start"); for (Product product : resultList) { if (!product.getReviews().isEmpty()) { double score = product.getReviews().stream() .mapToDouble(review -> review.getValue()) .average().getAsDouble(); if (score > maxScore) { maxProduct = product; maxScore = score; } } } System.out.println(new Date() + ": Main: Completable product: end"); return maxProduct; }); System.out.println(new Date() + ": Main: Then apply for best selling product...."); CompletableFuture<Product> completableBestSellingProduct = loadFuture .thenApplyAsync(resultList -> { System.out.println(new Date() + ": Main: Completable best selling: start"); Product bestProduct = resultList .stream() .min(Comparator.comparingLong (Product::getSalesrank)) .orElse(null); System.out.println(new Date() + ": Main: Completable best selling: end"); return bestProduct; }); CompletableFuture<String> completableProductResult = completableBestSellingProduct .thenCombineAsync( completableProduct, (bestSellingProduct, bestRatedProduct) -> { System.out.println(new Date() + ": Main: Completable product result: start"); String ret = "The best selling product is " + bestSellingProduct.getTitle() + "\n"; ret += "The best rated product is " + bestRatedProduct.getTitle(); System.out.println(new Date() + ": Main: Completable product result: end"); return ret; }); System.out.println(new Date() + ": Main: Waiting for results"); CompletableFuture<Void> finalCompletableFuture = CompletableFuture .allOf(completableProductResult, completableUsers, completableWrite); finalCompletableFuture.join(); try { System.out.println("Number of loaded products: " + loadFuture.get().size()); System.out.println("Number of found products: " + completableSearch.get().size()); System.out.println("Number of users: " + completableUsers.get().size()); System.out.println("Best rated product: " + completableProduct.get().getTitle()); System.out.println("Best selling product: " + completableBestSellingProduct.get().getTitle()); System.out.println("Product result: "+completableProductResult.get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } }
相关推荐
例如,图9-1展示的并行接口有独立的信号线来管理数据的输入和输出,同时有状态寄存器供CPU查询接口的工作状态。 数据的输入和输出过程涉及多个步骤和信号交换。对于数据输入,外设通过“数据输入准备好”信号通知...
线程化、同步和通信 第三篇 系统体系结构 第8章 对称多处理机和CC-NUMA多处理机 第9章 机群仑和可用性支持 第10章 服务器和工作站机群 第11章 MPP的体系结构和性能 第四篇 并行编程 ...
特点是需要把并行数据变成串行数据发送到线路上去,接收时,要把串行信号变成并行数据。 §9-2 串行通信的同步方式与异步方式 * 异步串行通信:数据以字符为单位传送,在每个字符数据的传送过程中都要加进一些识别...
线程化、同步和通信 第三篇 系统体系结构 第8章 对称多处理机和CC-NUMA多处理机 第9章 机群仑和可用性支持 第10章 服务器和工作站机群 第11章 MPP的体系结构和性能 第四篇 并行编程 ...
6. **第9章 - 查找** - 静态查找表:数据不随查找过程改变的查找表,如哈希表和有序数组。 - 动态查找表:数据可以在查找过程中动态添加或删除,如二分查找树和B树。 这些课件涵盖了数据结构的关键概念,从基本的...
"软件工程第九章-面向对象方法学引论" 面向对象方法学是软件工程中一种重要的方法学,用于开发大型软件产品。它源于20世纪60年代后期出现的面向对象编程语言Simula-67,逐步形成了面向对象方法学。到了20世纪90年代...
课程内容主要涵盖绪论和MCS-51单片机结构的第1章到第9章。 MCS-51单片机的内部结构主要包括CPU、内存(ROM和RAM)、定时/计数器、I/O口以及内中断处理系统。其中,CPU是核心,由运算器和控制器构成,能进行算术运算...
CAD技术基础-第9章-协同、管理与集成 本章节主要介绍了CAD技术基础中的协同、管理与集成技术。协同技术是指在产品设计、制造和使用过程中,多个部门、多个专业人员之间的信息共享、交流和协调处理,旨在提高产品...
可能涵盖的问题包括编写并行排序算法(如快速排序、归并排序)、并行搜索算法或实现并行数据结构。 压缩文件中的"e9e2d7931a4242c19f268baa7f961303"可能是一个哈希值或文件名,表示包含具体课程材料的文件。这个...
9. pthread_join 的第二个参数的作用是获取指定线程函数返回结果。 10. 在分布式内存架构编程中,进程间不能通过读写变量交换数据。 11. 关于 OpenMP 循环并行程序的编写,程序员只需指出对哪个循环进行并行,循环...
第9章 - 基于对象的数据库 - **知识点**: - 对象-关系映射(ORM)技术。 - 对象数据库管理系统的优点和局限性。 - 面向对象的数据模型设计。 #### 10. 第10章 - XML - **知识点**: - XML文档的基本结构和...
【计算机组成原理第八章-第3讲-程序中断方式】主要探讨的是计算机处理突发事件和实现多任务并行处理的关键机制——中断方式。中断是计算机硬件和操作系统之间的一种通信方式,它使得CPU可以在执行正常程序的同时,...
Spark生态系统包括了多个组件,例如Spark SQL用于处理结构化数据,Spark Streaming用于实时数据流处理,MLlib用于机器学习,GraphX用于图计算等。Spark可以在多种运行模式下工作,比如可以作为独立集群运行,也可以...
异步通信是一种基于帧结构的数据传输方式,每一帧包含起始位、数据位、校验位和停止位。这种通信方式不要求发送端和接收端时钟同步,因此非常适合于随机的、非连续的数据发送和接收。而同步通信则需要通过同步字符来...
串行口的内部结构包括发送控制器、接收控制器、输入移位寄存器、定时器以及串行控制寄存器等组件,其中SBUF作为串行缓冲器,用于数据的存储和传输。 在实际应用中,比如两台单片机a和b之间的通信,a机通过P3.1(TXD)...
##### 第9章:异步编程模型 - **异步编程基础**:介绍异步编程的基本概念,包括异步方法的调用机制。 - **任务和异步等待**:讲解如何使用async/await关键字来简化异步编程。 - **异步流**:探讨如何使用异步流来...
《数字电子技术基础》第五章主要探讨了时序逻辑电路,这是数字系统设计中的关键组成部分。时序逻辑电路能够记忆信息,并根据当前状态和输入产生输出。本章分为概述和时序逻辑电路的分析两大部分。 一、时序逻辑电路...
第一个分太高了要50,过分,通过阅读和学习,读者可以掌握基于多种平台(多核、多处理器、集群和GPU等),利用多项技术(Matlab并行计算工具箱、多线程MEX文件、OpenMP...第9章为在Matlab中应用OpenMP进行并行计算;第10章