BasicCookieStore no longer synchronizes on reads

BasicCookieStore uses a ReentrantReadWriteLock to avoid
synchronization on getCookies/toString while maintaining
thread safety.

Closes PR #81
This commit is contained in:
Carter Kozak 2017-08-07 09:47:04 -04:00 committed by Oleg Kalnichevski
parent 9efcba8730
commit a10967a427
1 changed files with 51 additions and 22 deletions

View File

@ -32,6 +32,8 @@ import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.annotation.ThreadingBehavior;
@ -48,10 +50,12 @@ public class BasicCookieStore implements CookieStore, Serializable {
private static final long serialVersionUID = -7581093305228232025L; private static final long serialVersionUID = -7581093305228232025L;
private final TreeSet<Cookie> cookies; private final TreeSet<Cookie> cookies;
private final ReadWriteLock lock;
public BasicCookieStore() { public BasicCookieStore() {
super(); super();
this.cookies = new TreeSet<>(new CookieIdentityComparator()); this.cookies = new TreeSet<>(new CookieIdentityComparator());
this.lock = new ReentrantReadWriteLock();
} }
/** /**
@ -65,12 +69,17 @@ public class BasicCookieStore implements CookieStore, Serializable {
* *
*/ */
@Override @Override
public synchronized void addCookie(final Cookie cookie) { public void addCookie(final Cookie cookie) {
if (cookie != null) { if (cookie != null) {
// first remove any old cookie that is equivalent lock.writeLock().lock();
cookies.remove(cookie); try {
if (!cookie.isExpired(new Date())) { // first remove any old cookie that is equivalent
cookies.add(cookie); cookies.remove(cookie);
if (!cookie.isExpired(new Date())) {
cookies.add(cookie);
}
} finally {
lock.writeLock().unlock();
} }
} }
} }
@ -85,10 +94,10 @@ public class BasicCookieStore implements CookieStore, Serializable {
* @see #addCookie(Cookie) * @see #addCookie(Cookie)
* *
*/ */
public synchronized void addCookies(final Cookie[] cookies) { public void addCookies(final Cookie[] cookies) {
if (cookies != null) { if (cookies != null) {
for (final Cookie cooky : cookies) { for (final Cookie cookie : cookies) {
this.addCookie(cooky); this.addCookie(cookie);
} }
} }
} }
@ -100,9 +109,14 @@ public class BasicCookieStore implements CookieStore, Serializable {
* @return an array of {@link Cookie cookies}. * @return an array of {@link Cookie cookies}.
*/ */
@Override @Override
public synchronized List<Cookie> getCookies() { public List<Cookie> getCookies() {
//create defensive copy so it won't be concurrently modified lock.readLock().lock();
return new ArrayList<>(cookies); try {
//create defensive copy so it won't be concurrently modified
return new ArrayList<>(cookies);
} finally {
lock.readLock().unlock();
}
} }
/** /**
@ -114,31 +128,46 @@ public class BasicCookieStore implements CookieStore, Serializable {
* @see Cookie#isExpired(Date) * @see Cookie#isExpired(Date)
*/ */
@Override @Override
public synchronized boolean clearExpired(final Date date) { public boolean clearExpired(final Date date) {
if (date == null) { if (date == null) {
return false; return false;
} }
boolean removed = false; lock.writeLock().lock();
for (final Iterator<Cookie> it = cookies.iterator(); it.hasNext();) { try {
if (it.next().isExpired(date)) { boolean removed = false;
it.remove(); for (final Iterator<Cookie> it = cookies.iterator(); it.hasNext(); ) {
removed = true; if (it.next().isExpired(date)) {
it.remove();
removed = true;
}
} }
return removed;
} finally {
lock.writeLock().unlock();
} }
return removed;
} }
/** /**
* Clears all cookies. * Clears all cookies.
*/ */
@Override @Override
public synchronized void clear() { public void clear() {
cookies.clear(); lock.writeLock().lock();
try {
cookies.clear();
} finally {
lock.writeLock().unlock();
}
} }
@Override @Override
public synchronized String toString() { public String toString() {
return cookies.toString(); lock.readLock().lock();
try {
return cookies.toString();
} finally {
lock.readLock().unlock();
}
} }
} }