0

私は本当に苦痛な Java 学習曲線を経験しましたが、まだ初心者です。

低レベルのライブラリ クラスを設計しています。将来的には、マルチスレッド環境またはシングル スレッドで使用できるようになる可能性があります。私にはそれがわかりません。

このクラスのユーザーに外部からの同期を許可できます。しかし、それはスレッド セーフ バージョンを提供するよりもはるかに非効率的です。

これがpコードです。

Class Example{
    public int checkAndProcess(){
        WriteLock writeLock=this.getWriteLock();
        ReadLock readLock=new ReadLock(writeLock);
        int a;
        try{
            lockManager.lock(readLock);
            a=readSomething();
        }finally{
            lockManager.release(readLock);
        }
        if(a!=null){
            return a;
        }
        try{
            lockManager.lock(writeLock);
            a=doSomeProcessing();
        }finally{
            lockManager.release(writeLock);
        }
        return a;
    }
}

リードロックがブロックされないため、外部から同期するよりもはるかに高速になります。すべてのメソッド呼び出しで作成され、ガベージ コレクションされます。

問題:

オーバーヘッド。WriteLock は非常に複雑で、ReadLock は安価で単純ですが、すべてのメソッド呼び出し (および場合によっては複数回) で作成されるため、依然としてオーバーヘッドが生じます。

そのようなすべてのクラスのスレッド セーフ バージョンを提供する必要がありますか? すべてのオープンソース ライブラリがそうしているわけではありません。しかし、私がそれを提供しない場合、ユーザーが外部から同期できるようにすると、パフォーマンスが低下します。

それとももっと良い方法がありますか?

編集:

分けるべきですか?

ステートレス プロセッサとストアに分割し、ユーザーが readLock/writeLock を作成してロックできるようにしますか? そうすれば、ストアは完全にプロセッサ用に設計され、他のクラスにはあまり意味がなくなり、ライブラリはそれらによって急速にブームになります。

これが私の実際のコードです。嫌なら無視していい。

package lazycatTools.runtime;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.jobs.Job;
import org.osgi.framework.AllServiceListener;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;

public class DynamicServiceTracker {

    private final HashMap<Long,Object> _serviceCache;
    private final HashMap<String,Long> _keyCache;

    private final MultiResourceSchedulingRule _writeLock;

    private final ServiceListener _tracker;

    private final BundleContext _context;

    public DynamicServiceTracker(BundleContext context){
        Assert.isLegal(context!=null);
        _serviceCache=new HashMap<Long,Object>();
        _keyCache=new HashMap<String,Long>();
        HashSet<Object> lockResource=new HashSet<Object>(4);
        lockResource.add(_serviceCache);
        lockResource.add(_keyCache);
        _writeLock=new MultiResourceSchedulingRule<DynamicServiceTracker,Object>(this,lockResource);
        _context=context;
        _tracker=new AllServiceListener(){

            @Override
            public void serviceChanged(ServiceEvent event) {
                if(event.getType()==ServiceEvent.UNREGISTERING){
                    ServiceReference<?> ref=event.getServiceReference();
                    Long sid=(Long)ref.getProperty(Constants.SERVICE_ID);
                    String[] classes=(String[])ref.getProperty(Constants.OBJECTCLASS);
                    boolean ungetService=false;
                    try{
                        Job.getJobManager().beginRule(_writeLock, null);
                        for(String clazz : classes){
                            if(_keyCache.get(clazz)==sid){
                                _keyCache.remove(clazz);
                                break;
                            }
                        }
                        if(_serviceCache.containsKey(sid)){
                            _serviceCache.remove(sid);
                            ungetService=true;
                        }
                    }finally{
                        Job.getJobManager().endRule(_writeLock);
                    }
                    if(ungetService){
                        //The order of ungetting a serviceReference is not important
                        _context.ungetService(ref);
                    }
                    SharedSchedulingRule readLock=new SharedSchedulingRule(_writeLock);
                    try{
                        Job.getJobManager().beginRule(readLock, null);
                        if(_serviceCache.size()==0){
                            _context.removeServiceListener(_tracker);
                        }
                    }finally{
                        Job.getJobManager().endRule(readLock);
                    }
                }
            }
        };
    }

    public Object getService(String clazz) throws Exception{
        Object cachedService=null;
        Long key;
        SharedSchedulingRule readLock=new SharedSchedulingRule(_writeLock);
        try{
            Job.getJobManager().beginRule(readLock, null);
            key=_keyCache.get(clazz);
            if(key!=null){
                cachedService=_serviceCache.get(key);
            }
        }finally{
            Job.getJobManager().endRule(readLock);
        }
        if(cachedService!=null){
            return cachedService;
        }
        ServiceReference<?> ref=_context.getServiceReference(clazz);
        Long sid=(Long)ref.getProperty(Constants.SERVICE_ID);
        Object newService=_context.getService(ref);
        try{
            Job.getJobManager().beginRule(_writeLock, null);
            key=_keyCache.get(clazz);
            if(key!=null){
                cachedService=_serviceCache.get(key);
            }else{
                _keyCache.put(clazz,sid);
                _serviceCache.put(sid, newService);
            }
        }finally{
            Job.getJobManager().endRule(_writeLock);
        }

        if(cachedService!=null){
            _context.ungetService(ref);
            return cachedService;
        }else{
            _context.addServiceListener(_tracker);
            return newService;
        }
    }

    public <Type> Type getService(Class<Type> clazz){
        Object cachedService=null;
        Long key;
        SharedSchedulingRule readLock=new SharedSchedulingRule(_writeLock);
        try{
            Job.getJobManager().beginRule(readLock, null);
            key=_keyCache.get(clazz);
            if(key!=null){
                cachedService=_serviceCache.get(key);
            }
        }finally{
            Job.getJobManager().endRule(readLock);
        }
        if(cachedService!=null){
            @SuppressWarnings("unchecked")
            Type castedService=(Type)cachedService;
            return castedService;
        }
        ServiceReference<Type> ref=_context.getServiceReference(clazz);
        Long sid=(Long)ref.getProperty(Constants.SERVICE_ID);
        Type newService=_context.getService(ref);
        try{
            Job.getJobManager().beginRule(_writeLock, null);
            key=_keyCache.get(clazz);
            if(key!=null){
                cachedService=_serviceCache.get(key);
            }else{
                _keyCache.put(clazz.getName(),sid);
                _serviceCache.put(sid, newService);
            }
        }finally{
            Job.getJobManager().endRule(_writeLock);
        }
        if(cachedService!=null){
            _context.ungetService(ref);
            @SuppressWarnings("unchecked")
            Type castedService=(Type)cachedService;
            return castedService;
        }else{
            _context.addServiceListener(_tracker);
            return newService;
        }
    }

    public Object[] getServices(String clazz,String filter) throws InvalidSyntaxException{
        ServiceReference<?>[] refs=_context.getServiceReferences(clazz,filter);
        if(refs==null){
            return null;
        }
        Object[] services=new Object[refs.length];
        int count=refs.length;
        boolean[] serviceAbsence=new boolean[refs.length];
        Long[] SIDs=new Long[refs.length];
        for(int i=0;i<=count-1;i++){
            ServiceReference<?> ref=refs[i];
            SIDs[i]=(Long)ref.getProperty(Constants.SERVICE_ID);
        }
        boolean loop=true;
        SharedSchedulingRule readLock=new SharedSchedulingRule(_writeLock);
        while(loop){
            try{
                Job.getJobManager().beginRule(readLock, null);
                for(int i=0;i<=count-1;i++){
                    if(_serviceCache.containsKey(SIDs[i])==false){
                        serviceAbsence[i]=true;
                    }
                }
            }finally{
                Job.getJobManager().endRule(readLock);
            }
            for(int i=0;i<=count-1;i++){
                if(serviceAbsence[i]==true){
                    services[i]=_context.getService(refs[i]);
                }
            }
            try{
                Job.getJobManager().beginRule(_writeLock, null);
                boolean gotNewRequire=false;
                for(int i=0;i<=count-1;i++){
                    if(_serviceCache.containsKey(SIDs[i])==false && services[i]==null){
                        serviceAbsence[i]=true;
                        gotNewRequire=true;
                    }
                }
                if(gotNewRequire==false){
                    for(int i=0;i<=count-1;i++){
                        Object service=services[i];
                        if(service!=null){
                            _serviceCache.put(SIDs[i], service);
                        }else{
                            services[i]=_serviceCache.get(SIDs[i]);
                        }
                    }
                    loop=false;
                }
            }finally{
                Job.getJobManager().endRule(_writeLock);
            }
        }
        _context.addServiceListener(_tracker);
        return services;    
    }
    public <Type> Collection<Type> getServices(Class<Type> clazz,String filter) throws InvalidSyntaxException{
        Collection<ServiceReference<Type>> refsCollection=_context.getServiceReferences(clazz,filter);
        HashMap<Integer,Type> services=new HashMap<Integer,Type>(refsCollection.size()+1,1.0f);
        if(refsCollection.size()==0){
            return services.values();
        }
        ArrayList<ServiceReference<Type>> refs=new ArrayList<ServiceReference<Type>>(refsCollection);
        int count=refs.size();
        boolean[] serviceAbsence=new boolean[refs.size()];
        Long[] SIDs=new Long[refs.size()];
        for(int i=0;i<=count-1;i++){
            ServiceReference<Type> ref=refs.get(i);
            SIDs[i]=(Long)ref.getProperty(Constants.SERVICE_ID);
        }
        boolean loop=true;
        SharedSchedulingRule readLock=new SharedSchedulingRule(_writeLock);
        while(loop){
            try{
                Job.getJobManager().beginRule(readLock, null);
                for(int i=0;i<=count-1;i++){
                    if(_serviceCache.containsKey(SIDs[i])==false){
                        serviceAbsence[i]=true;
                    }
                }
            }finally{
                Job.getJobManager().endRule(readLock);
            }
            for(int i=0;i<=count-1;i++){
                if(serviceAbsence[i]==true){
                    services.put(i, _context.getService(refs.get(i)));
                }
            }
            try{
                Job.getJobManager().beginRule(_writeLock, null);
                boolean gotNewRequire=false;
                for(int i=0;i<=count-1;i++){
                    if(_serviceCache.containsKey(SIDs[i])==false && services.containsKey(i)==false){
                        serviceAbsence[i]=true;
                        gotNewRequire=true;
                    }
                }
                if(gotNewRequire==false){
                    for(int i=0;i<=count-1;i++){
                        Object service=services.get(i);
                        if(service!=null){
                            _serviceCache.put(SIDs[i], service);
                        }else{
                            @SuppressWarnings("unchecked")
                            Type cachedService=(Type)_serviceCache.get(SIDs[i]);
                            services.put(i,cachedService);
                        }
                    }
                    loop=false;
                }
            }finally{
                Job.getJobManager().endRule(_writeLock);
            }
        }
        _context.addServiceListener(_tracker);
        return services.values();   
    }

}

これが MultiResourceSchedulingRule です。

package lazycatTools.runtime;

import java.util.Collections;
import java.util.Set;
import java.util.HashSet;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.jobs.ISchedulingRule;

public class MultiResourceSchedulingRule<ParentType,ResourceType> extends ResourceBindingSchedulingRule<ParentType> implements IMultiResourceSchedulingRule<ParentType,ResourceType>  {

    private final Set<ResourceType> _resources;

    public MultiResourceSchedulingRule(ParentType parent){
        this(parent,new HashSet<ResourceType>());
    }
    public MultiResourceSchedulingRule(ParentType parent,Set<ResourceType> resources){
        super(parent);
        Assert.isLegal(resources!=null);
        _resources=resources;
    }
    @Override
    public boolean isConflicting(ISchedulingRule rule){
        if(rule==this){
            return true;
        }
        if(rule instanceof IResourceBindingSchedulingRule<?>){
            final IResourceBindingSchedulingRule<?> casted=(IResourceBindingSchedulingRule<?>)rule;
            if(_resources.contains(casted.getResource())){
                return true;
            }
        }
        if(rule instanceof IMultiResourceSchedulingRule<?,?>){
            final IMultiResourceSchedulingRule<?,?> casted=(IMultiResourceSchedulingRule<?,?>)rule;
            if(Collections.disjoint(_resources,casted.getResources())==false){
                return true;
            }
        }
        return false;
    }
    @Override
    public boolean contains(ISchedulingRule rule){
        if(rule==this){
            return true;
        }
        if(rule instanceof IResourceBindingSchedulingRule<?>){
            final IResourceBindingSchedulingRule<?> casted=(IResourceBindingSchedulingRule<?>)rule;
            if(_resources.contains(casted.getResource())){
                return true;
            }
        }
        if(rule instanceof IMultiResourceSchedulingRule){
            final IMultiResourceSchedulingRule<?,?> casted=(IMultiResourceSchedulingRule<?,?>)rule;
            if(_resources.containsAll(casted.getResources())){
                return true;
            }
        }
        return false;
    }
    @Override
    public Set<ResourceType> getResources() {
        return Collections.<ResourceType>unmodifiableSet(_resources);
    }   
}

そして ResourceBindingSchedulingRule.

package lazycatTools.runtime;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.jobs.ISchedulingRule;

public class ResourceBindingSchedulingRule<ResourceType> implements IResourceBindingSchedulingRule<ResourceType> {

    private final ResourceType _resource;

    public ResourceBindingSchedulingRule(ResourceType resource){
        Assert.isLegal(resource!=null);
        _resource=resource;
    }

    /* (non-Javadoc)
     * @see lazycatTools.runtime.IResourceBindingSchedulingRule#getResource()
     */
    @Override
    public ResourceType getResource() {
        return _resource;
    }

    @Override
    public boolean contains(ISchedulingRule rule) {
        return isConflicting(rule);
    }

    @Override
    public boolean isConflicting(ISchedulingRule rule) {
        if(rule==this){
            return true;
        }
        if(rule instanceof IResourceBindingSchedulingRule<?>){
            final IResourceBindingSchedulingRule<?> casted=(IResourceBindingSchedulingRule<?>)rule;
            return _resource==casted.getResource();
        }
        return false;
    }


}

例で readLock として使用されている SharedSchedulingRule。

package lazycatTools.runtime;

import org.eclipse.core.runtime.jobs.ISchedulingRule;

public class SharedSchedulingRule implements ISchedulingRule {

    private final ISchedulingRule _rule;

    public SharedSchedulingRule(ISchedulingRule rule){
        _rule=rule;
    }

    @Override
    public boolean contains(ISchedulingRule rule) {
        if(rule==this){
            return true;
        }
        return _rule.contains(rule);
    }

    @Override
    public boolean isConflicting(ISchedulingRule rule) {
        if(rule==this){
            return true;
        }
        return _rule.isConflicting(rule);
    }

}
4

6 に答える 6

5

最良のオプションは、すべてをimmutableにすることです。その後、何も心配する必要はありません:-)

これが不可能であると仮定すると、ライブラリをスレッドセーフにしないことをお勧めします。

  • スレッド セーフは通常、オーバーヘッドを追加します。低レベルのライブラリでは、ユーザーが必要としない可能性のある安全性のためにパフォーマンス ペナルティを支払うように強制するべきではありません。
  • ライブラリのレベルでスレッド セーフを追加したとしても、ユーザーはおそらくより高いレベルで独自のロックを追加する必要があります(これは、ロックが作成されないためです)。したがって、実際にはユーザーの作業を節約していない可能性があります。
  • 必要に応じて、後でいつでも同期ラッパーを追加できます。ただし、同期が組み込まれている場合、同期を削除することはできません。
  • これにより、独自のライブラリ コードがより簡単になります。ロックについて心配するよりも、より優れた機能に集中できます。

Java 標準ライブラリには、このアプローチとロジックに従う良い例があります。たとえばArrayList、スレッド セーフではありません。

于 2012-09-07T02:51:27.567 に答える
2

ユーザーを理解します。事前に判断できない場合、または主題について知識に基づいた推測を行うことができない場合は、実行しないでください。

ライブラリがリリースされたら、ユーザーの声に耳を傾けてください。

于 2012-09-07T02:17:29.903 に答える
0

私は以前のすべての答えに同意します。同時使用パターンを把握しようとするのは時期尚早の最適化を意味する可能性があります。マルチスレッドのサポートを提供せず、それをライブラリのユーザーにプッシュするのが最善です。JCFクラスのようなものです。ArrayList、LinkedListを参照すると、リストは続きます。

于 2012-09-07T02:48:10.677 に答える
0

あなたはそれをスレッドセーフにすることができるかもしれませんが、はるかに効率的で、おそらく一般的なケースではたった1つの揮発性読み取りです。問題のより現実的なバージョンを提供して、理解を深めることができます。

于 2012-09-07T02:43:41.813 に答える
0

それは単に、ユーザーの種類と、ユーザーが API を使用する方法によって異なります。「常に正しい」答えはありません。

同期を行うことにした場合は、あなたが行った方法の代わりにReentrantReadWriteLockを使用することを検討してください。

于 2012-09-07T02:20:50.630 に答える
0

プログラムがどのような用途に適用されるかによって異なりますが、必要な変更を行う前にコードを複雑にしすぎないようにすることをお勧めします。マルチスレッドの最適化は後で追加できますが、プログラムが成熟してからです。

于 2012-09-07T02:36:46.467 に答える