线程不等待使用Condition的新数据

时间:2013-12-29 20:11:57

标签: java concurrency conditional-statements mutual-exclusion reentrantreadwritelock

我正在尝试为实验室编写一些基本上是将数据存储在文件夹中的Web缓存,因此如果客户端想要打开网页,那么该页面将存储在缓存文件夹中(如果它尚未存在) ,它将向客户展示。我能够下载和显示页面,但问题出现在线程必须等待,因为所请求的资源正在下载并存储在缓存文件夹中。

下载类如下:

public class Downloads {
private ArrayList<DownloadsNode> nodes;
private int debug,readersCount=0,writersCount=0;
private Lock readLock=new ReentrantReadWriteLock().readLock();
private Lock writeLock=new ReentrantReadWriteLock().writeLock();    

public Downloads ( int debug )
{   nodes = new ArrayList<DownloadsNode>();
    this.debug = debug;
}


public synchronized DownloadsNode findNode ( URL url )
{   Iterator<DownloadsNode> i = nodes.iterator();
    while ( i.hasNext() )
    {   DownloadsNode node = i.next();
        if (node.getUrl().equals(url))
            return node;
    }
    return null;
}

public synchronized DownloadsNode addNode ( URL url )
{   DownloadsNode node = new DownloadsNode ( url );
    nodes.add ( node );
    return node;
}

public synchronized void deleteNode ( DownloadsNode node )
{   nodes.remove ( node );
}

public synchronized void accessAsAReader()
{
    while(writersCount>0)
    {
        try 
        {
            this.wait();
        } 
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }

    }
    readLock.lock();
    readersCount++;
}

public synchronized void accessAsAWriter()
{
    while(readersCount>0||writersCount>0)
    {
        try 
        {
            this.wait();
        } 
        catch (InterruptedException e) 
        {
            e.printStackTrace();
        }
    }
    writeLock.lock();
    writersCount++;
}

public synchronized void outAsAReader()
{
    readLock.unlock();
    readersCount--;
    this.notifyAll();
}

public synchronized void outAsAWriter()
{
    writeLock.unlock();
    writersCount--;
    this.notifyAll();
}

public Lock getreadLock()
{
    return this.readLock;
}
}

类DownloadsNode如下:

public class DownloadsNode {
private int waiting;
private URL url;
private cacheNode node;
private Lock readLock=new ReentrantReadWriteLock().readLock();
private Lock writeLock=new ReentrantReadWriteLock().writeLock();    
private int readersCount=0,writersCount=0;

public DownloadsNode ( URL url )
{   waiting = 1;
    this.url = url;
}

public URL getUrl()
{   return url;
}

public synchronized void setnodeC ( cacheNode node )
{   this.node = node;
}

public synchronized cacheNode getnodeC()
{   return node;
}

public synchronized int getwaiting()
{   return waiting;
}

public synchronized void addWaiting()
{   waiting++;
}


public synchronized void quitWaiting()
{   waiting--;
}

    public synchronized void accessAsAReader()
{
    while(writersCount>0)
    {
        try 
        {
            this.wait();
        } 
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }

    }
    readLock.lock();
    readersCount++;
}

public synchronized void accessAsAWriter()
{
    while(readersCount>0||writersCount>0)
    {
        try 
        {
            this.wait();
        } 
        catch (InterruptedException e) 
        {
            e.printStackTrace();
        }
    }
    writeLock.lock();
    writersCount++;
}

public synchronized void outAsAReader()
{
    readLock.unlock();
    readersCount--;
    this.notifyAll();
}

public synchronized void outAsAWriter()
{
    writeLock.unlock();
    writersCount--;
    this.notifyAll();
}

public Lock getreadLock()
{
    return this.readLock;
}
}

缓存类如下:

public class Cache {
private HashMap<URL,cacheNode> map; 
private Lock readLock=new ReentrantReadWriteLock().readLock();
private Lock writeLock=new ReentrantReadWriteLock().writeLock();    
private boolean added=false;

public Cache( int up_edge, int lim_inf, int debug )
{   
       //The content of this doesn't matter
}

public synchronized cacheNode findNode ( URL url )
{   
    cacheNode cacheNode= map.get ( url );
    return cacheNode;
}


public synchronized cacheNode addNode ( resourceWeb resource, AtomicInteger referencias )
{   

    cacheNode node = new cacheNode ( resource, referencias );
    map.put ( resource.getUrl(), node );
    added=true;
    return node;
}

public boolean getadded()
{
    return added;
}

    public synchronized void accessAsAReader()
{
    while(writersCount>0)
    {
        try 
        {
            this.wait();
        } 
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }

    }
    readLock.lock();
    readersCount++;
}

public synchronized void accessAsAWriter()
{
    while(readersCount>0||writersCount>0)
    {
        try 
        {
            this.wait();
        } 
        catch (InterruptedException e) 
        {
            e.printStackTrace();
        }
    }
    writeLock.lock();
    writersCount++;
}

public synchronized void outAsAReader()
{
    readLock.unlock();
    readersCount--;
    this.notifyAll();
}

public synchronized void outAsAWriter()
{
    writeLock.unlock();
    writersCount--;
    this.notifyAll();
}

public Lock getreadLock()
{
    return this.readLock;
}
}

所有以前的类都使用以下类:

public class MainThread extends Thread{
private ArrayBlockingQueue<Petition> petitionQueue;
private Cache cache;
private int debug;

//constructor goes here but its content doesn't matter

public void run()
{
    webResource webResource = null;
    while(true)
    {
        try {
            CacheNode cNode;
            do
            {

                Petition petition = PetitionQueue.take();


                //Searchs the web resource inside the cache
                cache.accessAsAReader();
                synchronized(this)
                {
                    cNode = cache.findNode ( petition.getURL() );
                }
                if ( cNode != null )    // If it's found
                {   
                    cache.outAsAReader();
                    webResource = cNode.webResource;
                }                   

                else // if it's not found
                {
                    cache.outAsAReader();

                    Downloads downloads=new Downloads(debug);
                    DownloadsNode node;
                    downloads.accessAsAReader();
                    synchronized(this)
                    {
                        node=downloads.findNode(petition.getURL());
                    }

                    **if(node!=null)
                    {
                        downloads.outAsAReader();                           
                        /*Another thread is downloading to store in the cache*/
                        /*We indicate that we are waiting. Here is my problem if I'm not wrong*/
                        node.accessAsAWriter();                     
                        synchronized(this)
                        {
                            node.incrementWaiting();
                        }
                        Condition waitingCondition=node.getReadLock().newCondition();
                        while(cache!=null&&!cache.getAdded())
                        {
                            waitingCondition.await();   
                        }
                        /*Delete from count of waiting threads*/

                        synchronized(this)
                        {
                            nodo.decrementWaiting();
                        }
                        node.outAsAWriter();
                        /*If there aren't wating threads, delete the entry from downloads list*/
                        if(node.getWaiting()==0)
                        {
                            downloads.accessAsAWriter();
                            synchronized(this)
                            {
                                downloads.delete(node);
                            }
                            downloads.outAsAWriter();
                        }
                    }
                    else
                    {
                        downloads.outAsAReader();
                        downloads.accessAsAWriter();
                        synchronized(this)
                        {
                            node=downloads.addNode(petition.getURL());
                        }
                        downloads.outAsAWriter();
                        // Download the web resource.
                        webResource = petition.download();


                        cache.accessAsAWriter();
                        synchronized(this)
                        {
                            cNode = cache.addNode ( webResource, new AtomicInteger(1) );

                        }
                        cache.outAsAWriter();
                        /*Check if other threads are waiting*/
                        if(node.getWaiting()>0)
                        {
                            Condition condition2=nodo.getWriteLock().newCondition();
                            condition2.signalAll();
                        }
                        else
                        {
                            downloads.accessAsAWriter();
                            synchronized(this)
                            {
                                downloads.delete(node);
                            }
                            downloads.outAsAWriter();
                        }
                    }

                }
                //Sends response to the client
            } while (true);

        } catch (InterruptedException e) {
             // TODO: poner una forma de terminar.
        } catch (Exception e) {

        }
    }
}

}

有几点需要考虑:

- 我必须在每个节点中创建条件变量,因此线程会因此而等待,并实现方法来打开/关闭锁定并保证互斥。

- 如果一个线程正在下载其他线程感兴趣的资源,我使用Condition变量等待,并在资源下载并存储在缓存中时说“停止等待”。我在那部分做错了什么? (所有这些都在MainThread类中)

- 当保证互斥时,我是否必须在正确的方法中进行同步,请按以下方式执行:  -锁  -synchronized(this)并尽我所能  -开锁 或者我在两个方面做同样的事情?在管理下载列表和缓存时,我必须建立批评区域,所以我认为有些部分在方法的名称中添加synchronized是不需要的,或者我错误地使用了读写锁。 / p>

注意:当我翻译变量和方法的名称以帮助理解时,我可能已经写了一些不同的东西,但在我的程序中是正确的

非常感谢先进

1 个答案:

答案 0 :(得分:1)

首先,阅读该代码非常困难。在您的MainThread中,您几次嵌套if语句而没有深入的代码分析,很难说出错误。

但很少有事情引起我的注意。

Downloads类中,您从两个不同的ReentrantReadWriteLock对象中获取读写锁定的引用!

private Lock readLock=new ReentrantReadWriteLock().readLock();
private Lock writeLock=new ReentrantReadWriteLock().writeLock(); 

它无法正常工作!您应该为ReentrantReadWriteLock对象创建一个Download实例,并从中获取readLockwriteLock锁定的引用。读取和写入锁定已连接,因此当您锁定writeLock时,会阻止读者执行受保护的代码部分。请阅读有关ReadWriteLock的更多信息,您可以查看使用ReentrantReadWriteLock in mbassador EventBus的示例。

更重要的是,您在DownloadsDownloadsNode中也不需要锁定!因为您为每个线程创建了这些类的实例,并且您不在线程之间共享它。因此,您只需从这些类中删除所有负责锁定的代码。在线程中共享的唯一对象是Cache类的实例。

此外,您synchronized(this)中不需要MainThread!从此类创建的每个新线程将在不同对象(this)上同步。因此,添加所有这些synchronized块是没有意义的。最糟糕的是,我在附加的代码中只看到一个帖子。

我认为你应该删除所有负责锁定和同步的代码,然后分析在线程之间共享的女巫对象,并且应该保护它们不受并发访问的影响。对我来说,只需要锁定的对象是Cache实例。

也许如果您发布SSCCE工作,可以帮助您重构此代码。 VGR在他的评论中指出了一些不错的提示。