diff --git a/tokio/tests/udp.rs b/tokio/tests/udp.rs
index eea281c2316..5a9b71746f7 100644
--- a/tokio/tests/udp.rs
+++ b/tokio/tests/udp.rs
@@ -643,3 +643,46 @@ async fn poll_ready() {
         }
     }
 }
+
+#[tokio::test]
+async fn concurrent_recv() {
+    use std::sync::atomic::{AtomicBool, Ordering};
+
+    // Create two copies of the same server socket
+    let server = UdpSocket::bind("127.0.0.1:0").await.unwrap();
+    let server2 = server.try_clone().unwrap();
+    let saddr = server.local_addr().unwrap();
+
+    // Create client
+    let client = UdpSocket::bind("127.0.0.1:0").await.unwrap();
+
+    const MSG1: [u8; 3] = [1, 2, 3];
+    const MSG2: [u8; 3] = [4, 5, 6];
+
+    let got_msg1 = Arc::new(AtomicBool::new(false));
+    let got_msg2 = Arc::new(AtomicBool::new(false));
+
+    async fn recv(socket: UdpSocket, m1: Arc<AtomicBool>, m2: Arc<AtomicBool>) {
+        let mut buf = [0; 3];
+        socket.recv(&mut buf).await.unwrap();
+        match buf {
+            MSG1 => &m1,
+            MSG2 => &m2,
+            _ => return,
+        }
+        .store(true, Ordering::Relaxed);
+    }
+
+    // Try to receive a message on each clone of the socket
+    let recv1 = tokio::spawn(recv(server, got_msg1.clone(), got_msg2.clone()));
+    let recv2 = tokio::spawn(recv(server2, got_msg1.clone(), got_msg2.clone()));
+    client.send_to(&MSG1, saddr).await.unwrap();
+    client.send_to(&MSG2, saddr).await.unwrap();
+
+    // Both receivers should wake and read a datagram
+    recv1.await.unwrap();
+    recv2.await.unwrap();
+
+    // Both messages should have been received
+    assert!(got_msg1.load(Ordering::Relaxed) && got_msg1.load(Ordering::Relaxed));
+}