Does ruby 3.3 have an implicit mutex synchronization?

so I have a code example like this

counters = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
threads =  do
   do
    100000.times do
      counters.map! { |counter| counter + 1 }
    end
  end
end
threads.each(&:join)
puts counters.to_s5.times.mapThread.new

when I run this code in ruby 3.3 I always get
[500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000]

but if I ran same code in ruby less than 3.3 so ruby 3.2, 3.1, 2.7
I don't get the right result
[500000, 500000, 500000, 500000, 500000, 500000, 400000, 500000, 500000, 500000]

to get the right result I have to use mutex.

so my question is what changed in ruby 3.3?

BTW I was following this article https://vaneyckt.io/posts/ruby_concurrency_in_praise_of_the_mutex/ and on ruby 3.3 atomicity.rb and visibility.rb both works fine without mutex(it like ruby 3.3 have some implicit mutex built-in)

BTW I've tested on 2 different machines

  1. MacBook Pro M1 Pro running MacOS
  2. MacBook Pro 16 2019 Intel running Ubuntu 22.04

Edit: if I add an extra zero then it breaks the functionality even on ruby 3.3. so there is no implicit mutex and there some optimization in the ruby 3.3 that was creating an illusion of implicit mutex when thread have very little data to work on.