Raspberry Pi上Java应用频繁崩溃且CPU占用过高(>100%)求助
Hey there, let's break down why your Java app on Raspberry Pi keeps crashing with sky-high CPU usage (>100%). From your description of the workflow—RFID triggers messageReceived to store tags, then a new thread listens on a Socket and calls JavaFX methods when the collection changes—here are the most likely issues and practical fixes:
The biggest red flag here is that two separate threads are interacting with the same collection: the RFID reader's thread calling messageReceived to add tags, and your Socket listener thread checking for changes. If you're using a non-thread-safe collection like ArrayList or HashSet, concurrent reads/writes can lead to:
- ConcurrentModificationException (which might not crash immediately but corrupts state)
- Infinite loops or busy waiting as the collection's internal state gets messed up (directly spiking CPU)
Fix:
Switch to a thread-safe collection implementation:
- Use
CopyOnWriteArrayListif you mostly read from the collection and only occasionally add tags (it's optimized for read-heavy workloads) - Use
ConcurrentHashMap(orConcurrentSkipListSetif you need unique tags) for better write performance
Example:
// Replace your regular list with this private final List<String> rfidTags = new CopyOnWriteArrayList<>(); // In messageReceived public void messageReceived(String tag) { rfidTags.add(tag); }
JavaFX strictly requires all UI-related calls (like opening windows, updating controls) to run on the JavaFX Application Thread. If your Socket listener thread calls these methods directly, you're violating this rule—and that can cause everything from weird UI glitches to hard crashes, plus unexpected CPU usage as the UI thread and your listener thread fight for resources.
Fix:
Wrap all JavaFX calls in Platform.runLater() to queue them on the correct thread:
// Inside your Socket listener thread if (collectionChanged && socketIsConnected) { Platform.runLater(() -> { // Your JavaFX code here—e.g., open a window Stage newStage = new Stage(); newStage.setScene(new Scene(new Label("Tag detected!"))); newStage.show(); }); }
If your Socket thread is using a naive loop like while (true) to check if the collection has changed, it's probably spinning non-stop when there's no activity—eating up 100% of a CPU core. This is called "busy waiting" and is a huge waste of resources on a low-power device like the Raspberry Pi.
Fix:
Replace polling with a notification mechanism so the thread only wakes up when it needs to:
- Use a
BlockingQueueinstead of a regular collection: whenmessageReceivedadds a tag, it puts it in the queue, and the Socket thread blocks ontake()until a tag arrives - Use a
CountDownLatchorCondition(fromjava.util.concurrent) to signal the thread when the collection changes
Example with BlockingQueue:
private final BlockingQueue<String> tagQueue = new LinkedBlockingQueue<>(); // In messageReceived public void messageReceived(String tag) { tagQueue.put(tag); // Blocks if queue is full (adjust capacity if needed) } // In your Socket listener thread while (socketIsConnected) { String newTag = tagQueue.take(); // Blocks until a tag is available // Process the tag and trigger JavaFX UI update Platform.runLater(() -> { /* UI code */ }); }
If you're creating a new Socket listener thread every time a tag is read, you're quickly going to flood the Pi with threads. Each thread takes up memory and CPU time for context switching, leading to resource exhaustion and crashes.
Fix:
Use a thread pool to manage your Socket threads. This lets you limit the number of concurrent threads and reuse them instead of creating new ones:
// Create a thread pool with a fixed number of threads (adjust based on Pi's cores) private final ExecutorService socketThreadPool = Executors.newFixedThreadPool(2); // When you need to start a Socket listener socketThreadPool.submit(() -> { // Your Socket listening logic here }); // Don't forget to shut down the pool when your app exits! @Override public void stop() throws Exception { socketThreadPool.shutdown(); socketThreadPool.awaitTermination(5, TimeUnit.SECONDS); super.stop(); }
If your collection keeps growing indefinitely (you never remove old tags) or Socket threads aren't properly closed when connections drop, your app will run out of memory. When the JVM starts garbage collecting (GC) constantly to free up space, CPU usage spikes—and eventually, the app crashes with an OutOfMemoryError.
Fix:
- Add logic to clean up old tags from your collection (e.g., remove tags older than X minutes)
- Ensure Socket threads terminate gracefully when the connection closes: check the Socket's
isConnected()status regularly, and use a volatile boolean flag to signal the thread to stop - Enable GC logging to diagnose leaks: add these JVM args when running your app:
You can analyze the log to see if GC is running too frequently.-Xlog:gc*:file=gc.log:time,level,tags
Start with the first two fixes—thread-safe collections and correct JavaFX thread handling—since those are the most common causes of high CPU and crashes in this kind of setup. If you're still having issues, check for busy waiting and thread bloat next.
内容的提问来源于stack exchange,提问作者jordibenck

