Java线程正确实现疑问:离线缓存数据同步至数据库的难题
Hey there! Let's work through this third part of your timestamp project—synchronizing the buffer to the database while continuing to write new timestamps when the server is back online. It sounds like you've got the offline buffering and basic timestamp writing down, so let's focus on making the sync and new writes play nicely together without conflicts.
1. Use a Thread-Safe Buffer First
The biggest risk here is race conditions when reading/writing the buffer from two separate threads. Ditch any plain List or non-thread-safe collection and use a built-in concurrent queue instead. This eliminates the need for manual locking (which is easy to mess up):
// Thread-safe buffer to hold timestamps when offline private final ConcurrentLinkedQueue<Instant> timestampBuffer = new ConcurrentLinkedQueue<>(); // Thread-safe flag to track server online status private final AtomicBoolean isServerOnline = new AtomicBoolean(false);
2. Split Logic Between Your Two Runnable Threads
Let's clarify the role of each thread to avoid overlap:
Thread 1: Timestamp Generator & Writer
This thread keeps doing its core job—generating a timestamp every second. It switches behavior based on the server status:
class TimestampWriterTask implements Runnable { @Override public void run() { while (!Thread.currentThread().isInterrupted()) { Instant currentTimestamp = Instant.now(); if (isServerOnline.get()) { // Online: Write directly to database immediately try { writeSingleTimestampToDb(currentTimestamp); } catch (SQLException e) { // If write fails (server went offline mid-write), fall back to buffer timestampBuffer.offer(currentTimestamp); // Optional: Trigger a status check here to update isServerOnline } } else { // Offline: Buffer the timestamp as you already implemented timestampBuffer.offer(currentTimestamp); } // Enforce 1-second interval try { Thread.sleep(1000); } catch (InterruptedException e) { // Handle graceful shutdown Thread.currentThread().interrupt(); break; } } } }
Thread 2: Buffer Sync Worker
This thread's sole job is to empty the buffer when the server is online. It runs in the background, checking periodically for buffer content to sync in batches (way more efficient than writing one at a time):
class BufferSyncTask implements Runnable { @Override public void run() { while (!Thread.currentThread().isInterrupted()) { if (isServerOnline.get() && !timestampBuffer.isEmpty()) { // Drain a batch of timestamps from the buffer (avoids repeated locks) List<Instant> batchToSync = new ArrayList<>(); timestampBuffer.drainTo(batchToSync); if (!batchToSync.isEmpty()) { try { // Batch write to database to reduce overhead batchWriteTimestampsToDb(batchToSync); } catch (SQLException e) { // If sync fails, put the batch back into the buffer timestampBuffer.addAll(batchToSync); // Optional: Mark server as offline again if needed } } } // Don't check constantly—give the CPU a break (adjust interval as needed) try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } }
3. Handle Server Status Transitions Cleanly
When your app detects the server is back online, just flip the atomic flag—both threads will automatically adjust their behavior:
// Call this when server connectivity is restored public void markServerOnline() { isServerOnline.set(true); } // Call this when server goes offline public void markServerOffline() { isServerOnline.set(false); }
4. Critical Edge Cases to Handle
- Failed Writes During Sync: If the server drops out mid-sync, make sure to put the unsent batch back into the buffer (like in the code examples above) so no data is lost.
- Graceful Shutdown: Always handle
InterruptedExceptionproperly to let threads exit cleanly without leaving data in limbo. - Batch Size Limits: If your buffer can get huge, add a max size to
drainTo()(e.g.,timestampBuffer.drainTo(batchToSync, 100)) to avoid overwhelming the database with massive writes.
内容的提问来源于stack exchange,提问作者Leonardo

