我是一个由后台服务调用的Runnable。 Runnable本身在我的FactoryManagerClass中初始化为SingleTon-Object。
在我的Logcat中,我在同一秒内运行了一天后发现了几次连接尝试。
06-15 12:00:52.665 9374-9656/com.myAppI/﹕ RestPushServiceRunnable: : Requesting url: http://my.ip/lp/053303932; LastModify: Thu, 01 Jan 1970 00:00:00 UTC; ETAG:
06-15 12:00:52.680 9374-17595/com.myAppI/﹕ RestPushServiceRunnable: : Requesting url: http://my.ip/lp/053303932; LastModify: Thu, 01 Jan 1970 00:00:00 UTC; ETAG:
06-15 12:00:52.685 9374-15696/com.myAppI/﹕ RestPushServiceRunnable: : Requesting url: http://my.ip/lp/053303932; LastModify: Thu, 01 Jan 1970 00:00:00 UTC; ETAG:
这意味着它已经创建了3次,但至少它应该只有1次可运行。
private static LPRunnable lpRunnable = null;
private static ExecutorService pushThreadPoolExecutor = null;
public static LPRunnable getLPRunnable() {
if (lpRunnable == null) {
synchronized (LPRunnable.class) {
lpRunnable = new LPRunnable (CustomService.getContext());
}
}
return lpRunnable;
}
public static ExecutorService getPushThreadPoolExecutor() {
if (pushThreadPoolExecutor == null) {
pushThreadPoolExecutor = Executors.newSingleThreadExecutor();
}
return pushThreadPoolExecutor;
}
My Runnable Class(几乎没有截断)
public class LPRunnable implements Runnable {
public static boolean isRunning = false;
@Override
public void run() {
HttpURLConnection connection = null;
try {
isRunning = true;
URL serverAddress;
while (isRunning) {
try {
MDatabaseManager databaseManager = methodToInitMYDBManager();
PushConnection pushConnection = new PushConnection();
pushConnection.setStatusCode(0);
//this is used to store the last connection attempt (time)
databaseManager.insertEntry(toContentValues(pushConnection), "pc");
connection = null;
serverAddress = new URL(myURLforLongPolling);
connection = (HttpURLConnection) serverAddress.openConnection();
connection.setRequestMethod("GET");
connection.setDoOutput(false);
connection.setReadTimeout(100000);
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Charset", "UTF-8");
connection.setRequestProperty("Content-Transfer-Encoding", "binary");
connection.connect();
long begin_time = System.currentTimeMillis();
int resCode = connection.getResponseCode();
if (resCode != 200) {
throw new IOException("Response Status Code not 200");
}
parseInputstream(connection.getInputStream());
long end_time = System.currentTimeMillis();
} catch (Exception e) {
e.printStackTrace();
isRunning = false;
} finally {
if (connection != null) {
connection.disconnect();
connection = null;
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
isRunning = false;
}
}
现在有趣的部分。我有一个AlarmTimer调用另一个Runnable。 Runnable执行一些操作,最后通过调用此方法验证连接。
Future connectionFuture;
public void validatePushConnection() {
databaseManager = myMethodToInitTheDb();
//here it will get the last sent push
PushConnection pushConnection = databaseManager.getLastPushConnectionFromDB();
if (pushConnection != null) {
long secondsSinceLastPush = ((System.currentTimeMillis() - pushConnection.getLast_connection_attempt().getTime()) / 1000);
if ((secondsSinceLastPush >= 400 || secondsSinceLastPush == 0) && hasInternet()) {
Log.e("CommandManager", "Delay is larger then 400 or and internet is there. Reconnecting");
mFactoryManager.getLPRunnable().isRunning = false;
connectionFuture = mFactoryManager.getPushThreadPoolExecutor().submit(mFactoryManager.getLPRunnable());
}
} else {
Log.d("CommandManager", "No push yet. Waiting for the first push");
if (connectionFuture == null || connectionFuture.isCancelled() || connectionFuture.isDone() || connectionFuture.get() == null) {
connectionFuture = mFactoryManager.getPushThreadPoolExecutor().submit(mFactoryMAnager.getLPRunnable());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
问题是,服务器的连接(Runnable)似乎多次创建(一段时间后),并且应该只有一个runnable实例。什么可能导致多重连接?
答案 0 :(得分:2)
即使使用synchronized
,以下(两次使用)模式也不是那么线程安全。
private static A a;
public static A getA() {
if (a == null) {
a = new A();
}
return a;
}
正确的模式是:
private static class AHolder { // Ensures full initialisation of the class
private static A a;
}
public static A getA() {
if (AHolder.a == null) {
synchronized (A.class) {
// The first may have filled a
if (AHolder.a == null) {
AHolder.a = new A();
}
}
}
return AHolder.a;
}
这适用于两个静态字段。我自己不想那么多使用static
。
<强> 精化 强>
以上lpRunnable和pushThreadPoolExecutor都应该是单例(对象只存在一次)。
(如果包含这些字段的类是单身,则可以在多个位置删除static
。)
现在synchronized (LPRunnable.class) {
保证只有一个线程通过,另一个线程等待。在你的代码中,它意味着如果已经不为空,则不会发生“同步”并且一切都很快。但是如果仍然为null,则在线程处于同步块内(所谓的关键区域),其他线程可能会停止在synchronized,如果第一个线程离开同步块(首先创建lpRunnable),第二个线程进入synchronized块,再次创建一个新的lpRunnable。
出于这个原因,我添加了第二个if lpRunnable == null
:
if
是填充lpRunnable
时的加速。if
检查我们是否可能没有等待,而早期的线程创建了它。