diff --git a/libio/feof.c b/libio/feof.c
index 8275321788..823b40637a 100644
--- a/libio/feof.c
+++ b/libio/feof.c
@@ -32,7 +32,7 @@ _IO_feof (FILE *fp)
 {
   int result;
   CHECK_FILE (fp, EOF);
-  if (!_IO_need_lock (fp))
+  if (!io_need_lock (fp))
     return _IO_feof_unlocked (fp);
   _IO_flockfile (fp);
   result = _IO_feof_unlocked (fp);
diff --git a/libio/ferror.c b/libio/ferror.c
index 588f2eb50b..13f1a15ecc 100644
--- a/libio/ferror.c
+++ b/libio/ferror.c
@@ -32,7 +32,7 @@ _IO_ferror (FILE *fp)
 {
   int result;
   CHECK_FILE (fp, EOF);
-  if (!_IO_need_lock (fp))
+  if (!io_need_lock (fp))
     return _IO_ferror_unlocked (fp);
   _IO_flockfile (fp);
   result = _IO_ferror_unlocked (fp);
diff --git a/libio/fputc.c b/libio/fputc.c
index c3a1fa47db..b3925c34e8 100644
--- a/libio/fputc.c
+++ b/libio/fputc.c
@@ -32,7 +32,7 @@ fputc (int c, FILE *fp)
 {
   int result;
   CHECK_FILE (fp, EOF);
-  if (!_IO_need_lock (fp))
+  if (!io_need_lock (fp))
     return _IO_putc_unlocked (c, fp);
   _IO_acquire_lock (fp);
   result = _IO_putc_unlocked (c, fp);
diff --git a/libio/getc.c b/libio/getc.c
index e3e658bf79..df2e40dd51 100644
--- a/libio/getc.c
+++ b/libio/getc.c
@@ -34,7 +34,7 @@ _IO_getc (FILE *fp)
 {
   int result;
   CHECK_FILE (fp, EOF);
-  if (!_IO_need_lock (fp))
+  if (!io_need_lock (fp))
     return _IO_getc_unlocked (fp);
   _IO_acquire_lock (fp);
   result = _IO_getc_unlocked (fp);
diff --git a/libio/getchar.c b/libio/getchar.c
index 0d6225853f..0607cd40dd 100644
--- a/libio/getchar.c
+++ b/libio/getchar.c
@@ -33,7 +33,7 @@ int
 getchar (void)
 {
   int result;
-  if (!_IO_need_lock (stdin))
+  if (!io_need_lock (stdin))
     return _IO_getc_unlocked (stdin);
   _IO_acquire_lock (stdin);
   result = _IO_getc_unlocked (stdin);
diff --git a/libio/ioungetc.c b/libio/ioungetc.c
index dcf1c4ca9e..dcc99aa5a3 100644
--- a/libio/ioungetc.c
+++ b/libio/ioungetc.c
@@ -33,7 +33,7 @@ _IO_ungetc (int c, FILE *fp)
   CHECK_FILE (fp, EOF);
   if (c == EOF)
     return EOF;
-  if (!_IO_need_lock (fp))
+  if (!io_need_lock (fp))
     return _IO_sputbackc (fp, (unsigned char) c);
   _IO_acquire_lock (fp);
   result = _IO_sputbackc (fp, (unsigned char) c);
diff --git a/libio/libio.h b/libio/libio.h
index 511b39457f..6b569511ec 100644
--- a/libio/libio.h
+++ b/libio/libio.h
@@ -47,6 +47,8 @@
 #include <bits/types/__mbstate_t.h>
 #include <bits/types/wint_t.h>
 #include <gconv.h>
+#include <atomic.h>
+#include <stdbool.h>
 
 typedef struct
 {
@@ -210,8 +212,33 @@ extern int _IO_ftrylockfile (FILE *) __THROW;
 #define _IO_cleanup_region_end(_Doit) /**/
 #endif
 
-#define _IO_need_lock(_fp) \
-  (((_fp)->_flags2 & _IO_FLAGS2_NEED_LOCK) != 0)
+
+/* The _IO_FLAGS2_NEED_LOCK flag is set on:
+
+   1. the stream creation (for memstream and wmemstream).
+   2. by pthread_create when it detects the process become multi-thread.
+   3. by flockfile.
+
+   The 1. and 2. cases do not generate a race condition since for 1.
+   the stream reference is not accessible by other threads and for 2.
+   the process is still single-threaded.
+
+   However, for 3. the functions feof, ferror, fputc, getc, getchar, ungetc,
+   and putc access it without locking the stream to check whether to use the
+   optimized _unlocked variants.  So to avoid a racy condition, both the
+   check and set are done using atomic operations.  */
+
+static inline bool
+io_need_lock (FILE *fp)
+{
+  return atomic_load_acquire (&fp->_flags2) & _IO_FLAGS2_NEED_LOCK;
+}
+
+static inline void
+io_set_need_lock (FILE *fp)
+{
+  atomic_fetch_or_release (&fp->_flags2, _IO_FLAGS2_NEED_LOCK);
+}
 
 extern int _IO_vfscanf (FILE * __restrict, const char * __restrict,
 			__gnuc_va_list, int *__restrict);
diff --git a/libio/putc.c b/libio/putc.c
index 0b0dd40736..1f1da21239 100644
--- a/libio/putc.c
+++ b/libio/putc.c
@@ -25,7 +25,7 @@ _IO_putc (int c, FILE *fp)
 {
   int result;
   CHECK_FILE (fp, EOF);
-  if (!_IO_need_lock (fp))
+  if (!io_need_lock (fp))
     return _IO_putc_unlocked (c, fp);
   _IO_acquire_lock (fp);
   result = _IO_putc_unlocked (c, fp);
diff --git a/stdio-common/flockfile.c b/stdio-common/flockfile.c
index a66e0a731e..3b42fe891d 100644
--- a/stdio-common/flockfile.c
+++ b/stdio-common/flockfile.c
@@ -22,7 +22,7 @@
 void
 __flockfile (FILE *stream)
 {
-  stream->_flags2 |= _IO_FLAGS2_NEED_LOCK;
+  io_set_need_lock (stream);
   _IO_lock_lock (*stream->_lock);
 }
 weak_alias (__flockfile, flockfile);
