前言:搜索是很多软件不可或缺的功能。对于有序的数组,我们可以采用常见的二分查找法,对于无序的数据,只能是挨个查找。
如果说在线程充足的情况,可以考虑使用多线程思路去解决搜索问题,即并发搜索。
思路:将原来的数组按照线程数进行分割,当有两个线程搜索元素时,可以将数组一分为二,让每个线程在指定的角标范围内搜索元素,当其中有一个线程搜索到元素则,立即将结果返回。
代码如下:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
public class Demo8 {
static ExecutorService pool = Executors.newCachedThreadPool();
static final int Thread_Num = 2;
//假设数组的值都大于或等于0,当出现小于0的情况,可以设置一个无穷小的值或新增一个标志
static AtomicInteger result = new AtomicInteger(-1);
public static int pSearch(int[] arr, int searchValue) throws Exception {
int subArrSize = arr.length / Thread_Num + 1;
List<Future<Integer>> re = new ArrayList<Future<Integer>>();
for (int i = 0, len = arr.length; i < len; i += subArrSize) {
//分割数组操作
int end = i + subArrSize;
if (end >= len) {
end = len;
}
re.add(pool.submit(new SearchTask(arr, searchValue, i, end)));
}
for (Future<Integer> fu : re) {
//Future接口的get()方法具有阻塞效果,当结果还没返回时则一直阻塞,直到返回结果
if (fu.get() > 0) return fu.get();
}
return -1;
}
/**
* 查询的方法
* @param arr 指定搜索的数组
* @param searchValue 搜索值
* @param beginPos 开始索引
* @param endPos 结束索引
* @return 所在角标
*/
public static int search(int[] arr, int searchValue, int beginPos, int endPos) {
int i = 0;
for (i = beginPos; i < endPos; i++) {
//当大于0所以值已经被替换,直接返回结果
if (result.get() > 0) {
return result.get();
}
if (arr[i] == searchValue) {
//使用CAS原理,当更新失败则说明值已经被修改过,直接返回结果
if (!result.compareAndSet(-1, searchValue)) {
return result.get();
}
return i;
}
}
return -1;
}
/**
* 自定义一个任务类,以便将数据封装到任务中
*/
public static class SearchTask implements Callable<Integer> {
int begin, end, searchValue;
int[] arr;
public SearchTask(int[] arr, int searchValue, int begin, int end) {
this.begin = begin;
this.end = end;
this.searchValue = searchValue;
this.arr = arr;
}
@Override
public Integer call() throws Exception {
return search(arr, searchValue, begin, end);
}
}
}
知识点补充:
1、CAS原理: 操作的时包含三个操作数 — 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。
2、AtomicInteger相比于是具有原子性的Integer,它的compareAndSet方法采用的是CAS原理,所以第一次会设置成功,第二次起便会失败。
3、Future模式可以异步获取接口的数据,Future.get()当数据未完成时会阻塞,直到数据获取完成,或是设置超时时间。
参考自:《实战Java高并发程序设计》