C#
Java
Synchronized Keyword
Programming Languages
Code Conversion

C# version of java's synchronized keyword?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

C# does not have a direct synchronized keyword like Java, but the closest everyday equivalent is the lock statement. Both are built around monitor-style mutual exclusion, but the syntax and common usage patterns are different enough that a straight one-to-one translation can be misleading.

The Closest Match Is lock

In Java, synchronized can protect a method or a block using an object's monitor. In C#, the normal pattern is to choose an explicit lock object and protect only the critical section.

csharp
1using System;
2
3public class Account
4{
5    private readonly object _sync = new object();
6    private decimal _balance;
7
8    public void Deposit(decimal amount)
9    {
10        lock (_sync)
11        {
12            _balance += amount;
13        }
14    }
15
16    public decimal GetBalance()
17    {
18        lock (_sync)
19        {
20            return _balance;
21        }
22    }
23}

This is the C# form most similar in effect to a synchronized block in Java.

Why lock Is Usually Better Than Method-Level Synchronization

Java allows this style:

java
public synchronized void deposit(int amount) {
    balance += amount;
}

C# does not have that exact method modifier. Instead, you write the critical section explicitly. That is often an improvement because it makes the locked region visible and easier to minimize.

A smaller critical section usually means less contention and fewer accidental performance problems.

lock Uses Monitor Under the Hood

The lock statement is syntax sugar around Monitor.Enter and Monitor.Exit.

csharp
1using System;
2using System.Threading;
3
4public class Counter
5{
6    private readonly object _sync = new object();
7    private int _value;
8
9    public void Increment()
10    {
11        Monitor.Enter(_sync);
12        try
13        {
14            _value++;
15        }
16        finally
17        {
18            Monitor.Exit(_sync);
19        }
20    }
21}

You usually do not write it this way unless you need advanced monitor features such as timed lock acquisition with Monitor.TryEnter. For normal mutual exclusion, lock is clearer.

Do Not Lock on this or Public Objects

A very important C# guideline is to lock on a private object dedicated to synchronization.

Bad patterns include:

  • 'lock(this)'
  • 'lock(typeof(MyClass))'
  • 'lock on a public field or on a string'

Those choices expose your synchronization mechanism to code outside your control, which can create accidental deadlocks or unpredictable lock contention.

This is why the earlier _sync field is private and readonly.

Reentrancy Behavior

Java's synchronized is reentrant, and so is C# lock. That means the same thread can enter the same monitor more than once without deadlocking itself.

This matters when one locked method calls another method that uses the same lock object. The code still requires design care, but the monitor itself is reentrant.

Other Synchronization Tools in C#

lock is the closest semantic match to synchronized, but it is not the only tool.

Common alternatives include:

  • 'Monitor for lower-level control'
  • 'Mutex for cross-process mutual exclusion'
  • 'SemaphoreSlim when limited parallel access is needed'
  • 'ReaderWriterLockSlim when reads greatly outnumber writes'
  • 'Interlocked for simple atomic updates such as counters'

For example, a simple increment does not need a full lock if atomic arithmetic is enough.

csharp
1using System.Threading;
2
3int counter = 0;
4Interlocked.Increment(ref counter);

That is often a better choice than lock for very small atomic state changes.

async Changes the Picture

One major difference from older Java-style locking discussions is async code. You cannot use await inside a normal lock block in the same straightforward way you use synchronized in synchronous code.

For async coordination, other patterns such as SemaphoreSlim are often more appropriate.

csharp
1using System;
2using System.Threading;
3using System.Threading.Tasks;
4
5public class AsyncGate
6{
7    private readonly SemaphoreSlim _gate = new SemaphoreSlim(1, 1);
8
9    public async Task RunAsync()
10    {
11        await _gate.WaitAsync();
12        try
13        {
14            await Task.Delay(100);
15        }
16        finally
17        {
18            _gate.Release();
19        }
20    }
21}

So the closest answer to Java's synchronized is still lock, but modern C# also requires you to think about whether the code path is synchronous or asynchronous.

Common Pitfalls

A common mistake is translating Java synchronized directly into lock(this). That is usually the wrong locking target in C#.

Another mistake is locking too much code instead of protecting only the shared-state critical section.

Developers also sometimes use lock for work that should have used Interlocked or SemaphoreSlim instead.

Finally, do not forget that async code needs different coordination patterns. A monitor lock is not the right tool for every concurrency problem.

Summary

  • The closest C# equivalent to Java's synchronized is the lock statement.
  • Use a private dedicated lock object rather than this or a public object.
  • 'lock is built on Monitor and is reentrant, like Java monitors.'
  • Use other primitives such as Interlocked or SemaphoreSlim when the problem calls for them.
  • Pick the synchronization tool based on the concurrency pattern, not just on surface syntax similarity.

Course illustration
Course illustration

All Rights Reserved.