diff --git a/spec/std/file_spec.cr b/spec/std/file_spec.cr
index a393049f596a..e9270a48b483 100644
--- a/spec/std/file_spec.cr
+++ b/spec/std/file_spec.cr
@@ -933,22 +933,21 @@ describe "File" do
     end
   end
 
-  # TODO: implement flock on windows
   describe "flock" do
-    pending_win32 "exclusively locks a file" do
+    it "#flock_exclusive" do
       File.open(datapath("test_file.txt")) do |file1|
         File.open(datapath("test_file.txt")) do |file2|
           file1.flock_exclusive do
             exc = expect_raises(IO::Error, "Error applying file lock: file is already locked") do
               file2.flock_exclusive(blocking: false) { }
             end
-            exc.os_error.should eq Errno::EWOULDBLOCK
+            exc.os_error.should eq({% if flag?(:win32) %}WinError::ERROR_LOCK_VIOLATION{% else %}Errno::EWOULDBLOCK{% end %})
           end
         end
       end
     end
 
-    pending_win32 "shared locks a file" do
+    it "#flock_shared" do
       File.open(datapath("test_file.txt")) do |file1|
         File.open(datapath("test_file.txt")) do |file2|
           file1.flock_shared do
@@ -958,7 +957,7 @@ describe "File" do
       end
     end
 
-    pending_win32 "#flock_shared soft blocking fiber" do
+    it "#flock_shared soft blocking fiber" do
       File.open(datapath("test_file.txt")) do |file1|
         File.open(datapath("test_file.txt")) do |file2|
           done = Channel(Nil).new
@@ -975,7 +974,7 @@ describe "File" do
       end
     end
 
-    pending_win32 "#flock_exclusive soft blocking fiber" do
+    it "#flock_exclusive soft blocking fiber" do
       File.open(datapath("test_file.txt")) do |file1|
         File.open(datapath("test_file.txt")) do |file2|
           done = Channel(Nil).new
diff --git a/src/crystal/system/win32/file.cr b/src/crystal/system/win32/file.cr
index 0df1926cc047..bda41cde8b69 100644
--- a/src/crystal/system/win32/file.cr
+++ b/src/crystal/system/win32/file.cr
@@ -258,15 +258,54 @@ module Crystal::System::File
   end
 
   private def system_flock_shared(blocking : Bool) : Nil
-    raise NotImplementedError.new("File#flock_shared")
+    flock(false, blocking)
   end
 
   private def system_flock_exclusive(blocking : Bool) : Nil
-    raise NotImplementedError.new("File#flock_exclusive")
+    flock(true, blocking)
   end
 
   private def system_flock_unlock : Nil
-    raise NotImplementedError.new("File#flock_unlock")
+    unlock_file(windows_handle)
+  end
+
+  private def flock(exclusive, retry)
+    flags = LibC::LOCKFILE_FAIL_IMMEDIATELY
+    flags |= LibC::LOCKFILE_EXCLUSIVE_LOCK if exclusive
+
+    handle = windows_handle
+    if retry
+      until lock_file(handle, flags)
+        ::Fiber.yield
+      end
+    else
+      lock_file(handle, flags) || raise IO::Error.from_winerror("Error applying file lock: file is already locked")
+    end
+  end
+
+  private def lock_file(handle, flags)
+    # lpOverlapped must be provided despite the synchronous use of this method.
+    overlapped = LibC::OVERLAPPED.new
+    # lock the entire file with offset 0 in overlapped and number of bytes set to max value
+    if 0 != LibC.LockFileEx(handle, flags, 0, 0xFFFF_FFFF, 0xFFFF_FFFF, pointerof(overlapped))
+      true
+    else
+      winerror = WinError.value
+      if winerror == WinError::ERROR_LOCK_VIOLATION
+        false
+      else
+        raise IO::Error.from_winerror("LockFileEx", winerror)
+      end
+    end
+  end
+
+  private def unlock_file(handle)
+    # lpOverlapped must be provided despite the synchronous use of this method.
+    overlapped = LibC::OVERLAPPED.new
+    # unlock the entire file with offset 0 in overlapped and number of bytes set to max value
+    if 0 == LibC.UnlockFileEx(handle, 0, 0xFFFF_FFFF, 0xFFFF_FFFF, pointerof(overlapped))
+      raise IO::Error.from_winerror("UnLockFileEx")
+    end
   end
 
   private def system_fsync(flush_metadata = true) : Nil
diff --git a/src/lib_c/x86_64-windows-msvc/c/fileapi.cr b/src/lib_c/x86_64-windows-msvc/c/fileapi.cr
index 42533d369411..701a9590ca22 100644
--- a/src/lib_c/x86_64-windows-msvc/c/fileapi.cr
+++ b/src/lib_c/x86_64-windows-msvc/c/fileapi.cr
@@ -75,6 +75,21 @@ lib LibC
   fun FindNextFileW(hFindFile : HANDLE, lpFindFileData : WIN32_FIND_DATAW*) : BOOL
   fun FindClose(hFindFile : HANDLE) : BOOL
 
+  fun LockFileEx(
+    hFile : HANDLE,
+    dwFlags : DWORD,
+    dwReserved : DWORD,
+    nNumberOfBytesToLockLow : DWORD,
+    nNumberOfBytesToLockHigh : DWORD,
+    lpOverlapped : OVERLAPPED*
+  ) : BOOL
+  fun UnlockFileEx(
+    hFile : HANDLE,
+    dwReserved : DWORD,
+    nNumberOfBytesToUnlockLow : DWORD,
+    nNumberOfBytesToUnlockHigh : DWORD,
+    lpOverlapped : OVERLAPPED*
+  ) : BOOL
   fun SetFileTime(hFile : HANDLE, lpCreationTime : FILETIME*,
                   lpLastAccessTime : FILETIME*, lpLastWriteTime : FILETIME*) : BOOL
 end
diff --git a/src/lib_c/x86_64-windows-msvc/c/minwinbase.cr b/src/lib_c/x86_64-windows-msvc/c/minwinbase.cr
index d01b5232e387..cb56e3269de2 100644
--- a/src/lib_c/x86_64-windows-msvc/c/minwinbase.cr
+++ b/src/lib_c/x86_64-windows-msvc/c/minwinbase.cr
@@ -45,6 +45,9 @@ lib LibC
     GetFileExMaxInfoLevel
   end
 
+  LOCKFILE_FAIL_IMMEDIATELY = DWORD.new(0x00000001)
+  LOCKFILE_EXCLUSIVE_LOCK   = DWORD.new(0x00000002)
+
   STATUS_PENDING = 0x103
   STILL_ACTIVE   = STATUS_PENDING
 end