diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 0c1ffeb1a79a4..12d9f955bb767 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -1402,6 +1402,15 @@ impl<T> JoinHandle<T> {
     pub fn join(mut self) -> Result<T> {
         self.0.join()
     }
+
+    /// Checks if the the associated thread is still running its main function.
+    ///
+    /// This might return `false` for a brief moment after the thread's main
+    /// function has returned, but before the thread itself has stopped running.
+    #[unstable(feature = "thread_is_running", issue = "90470")]
+    pub fn is_running(&self) -> bool {
+        Arc::strong_count(&self.0.packet.0) > 1
+    }
 }
 
 impl<T> AsInner<imp::Thread> for JoinHandle<T> {
diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs
index 16ad366fc126a..ca0d88135a5d8 100644
--- a/library/std/src/thread/tests.rs
+++ b/library/std/src/thread/tests.rs
@@ -2,9 +2,13 @@ use super::Builder;
 use crate::any::Any;
 use crate::mem;
 use crate::result;
-use crate::sync::mpsc::{channel, Sender};
+use crate::sync::{
+    mpsc::{channel, Sender},
+    Arc, Barrier,
+};
 use crate::thread::{self, ThreadId};
 use crate::time::Duration;
+use crate::time::Instant;
 
 // !!! These tests are dangerous. If something is buggy, they will hang, !!!
 // !!! instead of exiting cleanly. This might wedge the buildbots.       !!!
@@ -46,6 +50,36 @@ fn test_run_basic() {
     rx.recv().unwrap();
 }
 
+#[test]
+fn test_is_running() {
+    let b = Arc::new(Barrier::new(2));
+    let t = thread::spawn({
+        let b = b.clone();
+        move || {
+            b.wait();
+            1234
+        }
+    });
+
+    // Thread is definitely running here, since it's still waiting for the barrier.
+    assert_eq!(t.is_running(), true);
+
+    // Unblock the barrier.
+    b.wait();
+
+    // Now check that t.is_running() becomes false within a reasonable time.
+    let start = Instant::now();
+    while t.is_running() {
+        assert!(start.elapsed() < Duration::from_secs(2));
+        thread::sleep(Duration::from_millis(15));
+    }
+
+    // Joining the thread should not block for a significant time now.
+    let join_time = Instant::now();
+    assert_eq!(t.join().unwrap(), 1234);
+    assert!(join_time.elapsed() < Duration::from_secs(2));
+}
+
 #[test]
 fn test_join_panic() {
     match thread::spawn(move || panic!()).join() {