我认为问题本质上是关于让线程等待并重试的有效方法。
听听变化怎么样
现有授权间隔数组
仅在更改时重试?
以下不应该被认为是正确的实现(我对线程的经验非常有限),而是对建议机制的演示:
Ranges.java 和 Range.java
//represents all ranges
//see also: https://stackoverflow.com/a/7721388/3992939
public class Ranges {
private List<Range> ranges = new ArrayList<>();
private PropertyChangeSupport rangeChangedProperty = new PropertyChangeSupport(this);
public Range getRange(int rangeStart, int rangeEnd) {
if(contains(rangeStart) || contains(rangeEnd)) {
return null;
}
Range range = new Range(rangeStart, rangeEnd);
range.addListener( (observable, oldValue, newValue) -> {
rangeChangedProperty.firePropertyChange("Range", "-" , "changed");
}
);
ranges.add(range);
return range;
}
private boolean contains(int number){
for(Range range : ranges) {
if(range.contains(number)) {return true;}
}
return false;
}
public boolean removeRange(Range range) {
boolean isContains = ranges.remove(range);
rangeChangedProperty.firePropertyChange("Range", "-" , "removed");
return isContains;
}
/**
* Listen to {@link #rangeChangedProperty}. Fires whenever a range changes
* or removed.
* <br/>A client and a listener and when it fires, notify all threads.
*/
public void addChangeListener(PropertyChangeListener listener) {
rangeChangedProperty.addPropertyChangeListener(listener);
}
//represents a single range
//It is muttable
//can be implemented using ValueRange (https://stackoverflow.com/a/40716042/3992939)
class Range{
private SimpleIntegerProperty low = new SimpleIntegerProperty();
private SimpleIntegerProperty high = new SimpleIntegerProperty();
private SimpleObjectProperty<int[]> rangeProperty = new SimpleObjectProperty<>();
private Range(int rangeStart, int rangeEnd){
low.set(rangeStart) ; high.set(rangeEnd);
updateRange();
low.addListener((observable, oldValue, newValue) -> { updateRange(); });
high.addListener((observable, oldValue, newValue) -> { updateRange(); });
}
/**
* Listen to {@link #rangeProperty} that changes whenever the range changes
*/
void addListener(ChangeListener<int[]> listener) {
rangeProperty.addListener(listener);
}
private void updateRange() {rangeProperty.set(new int[] {low.get(), high.get()});}
public int getRangeStart() { return low.get(); }
public void setRangeStart(int rangeStart) { low.set(rangeStart);}
public int getRangeEnd() {return high.get();}
public void setRangeEnd(int rangeEnd) { high.set(rangeEnd);}
public boolean contains(int number){
int min = Math.min(low.get(), high.get());
int max = Math.max(low.get(), high.get());
return ((number >= min) && (number <= max));
}
}
}
GetRange.java
//used to simulate a thread trying to get a range
public class GetRange implements Runnable{
private Ranges ranges;
private int low, high;
private String id;
GetRange(Ranges ranges, int low, int high, String id) {
this.ranges = ranges;
this.low = low; this.high = high; this.id = id;
}
@Override
public void run() {
synchronized (ranges) {
while(ranges.getRange(low,high) == null) {
System.out.println("Tread "+ id + " is waiting");
try {
ranges.wait();
} catch (InterruptedException ex) { ex.printStackTrace();}
}
}
System.out.println("Tread "+ id + " got range. All done");
}
}
测试是:
//test
public static void main(String[] args) throws InterruptedException {
Ranges ranges = new Ranges();
ranges.addChangeListener( (evt) -> {
synchronized (ranges) {
ranges.notifyAll();
System.out.println(evt.getPropertyName() + " "+ evt.getNewValue());
}
});
Range range1 = ranges.getRange(10,15);
Range range2 = ranges.getRange(20,25);
new Thread(new GetRange(ranges, 10, 12, "A")).start();
new Thread(new GetRange(ranges, 21, 28, "B")).start();
new Thread(new GetRange(ranges, 10, 12, "C")).start();
Thread.sleep(50);
System.out.println("-- Changing end of range 1. Threads notifyied and keep waiting -----");
range1.setRangeEnd(16); //no thread effected
Thread.sleep(50);
System.out.println("-- Changing start of range 1. Threads notifyied and A or C get range -----");
range1.setRangeStart(13); //effects thread A or C
Thread.sleep(50);
System.out.println("-- Removing range 2. Threads notifyied and B get range -----");
ranges.removeRange(range2);//effects thread B
Thread.sleep(50);
System.exit(1);
}
输出:
胎面 A 正在等待
胎面 C 正在等待
胎面 B 正在等待
-- 更改范围的结束 1. 通知线程并保持等待 -----
范围已更改
胎面 B 正在等待
胎面 C 正在等待
胎面 A 正在等待
-- 更改范围的开始 1. 线程通知和 A 或 C 获取范围 -----
范围更改 Tread A 获取范围。全部完成
线程 C 正在等待
胎面 B 正在等待
-- 删除范围 2. 线程通知和 B 获取范围 -----
范围已删除
胎面 B 有范围。全部完成
胎面 C 正在等待