我有一个将多个元素插入BST的任务。必须通过使用多个线程来优化任务。可以启动多少个线程没有限制。
这是我的方法。这是一种理论方法。我没有尝试过它,也不知道它的工作程度。请提出您对此想法的意见。
BST节点看起来像这样:
class BSTNode {
int val;
BSTNode left, right;
boolean leftLock, rightLock;
Queue<BSTNode> leftQ, rightQ;
}
我没有使用任何Java锁,而是使用两个布尔变量来表示锁定状态。
由于插入任何元素首先要求我们找到相关位置,因此可以并行执行此任务,因为这不会修改树。该任务只能在路径上的节点解锁之前工作。如果某个特定节点被锁定,那么该特定插入线程将被放入sleep()
并添加到相应的左或右队列,并在释放锁定时再次唤醒。
另一方面,如果路径上没有节点锁定它们,我们可以继续进行插入。在插入和修改父对应的指针之前,必须在父对象上获取锁。
有人可以就这种实施方法提出自己的看法吗?
答案 0 :(得分:1)
这实际上是尝试实现自己的锁定的练习。你所做的是创建一个解包锁(boolean,waitingQueue)。但是,这种方法安全工作的唯一方法是外部同步访问'lock'布尔变量和队列。因此,要使此非锁定代码成功运行,您必须使用锁定。
如果你没有使用锁,你会遇到几个与并发有关的问题:
如果您想要一个具有线性搜索时间的线程安全,有序,可变容器,请使用ConcurrentSkipListSet&lt; Integer&gt;
答案 1 :(得分:1)
一个有趣的问题。还有一些要点,你必须重新定义你的方法。
由于任何元素的插入首先要求我们找到相关的元素 这个任务可以并行执行
finding a suitable place + actual insertion
。原因是,在一个线程(比如t1)完成finding a suitable place
然后尝试actual insertion
的情况下,但是必须坚持,因为另一个线程(比如说t2)正在执行{{1}然后第一个线程(t1)将不得不再次进行位置计算,因为树在第二个线程(t2)actual insertion
之后发生了变化。 (我想你得到我说的话)总而言之,二进制搜索树 的并行插入不会使 受益,因为二进制搜索树插入不能独立于另一个插入执行。
答案 2 :(得分:1)
我试图解释一个问题,这个问题显示了这种方法的基本漏洞。
假设您现在仅支持插入操作,而不支持任何其他操作。以下可能是插入操作的实现:
//Using C
BSTNode* insert(BSTNode* root,int value)
{
1 if(root == NULL){
2 return createNewNode(value);
3 }
4
5 if(root->data == value){
6 return root;
7 }
8 else if(root->data > value){
9 while(root->leftLock);
10 if(!root->left){
11 root->leftLock = true;
12 root->left = insert(root->left,value);
13 root->leftLock = false;
14 }
15 else{
16 root->left = insert(root->left,value);
17 }
18 }
19 else{
20 while(root->rightLock);
21 if(!root->right){
22 root->rightLock = true;
23 root->right = insert(root->right,value);
24 root->rightLock = false;
25 }
26 else{
27 root->right = insert(root->right,value);
28 }
29 }
30
31 return root;
32
}
在这种方法中,由于只有最后一个节点(叶节点)的子节点在插入值时才会更新,因此我们在更新父节点时(重复返回时)不会进行任何锁定。
我正在避免插入请求排队和仅使用自旋锁来保持它有点简单。不过,我要提出的观点也是如此......
考虑一下这个BST:
10
/ \
5 15
/ \ / \
2 6 13 20
假设同时调用2个线程t1和t2,试图分别插入值25和26,并且当前位于BSTNode,值为20。 (最右边的节点)。
现在让我们通过线程之间的上下文切换来执行上面的代码:
a. t1:
1. if(root == NULL) //not true, will go to line 5.
//switch
b. t2:
1. if(root == NULL) //not true, will go to line 5.
//switch
c. t1:
5. if(root->data == value){ //not true, will go to line 8.
8. else if(root->data > value) //not true, will go to line 19.
//switch
d. t2:
5. if(root->data == value){ //not true, will go to line 8.
//switch
e. t1:
19 else{
20 while(root->rightLock); // lock is not held by anyone, so continue.
21 if(!root->right){
//switch
f. t2:
8. else if(root->data > value) //not true, will go to line 19.
19 else{
20 while(root->rightLock); // lock is not helpd by anyone, so continue.
21 if(!root->right){
22 root->rightLock = true;
//switch
g. t1:
22 root->rightLock = true;
23 root->right = insert(root->right,value);
//switch
h. t2:
23 root->right = insert(root->right,value);
24 root->rightLock = false;
//switch
假设第23行涵盖该行的完整执行。
正如您在 f , g 和 h 一节中所见, t1 和 t2 正在进入关键部分而不知道彼此的存在。代码不应该允许这样做。
问题是什么?
问题是有一段代码应该一次性执行:
20 while(root->rightLock);
21 if(!root->right){
22 root->rightLock = true;
所以我们可能需要一些硬件控制来制作我们自己的不间断指令,它一起执行上面提到的所有3个任务。