diff --git a/components/net/sf/briar/plugins/bluetooth/AbstractListener.java b/components/net/sf/briar/plugins/bluetooth/AbstractListener.java
index 8a0815dd442f805582aa1e914abd34300cacf0dc..a7bed55c75f132548a5b9ca428d658891fedd727 100644
--- a/components/net/sf/briar/plugins/bluetooth/AbstractListener.java
+++ b/components/net/sf/briar/plugins/bluetooth/AbstractListener.java
@@ -3,6 +3,7 @@ package net.sf.briar.plugins.bluetooth;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Enumeration;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.bluetooth.DataElement;
@@ -14,29 +15,18 @@ abstract class AbstractListener implements DiscoveryListener {
 
 	protected final DiscoveryAgent discoveryAgent;
 	protected final AtomicInteger searches = new AtomicInteger(1);
-
-	protected boolean finished = false; // Locking: this
+	protected final CountDownLatch finished = new CountDownLatch(1);
 
 	protected AbstractListener(DiscoveryAgent discoveryAgent) {
 		this.discoveryAgent = discoveryAgent;
 	}
 
 	public void inquiryCompleted(int discoveryType) {
-		if(searches.decrementAndGet() == 0) {
-			synchronized(this) {
-				finished = true;
-				notifyAll();
-			}
-		}
+		if(searches.decrementAndGet() == 0) finished.countDown();
 	}
 
 	public void serviceSearchCompleted(int transaction, int response) {
-		if(searches.decrementAndGet() == 0) {
-			synchronized(this) {
-				finished = true;
-				notifyAll();
-			}
-		}
+		if(searches.decrementAndGet() == 0) finished.countDown();
 	}
 
 	protected Object getDataElementValue(Object o) {
diff --git a/components/net/sf/briar/plugins/bluetooth/ContactListener.java b/components/net/sf/briar/plugins/bluetooth/ContactListener.java
index daf546ce3f110229e44c1663751548fcc7234350..9b77704e0ef254cc3fcfda31fd250e7ea52506d3 100644
--- a/components/net/sf/briar/plugins/bluetooth/ContactListener.java
+++ b/components/net/sf/briar/plugins/bluetooth/ContactListener.java
@@ -34,13 +34,11 @@ class ContactListener extends AbstractListener {
 		urls = Collections.synchronizedMap(new HashMap<ContactId, String>());
 	}
 
-	public synchronized Map<ContactId, String> waitForUrls() {
-		while(!finished) {
-			try {
-				wait();
-			} catch(InterruptedException e) {
-				if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
-			}
+	Map<ContactId, String> waitForUrls() {
+		try {
+			finished.await();
+		} catch(InterruptedException e) {
+			if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
 		}
 		return urls;
 	}
diff --git a/components/net/sf/briar/plugins/bluetooth/InvitationListener.java b/components/net/sf/briar/plugins/bluetooth/InvitationListener.java
index 01663b99d21bdeb19a9068fbfafe2748f21a5c27..9d258d87d1245eec096e85bcd26b81eaac2b03e5 100644
--- a/components/net/sf/briar/plugins/bluetooth/InvitationListener.java
+++ b/components/net/sf/briar/plugins/bluetooth/InvitationListener.java
@@ -19,20 +19,18 @@ class InvitationListener extends AbstractListener {
 
 	private final String uuid;
 
-	private String url = null; // Locking: this
+	private volatile String url = null;
 
 	InvitationListener(DiscoveryAgent discoveryAgent, String uuid) {
 		super(discoveryAgent);
 		this.uuid = uuid;
 	}
 
-	synchronized String waitForUrl() {
-		while(!finished) {
-			try {
-				wait();
-			} catch(InterruptedException e) {
-				if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
-			}
+	String waitForUrl() {
+		try {
+			finished.await();
+		} catch(InterruptedException e) {
+			if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
 		}
 		return url;
 	}
@@ -60,12 +58,9 @@ class InvitationListener extends AbstractListener {
 			for(String u : uuids) {
 				if(uuid.equalsIgnoreCase(u)) {
 					// The UUID matches - store the URL
-					synchronized(this) {
-						url = serviceUrl;
-						finished = true;
-						notifyAll();
-						return;
-					}
+					url = serviceUrl;
+					finished.countDown();
+					return;
 				}
 			}
 		}
diff --git a/components/net/sf/briar/plugins/file/FileListener.java b/components/net/sf/briar/plugins/file/FileListener.java
index d71de06e4833296d260057dd200e36f9554c8677..e98e0f34210d697ea701106620355ce4843a82ce 100644
--- a/components/net/sf/briar/plugins/file/FileListener.java
+++ b/components/net/sf/briar/plugins/file/FileListener.java
@@ -1,6 +1,8 @@
 package net.sf.briar.plugins.file;
 
 import java.io.File;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -11,31 +13,29 @@ class FileListener {
 
 	private final String filename;
 	private final long end;
+	private final CountDownLatch finished = new CountDownLatch(1);
 
-	private File file = null; // Locking: this
+	private volatile File file = null;
 
 	FileListener(String filename, long timeout) {
 		this.filename = filename;
 		end = System.currentTimeMillis() + timeout;
 	}
 
-	synchronized File waitForFile() {
+	File waitForFile() {
 		long now = System.currentTimeMillis();
-		while(file == null && now < end) {
-			try {
-				wait(end - now);
-			} catch(InterruptedException e) {
-				if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
-			}
-			now = System.currentTimeMillis();
+		try {
+			finished.await(end - now, TimeUnit.MILLISECONDS);
+		} catch(InterruptedException e) {
+			if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
 		}
 		return file;
 	}
 
-	synchronized void addFile(File f) {
+	void addFile(File f) {
 		if(filename.equals(f.getName())) {
 			file = f;
-			notifyAll();
+			finished.countDown();
 		}
 	}
 }