我有一个由后台服务调用的Runnable。 Runnable本身在FactoryManagerClass中初始化为SingleTon-Object。
在我的Logcat中,我在同一秒内运行1天后进行了几次连接尝试。
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;
}
我的可运行类是(几乎不被截断)
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();
}
}
问题在于,与服务器的连接(可运行)似乎已创建多次(一段时间后),并且应该仅具有一个可运行实例。是什么引起多重连接?
最佳答案
即使使用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,则不会发生“同步”,并且一切都很快。但是,如果仍然为null,则当线程在同步块内(所谓的关键区域)时,其他线程可能会在同步块处停止。如果第一个线程离开了同步块(创建了第一个lpRunnable),则第二个线程进入了同步块,再次创建一个新的lpRunnable。因此,我添加了第二个
if lpRunnable == null
:填充
if
时,第一个lpRunnable
是加速。第二个
if
检查我们是否不必等待,并且先前的线程已创建它。关于java - 让一次Runnable或Thread只调用一次,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24228778/