私は小さなGroovyプロジェクトをやろうとしていて、ConcurrentLinkedHashSetが欲しかったのですが、Javaは提供していません。そこで、通常の LinkedHashSet を「保護」するために、Gpars エージェントを使用して独自の作成に着手しました。次に、エージェントを保持するラッパー クラスを作成し、クラスのメソッドをこのように内部エージェントに委任しました (このバージョンでは、委任アプローチとして methodMissing を使用しています)。Groovyのinterceptable/invokeMethodを試してみましたが、どちらかで動作させることができました
class ConcurrentLinkedHashSet /*implements GroovyInterceptable*/ {
Agent $concurrentHashSet = new Agent (new LinkedHashSet())
/*Object[] toArray () {
$concurrentHashSet.val.asType(LinkedHashSet).toArray()
}*/
def asType (Set) {
$concurrentHashSet.val
}
//set up delegation action to pass all methods to the agent to execute
def /*invokeMethod*/methodMissing (String name, args){
def result
System.out.println "methodMissing with $name and $args on $this"
System.out.println "agent value is : ${$concurrentHashSet.val.dump()} is instance of: ${$concurrentHashSet.getClass()}"
$concurrentHashSet {
System.out.println "\t\tconcHashSet methodMissing: it is of type ${it.getClass()} so called $it.invokemethod ($name, $args) "
if (args == []) {
System.out.println "\t\tconcHashSet methodMissing: invoke method '$name' with no args "
result = it."$name"()//it.invokeMethod (name)
} else {
System.out.println "\t\tconcHashSet methodMissing: invoke method '$name' with args $args"
result = it.invokeMethod(name, *args)//"$name" (*args)//it.invokeMethod(name, args)
}
System.out.println "\tconcHashSet methodMissing: 'it' is now showing as > '$it'"
"success"
}
//System.out.println "agent protected value is now : " + $concurrentHashSet.val + " and result now $result"
System.out.println "agent protected value is now : " + $concurrentHashSet.val.dump() + " and result now $result"
$concurrentHashSet.val
}
}
ただし、これを使用しようとすると、最初は機能し、文字列が追加されますが、同じ欠落しているメソッドでの 2 回目の呼び出しでは、agent.send 呼び出しは行われず、スキップされます。
したがって、私の単純なスクリプトのコンシューマは次のようになります
// delegates add method via agent.send first time through but not after !
ConcurrentLinkedHashSet conhs = new ConcurrentLinkedHashSet ()
conhs.add ('col1')
println "1st conHashSet as list : ${conhs.toArray()}"
conhs.add ('col2')
println "2nd conHashSet as list : ${conhs.toArray()}"
// direct calls on an agent using invokeMethod
Agent myHash = new Agent (new LinkedHashSet())
myHash {it.invokeMethod ('add', 'col1')}
println "1st agentHashSet as list : ${myHash.val.toArray()}"
myHash {it.invokeMethod ('add','col2')}
println "2nd agentHashSet as list : ${myHash.val.toArray()}"
私の単純なトレースログは、コンソール出力で次のようになります
methodMissing with add and [col1] on org2.softwood.rules.utils.ConcurrentLinkedHashSet@3b0090a4
agent value is : <java.util.LinkedHashSet@0 map=[:]> is instance of: class groovyx.gpars.agent.Agent
concHashSet methodMissing: it is of type class java.util.LinkedHashSet so called [] (add, [col1])
concHashSet methodMissing: invoke method 'add' with args [col1]
concHashSet methodMissing: 'it' is now showing as > '[col1]'
agent protected value is now : <java.util.LinkedHashSet@2eaeb1 map=[col1:java.lang.Object@6a2f6f80]> and result now true
methodMissing with toArray and [] on org2.softwood.rules.utils.ConcurrentLinkedHashSet@3b0090a4
agent value is : <java.util.LinkedHashSet@2eaeb1 map=[col1:java.lang.Object@6a2f6f80]> is instance of: class groovyx.gpars.agent.Agent
agent protected value is now : <java.util.LinkedHashSet@2eaeb1 map=[col1:java.lang.Object@6a2f6f80]> and result now null
1st conHashSet as list : [col1]
methodMissing with add and [col2] on org2.softwood.rules.utils.ConcurrentLinkedHashSet@3b0090a4
agent value is : <java.util.LinkedHashSet@2eaeb1 map=[col1:java.lang.Object@6a2f6f80]> is instance of: class groovyx.gpars.agent.Agent
agent protected value is now : <java.util.LinkedHashSet@2eaeb1 map=[col1:java.lang.Object@6a2f6f80]> and result now null
methodMissing with toArray and [] on org2.softwood.rules.utils.ConcurrentLinkedHashSet@3b0090a4
agent value is : <java.util.LinkedHashSet@2eaeb1 map=[col1:java.lang.Object@6a2f6f80]> is instance of: class groovyx.gpars.agent.Agent
agent protected value is now : <java.util.LinkedHashSet@2eaeb1 map=[col1:java.lang.Object@6a2f6f80]> and result now null
2nd conHashSet as list : [col1]
1st agentHashSet as list : [col1]
2nd agentHashSet as list : [col1, col2]
委譲の最初の試行でわかるように、「concHashSet methodMissing:」トレースを確認でき、エージェントは要素の追加を実行するために、エージェント内でそれに対して invokeMethod を呼び出します。
conhs.add ('col2') への 2 回目の呼び出しでは、agent.sand 呼び出しは発生しないため、余分なアイテムが追加されることはありません。
ConcurrentLinkedHashSet を作成する簡単な方法があると思っていたので、これは面倒ですが、コードは機能しません。適切な結果を得るためにどのメカニズムを使用できますか?
ご覧のとおり、invokeMethod (追加) を直接実行すると、Agent<LinkedHashSet>
問題なく動作します。私の実際の消費クラスでは、ConcurrentLinkedHashSet を通常の LinkedHashSet に置き換えると夢のように動作しますが、スレッドセーフではありません。これを機能させることに依存するスレッドセーフバージョンを作成したかったのです。
エージェントを置き換えて、LinkedHashSet の周りで同期ブロックを使用することもできると思いますが、少し醜いです。Gpars エージェントは、委任を伴うラッパーとしての一般的なソリューション パターンとして、これらすべてを整理してくれると思いました。
PS私は別のタックを試してみましたが、この種の作品は私が思うにうまくいきません.GroovyInterceptableを実装するクラスのinvokeMethodで@Synchroniseを使用して、委譲時にスレッドセーフな呼び出しを実現します。これが本当にスレッドセーフかどうかはわかりません。
class ConcurrentLinkedHashSet2 implements GroovyInterceptable{
@Delegate private LinkedHashSet $mySet = new LinkedHashSet()
@Synchronized
def invokeMethod (String name, args) {
System.out.println "call to $name intercepted invoke using metaclass invoke"
ConcurrentLinkedHashSet2.metaClass.getMetaMethod(name).invoke (this, *args)
}
}