我正在尝试为实验室编写一些基本上是将数据存储在文件夹中的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>
注意:当我翻译变量和方法的名称以帮助理解时,我可能已经写了一些不同的东西,但在我的程序中是正确的
非常感谢先进
答案 0 :(得分:1)
首先,阅读该代码非常困难。在您的MainThread中,您几次嵌套if
语句而没有深入的代码分析,很难说出错误。
但很少有事情引起我的注意。
在Downloads
类中,您从两个不同的ReentrantReadWriteLock
对象中获取读写锁定的引用!
private Lock readLock=new ReentrantReadWriteLock().readLock();
private Lock writeLock=new ReentrantReadWriteLock().writeLock();
它无法正常工作!您应该为ReentrantReadWriteLock
对象创建一个Download
实例,并从中获取readLock
和writeLock
锁定的引用。读取和写入锁定已连接,因此当您锁定writeLock
时,会阻止读者执行受保护的代码部分。请阅读有关ReadWriteLock的更多信息,您可以查看使用ReentrantReadWriteLock in mbassador EventBus的示例。
更重要的是,您在Downloads
和DownloadsNode
中也不需要锁定!因为您为每个线程创建了这些类的实例,并且您不在线程之间共享它。因此,您只需从这些类中删除所有负责锁定的代码。在线程中共享的唯一对象是Cache
类的实例。
此外,您synchronized(this)
中不需要MainThread
!从此类创建的每个新线程将在不同对象(this
)上同步。因此,添加所有这些synchronized
块是没有意义的。最糟糕的是,我在附加的代码中只看到一个帖子。
我认为你应该删除所有负责锁定和同步的代码,然后分析在线程之间共享的女巫对象,并且应该保护它们不受并发访问的影响。对我来说,只需要锁定的对象是Cache
实例。
也许如果您发布SSCCE工作,可以帮助您重构此代码。
VGR
在他的评论中指出了一些不错的提示。