diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java
index 02ce79b18..4acacd28c 100644
--- a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java
+++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java
@@ -15,6 +15,7 @@
  */
 package org.teavm.dependency;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
@@ -109,6 +110,40 @@ class DependencyGraphBuilder {
                 }
             }
         }
+
+        if (method.hasModifier(ElementModifier.SYNCHRONIZED)) {
+            List<DependencyNode> syncNodes = new ArrayList<>();
+
+            MethodDependency methodDep = dependencyChecker.linkMethod(
+                        new MethodReference(Object.class, "monitorEnter", Object.class, void.class), null);
+            syncNodes.add(methodDep.getVariable(1));
+            methodDep.use();
+
+            methodDep = dependencyChecker.linkMethod(
+                    new MethodReference(Object.class, "monitorEnterSync", Object.class, void.class), null);
+            syncNodes.add(methodDep.getVariable(1));
+            methodDep.use();
+
+            methodDep = dependencyChecker.linkMethod(
+                    new MethodReference(Object.class, "monitorExit", Object.class, void.class), null);
+            syncNodes.add(methodDep.getVariable(1));
+            methodDep.use();
+
+            methodDep = dependencyChecker.linkMethod(
+                    new MethodReference(Object.class, "monitorExitSync", Object.class, void.class), null);
+            syncNodes.add(methodDep.getVariable(1));
+            methodDep.use();
+
+            if (method.hasModifier(ElementModifier.STATIC)) {
+                for (DependencyNode node : syncNodes) {
+                    node.propagate(dependencyChecker.getType("java.lang.Class"));
+                }
+            } else {
+                for (DependencyNode node : syncNodes) {
+                    nodes[0].connect(node);
+                }
+            }
+        }
     }
 
     private ExceptionConsumer createExceptionConsumer(MethodDependency methodDep, BasicBlockReader block) {
diff --git a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java
index 71349ba54..b0e285331 100644
--- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java
+++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java
@@ -383,6 +383,9 @@ public class Decompiler {
         if (modifiers.contains(ElementModifier.ENUM)) {
             result.add(NodeModifier.ENUM);
         }
+        if (modifiers.contains(ElementModifier.SYNCHRONIZED)) {
+            result.add(NodeModifier.SYNCHRONIZED);
+        }
         return result;
     }
 
diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java
index e9f5544a4..cd89dc1d6 100644
--- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java
+++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java
@@ -725,18 +725,43 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
                 writer.softNewLine();
                 writer.outdent().append("}").softNewLine();
 
+                if (methodNode.getModifiers().contains(NodeModifier.SYNCHRONIZED)) {
+                    writer.append("try").ws().append('{').indent().softNewLine();
+                }
                 writer.append(mainLoopName()).append(":").ws().append("while").ws().append("(true)")
                         .ws().append("{").ws();
                 writer.append("switch").ws().append("(").append(pointerName()).append(")").ws()
                         .append('{').softNewLine();
                 for (int i = 0; i < methodNode.getBody().size(); ++i) {
                     writer.append("case ").append(i).append(":").indent().softNewLine();
+                    if (i == 0 && methodNode.getModifiers().contains(NodeModifier.SYNCHRONIZED)) {
+                        writer.appendMethodBody(new MethodReference(Object.class, "monitorEnter",
+                                Object.class, void.class));
+                        writer.append("(");
+                        appendMonitor(methodNode);
+                        writer.append(");").softNewLine();
+                        emitSuspendChecker();
+                    }
                     AsyncMethodPart part = methodNode.getBody().get(i);
                     part.getStatement().acceptVisitor(Renderer.this);
                     writer.outdent();
                 }
                 writer.append("default:").ws().appendFunction("$rt_invalidPointer").append("();").softNewLine();
                 writer.append("}}").softNewLine();
+
+                if (methodNode.getModifiers().contains(NodeModifier.SYNCHRONIZED)) {
+                    writer.outdent().append("}").ws().append("finally").ws().append('{').indent().softNewLine();
+                    writer.append("if").ws().append("(!").appendFunction("$rt_suspending").append("())")
+                            .ws().append("{").indent().softNewLine();
+                    writer.appendMethodBody(new MethodReference(Object.class, "monitorExit",
+                            Object.class, void.class));
+                    writer.append("(");
+                    appendMonitor(methodNode);
+                    writer.append(");").softNewLine();
+                    writer.outdent().append('}').softNewLine();
+                    writer.outdent().append('}').softNewLine();
+                }
+
                 writer.appendFunction("$rt_nativeThread").append("().").append(pushName).append("(");
                 for (int i = firstToSave; i < variableCount; ++i) {
                     writer.append(variableName(i)).append(',').ws();
@@ -794,6 +819,15 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
         }
     }
 
+    private void appendMonitor(MethodNode methodNode) throws IOException {
+        if (methodNode.getModifiers().contains(NodeModifier.STATIC)) {
+            writer.appendFunction("$rt_cls").append("(")
+                    .appendClass(methodNode.getReference().getClassName()).append(")");
+        } else {
+            writer.append(variableName(0));
+        }
+    }
+
     private void pushLocation(NodeLocation location) {
         LocationStackEntry prevEntry = locationStack.peek();
         if (location != null) {
diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/NodeModifier.java b/teavm-core/src/main/java/org/teavm/javascript/ast/NodeModifier.java
index 724b060c0..70312473c 100644
--- a/teavm-core/src/main/java/org/teavm/javascript/ast/NodeModifier.java
+++ b/teavm-core/src/main/java/org/teavm/javascript/ast/NodeModifier.java
@@ -22,5 +22,6 @@ package org.teavm.javascript.ast;
 public enum NodeModifier {
     STATIC,
     INTERFACE,
-    ENUM
+    ENUM,
+    SYNCHRONIZED
 }
diff --git a/teavm-core/src/main/java/org/teavm/model/util/AsyncMethodFinder.java b/teavm-core/src/main/java/org/teavm/model/util/AsyncMethodFinder.java
index d17297756..da24ecbb8 100644
--- a/teavm-core/src/main/java/org/teavm/model/util/AsyncMethodFinder.java
+++ b/teavm-core/src/main/java/org/teavm/model/util/AsyncMethodFinder.java
@@ -72,6 +72,10 @@ public class AsyncMethodFinder {
                     if (asyncMethods.contains(method.getReference()) || method.getProgram() == null) {
                         continue;
                     }
+                    if (method.hasModifier(ElementModifier.SYNCHRONIZED)) {
+                        add(method.getReference());
+                        continue;
+                    }
                     ProgramReader program = method.getProgram();
                     AsyncInstructionReader insnReader = new AsyncInstructionReader();
                     for (int i = 0; i < program.basicBlockCount(); ++i) {