Index: org/armedbear/lisp/compiler-pass1.lisp
===================================================================
--- org/armedbear/lisp/compiler-pass1.lisp	(revision 12028)
+++ org/armedbear/lisp/compiler-pass1.lisp	(working copy)
@@ -304,6 +304,22 @@
     (setf (block-form block) result)
     block))
 
+(defun p1-threads-synchronized-on (form)
+  (let* ((synchronized-object (p1 (cadr form)))
+         (body (cddr form))
+         (block (make-block-node '(THREADS:SYNCHRONIZED-ON)))
+         (*blocks* (cons block *blocks*))
+         result)
+    (dolist (subform body)
+      (let ((op (and (consp subform) (%car subform))))
+        (push (p1 subform) result)
+        (when (memq op '(GO RETURN-FROM THROW))
+          (return))))
+    (setf (block-form block)
+          (list* 'threads:synchronized-on synchronized-object
+                 (nreverse result)))
+    block))
+
 (defun p1-unwind-protect (form)
   (if (= (length form) 2)
       (p1 (second form)) ; No cleanup forms: (unwind-protect (...)) => (...)
@@ -1040,7 +1056,9 @@
                   (THE                  p1-the)
                   (THROW                p1-throw)
                   (TRULY-THE            p1-truly-the)
-                  (UNWIND-PROTECT       p1-unwind-protect)))
+                  (UNWIND-PROTECT       p1-unwind-protect)
+                  (THREADS:SYNCHRONIZED-ON
+                                        p1-threads-synchronized-on)))
     (install-p1-handler (%car pair) (%cadr pair))))
 
 (initialize-p1-handlers)
Index: org/armedbear/lisp/compiler-pass2.lisp
===================================================================
--- org/armedbear/lisp/compiler-pass2.lisp	(revision 12028)
+++ org/armedbear/lisp/compiler-pass2.lisp	(working copy)
@@ -1131,6 +1131,8 @@
                  177 ; return
                  190 ; arraylength
                  191 ; athrow
+                 194 ; monitorenter
+                 195 ; monitorexit
                  198 ; ifnull
                  202 ; label
                  ))
@@ -7680,6 +7682,34 @@
         (label LABEL2)
         (emit-move-from-stack target representation)))))
 
+(defknown p2-threads-synchronized-on (t t) t)
+(defun p2-threads-synchronized-on (block target)
+  (let ((form (block-form block))
+        (*register* *register*)
+        (object-register (allocate-register))
+        (BEGIN-PROTECTED-RANGE (gensym))
+        (END-PROTECTED-RANGE (gensym))
+        (EXIT (gensym)))
+    (compile-form (cadr form) object-register nil) ; value to synchronize
+    (aload object-register)
+    (emit 'monitorenter)
+    (label BEGIN-PROTECTED-RANGE)
+    (compile-progn-body (cddr form) target)
+    (emit 'goto EXIT)
+    (label END-PROTECTED-RANGE)
+    (aload object-register)
+    (emit 'monitorexit)
+    (emit 'athrow)
+
+    (label EXIT)
+    (aload object-register)
+    (emit 'monitorexit)
+    (push (make-handler :from BEGIN-PROTECTED-RANGE
+                        :to END-PROTECTED-RANGE
+                        :code END-PROTECTED-RANGE
+                        :catch-type 0) *handlers*)))
+
+
 (defknown p2-catch-node (t t) t)
 (defun p2-catch-node (block target)
   (let ((form (block-form block)))
@@ -7885,6 +7915,9 @@
                 (fix-boxing representation nil))
                ((equal (block-name form) '(PROGV))
                 (p2-progv-node form target representation))
+               ((equal (block-name form) '(THREADS:SYNCHRONIZED-ON))
+                (p2-threads-synchronized-on form target)
+                (fix-boxing representation nil))
                (t
                 (p2-block-node form target representation))))
         ((constantp form)
Index: org/armedbear/lisp/jvm.lisp
===================================================================
--- org/armedbear/lisp/jvm.lisp	(revision 12028)
+++ org/armedbear/lisp/jvm.lisp	(working copy)
@@ -420,7 +420,8 @@
 "
   (let ((name (block-name object)))
     (or (equal name '(CATCH))
-        (equal name '(UNWIND-PROTECT)))))
+        (equal name '(UNWIND-PROTECT))
+        (equal name '(THREADS:SYNCHRONIZED-ON)))))
 
 
 (defknown enclosed-by-protected-block-p (&optional t) boolean)
Index: org/armedbear/lisp/Lisp.java
===================================================================
--- org/armedbear/lisp/Lisp.java	(revision 12028)
+++ org/armedbear/lisp/Lisp.java	(working copy)
@@ -78,6 +78,8 @@
     Packages.createPackage("JAVA");
   public static final Package PACKAGE_LISP =
     Packages.createPackage("LISP");
+  public static final Package PACKAGE_THREADS =
+    Packages.createPackage("THREADS");
 
   // ### nil
   public static final LispObject NIL = Nil.NIL;
@@ -103,6 +105,7 @@
         PACKAGE_TPL.usePackage(PACKAGE_EXT);
         PACKAGE_EXT.addNickname("EXT");
         PACKAGE_EXT.usePackage(PACKAGE_CL);
+        PACKAGE_EXT.usePackage(PACKAGE_THREADS);
         PACKAGE_JVM.usePackage(PACKAGE_CL);
         PACKAGE_JVM.usePackage(PACKAGE_EXT);
         PACKAGE_JVM.usePackage(PACKAGE_SYS);
Index: org/armedbear/lisp/LispThread.java
===================================================================
--- org/armedbear/lisp/LispThread.java	(revision 12030)
+++ org/armedbear/lisp/LispThread.java	(working copy)
@@ -1012,7 +1012,7 @@
 
     // ### make-thread
     private static final Primitive MAKE_THREAD =
-        new Primitive("make-thread", PACKAGE_EXT, true, "function &key name")
+        new Primitive("make-thread", PACKAGE_THREADS, true, "function &key name")
     {
         @Override
         public LispObject execute(LispObject[] args) throws ConditionThrowable
@@ -1038,7 +1038,7 @@
 
     // ### threadp
     private static final Primitive THREADP =
-        new Primitive("threadp", PACKAGE_EXT, true, "object",
+        new Primitive("threadp", PACKAGE_THREADS, true, "object",
 		      "Boolean predicate as whether OBJECT is a thread.")
     {
         @Override
@@ -1050,7 +1050,7 @@
 
     // ### thread-alive-p
     private static final Primitive THREAD_ALIVE_P =
-        new Primitive("thread-alive-p", PACKAGE_EXT, true, "thread",
+        new Primitive("thread-alive-p", PACKAGE_THREADS, true, "thread",
 		      "Boolean predicate whether THREAD is alive.")
     {
         @Override
@@ -1069,7 +1069,7 @@
 
     // ### thread-name
     private static final Primitive THREAD_NAME =
-        new Primitive("thread-name", PACKAGE_EXT, true, "thread",
+        new Primitive("thread-name", PACKAGE_THREADS, true, "thread",
 		      "Return the name of THREAD if it has one.")
     {
         @Override
@@ -1113,7 +1113,7 @@
 
     // ### mapcar-threads
     private static final Primitive MAPCAR_THREADS =
-        new Primitive("mapcar-threads", PACKAGE_EXT, true, "function",
+        new Primitive("mapcar-threads", PACKAGE_THREADS, true, "function",
 		      "Applies FUNCTION to all existing threads.")
     {
         @Override
@@ -1134,7 +1134,7 @@
 
     // ### destroy-thread
     private static final Primitive DESTROY_THREAD =
-        new Primitive("destroy-thread", PACKAGE_EXT, true, "thread", 
+        new Primitive("destroy-thread", PACKAGE_THREADS, true, "thread", 
 		      "Mark THREAD as destroyed.")
     {
         @Override
@@ -1158,7 +1158,7 @@
     // multiple interrupts are queued for a thread, they are all run, but the
     // order is not guaranteed.
     private static final Primitive INTERRUPT_THREAD =
-        new Primitive("interrupt-thread", PACKAGE_EXT, true,
+        new Primitive("interrupt-thread", PACKAGE_THREADS, true,
 		      "thread function &rest args",
 		      "Interrupts THREAD and forces it to apply FUNCTION to ARGS.\nWhen the function returns, the thread's original computation continues. If  multiple interrupts are queued for a thread, they are all run, but the order is not guaranteed.")
     {
@@ -1185,7 +1185,7 @@
 
     // ### current-thread
     private static final Primitive CURRENT_THREAD =
-        new Primitive("current-thread", PACKAGE_EXT, true, "",
+        new Primitive("current-thread", PACKAGE_THREADS, true, "",
 		      "Returns a reference to invoking thread.")
     {
         @Override
@@ -1197,7 +1197,7 @@
 
     // ### backtrace-as-list
     private static final Primitive BACKTRACE_AS_LIST =
-        new Primitive("backtrace-as-list", PACKAGE_EXT, true, "",
+        new Primitive("backtrace-as-list", PACKAGE_THREADS, true, "",
 		      "Returns a backtrace of the invoking thread as a list.")
     {
         @Override
@@ -1222,4 +1222,101 @@
             return use_fast_calls ? T : NIL;
         }
     };
+
+    // ### synchronized-on
+    private static final SpecialOperator SYNCHRONIZED_ON =
+        new SpecialOperator("synchronized-on", PACKAGE_THREADS, true,
+                            "form &body body")
+    {
+        @Override
+        public LispObject execute(LispObject args, Environment env)
+            throws ConditionThrowable
+        {
+          if (args == NIL)
+            return error(new WrongNumberOfArgumentsException(this));
+
+          LispThread thread = LispThread.currentThread();
+          synchronized (eval(args.car(), env, thread)) {
+              return progn(args.cdr(), env, thread);
+          }
+        }
+    };
+
+    // ### object-wait
+    private static final Primitive OBJECT_WAIT =
+        new Primitive("object-wait", PACKAGE_THREADS, true,
+                      "object &optional timeout")
+    {
+        @Override
+        public LispObject execute(LispObject object)
+            throws ConditionThrowable
+        {
+            try {
+                object.wait();
+            }
+            catch (InterruptedException e) {
+                currentThread().processThreadInterrupts();
+            }
+            catch (IllegalMonitorStateException e) {
+                return error(new IllegalMonitorState());
+            }
+            return NIL;
+        }
+
+        @Override
+        public LispObject execute(LispObject object, LispObject timeout)
+            throws ConditionThrowable
+        {
+            try {
+                object.wait(javaSleepInterval(timeout));
+            }
+            catch (InterruptedException e) {
+                currentThread().processThreadInterrupts();
+            }
+            catch (IllegalMonitorStateException e) {
+                return error(new IllegalMonitorState());
+            }
+            return NIL;
+        }
+    };
+
+    // ### object-notify
+    private static final Primitive OBJECT_NOTIFY =
+        new Primitive("object-notify", PACKAGE_THREADS, true,
+                      "object")
+    {
+        @Override
+        public LispObject execute(LispObject object)
+            throws ConditionThrowable
+        {
+            try {
+                object.notify();
+            }
+            catch (IllegalMonitorStateException e) {
+                return error(new IllegalMonitorState());
+            }
+            return NIL;
+        }
+    };
+
+    // ### object-notify-all
+    private static final Primitive OBJECT_NOTIFY_ALL =
+        new Primitive("object-notify-all", PACKAGE_THREADS, true,
+                      "object")
+    {
+        @Override
+        public LispObject execute(LispObject object)
+            throws ConditionThrowable
+        {
+            try {
+                object.notifyAll();
+            }
+            catch (IllegalMonitorStateException e) {
+                return error(new IllegalMonitorState());
+            }
+            return NIL;
+        }
+    };
+
+
 }
Index: org/armedbear/lisp/opcodes.lisp
===================================================================
--- org/armedbear/lisp/opcodes.lisp	(revision 12028)
+++ org/armedbear/lisp/opcodes.lisp	(working copy)
@@ -246,8 +246,8 @@
 (define-opcode athrow 191 1 0)
 (define-opcode checkcast 192 3 0)
 (define-opcode instanceof 193 3 0)
-(define-opcode monitorenter 194 1 nil)
-(define-opcode monitorexit 195 1 nil)
+(define-opcode monitorenter 194 1 -1)
+(define-opcode monitorexit 195 1 -1)
 (define-opcode wide 196 0 nil)
 (define-opcode multianewarray 197 4 nil)
 (define-opcode ifnull 198 3 -1)
Index: org/armedbear/lisp/precompiler.lisp
===================================================================
--- org/armedbear/lisp/precompiler.lisp	(revision 12028)
+++ org/armedbear/lisp/precompiler.lisp	(working copy)
@@ -488,6 +488,9 @@
               res))
         (cons 'PROGN (mapcar #'precompile1 body)))))
 
+(defun precompile-threads-synchronized-on (form)
+  (cons 'threads:synchronized-on (mapcar #'precompile1 (cdr form))))
+
 (defun precompile-progv (form)
   (if (< (length form) 3)
       (compiler-error "Not enough arguments for ~S." 'progv)
@@ -993,7 +996,10 @@
                   (QUOTE                precompile-identity)
                   (THE                  precompile-the)
                   (THROW                precompile-cons)
-                  (TRULY-THE            precompile-truly-the)))
+                  (TRULY-THE            precompile-truly-the)
+
+                  (THREADS:SYNCHRONIZED-ON
+                                        precompile-threads-synchronized-on)))
     (install-handler (first pair) (second pair))))
 
 (install-handlers)
Index: org/armedbear/lisp/Symbol.java
===================================================================
--- org/armedbear/lisp/Symbol.java	(revision 12030)
+++ org/armedbear/lisp/Symbol.java	(working copy)
@@ -3038,6 +3038,9 @@
   public static final Symbol _INSPECTOR_HOOK_ =
     PACKAGE_EXT.addExternalSymbol("*INSPECTOR-HOOK*");
 
-  public static final Symbol COMPILER_LET=
+  public static final Symbol COMPILER_LET =
     PACKAGE_LISP.addExternalSymbol("COMPILER-LET");
+
+  public static final Symbol SYNCHRONIZED_ON =
+    PACKAGE_THREADS.addExternalSymbol("SYNCHRONIZED-ON");
 }
