Java线程同步
两个或两个以上线程要共享资源时,必须保证某一刻只有一条线程占用。这个过程叫同步(synchronization)。Java 在语言层面提供了支持。
关键概念是管程(也叫信号量 semaphore):一个互斥独占锁定,或称互斥体(mutex)。同一时刻只有一条线程能获得管程;其他试图进入已锁定管程的线程必须挂起等待。拥有管程的线程可以再次进入同一个管程。
C / C++ 往往要调操作系统原语,用起来别扭。Java 用 synchronized 把大部分复杂性收掉。同步代码有两种写法:同步方法和同步语句块。
| 写法 | 何时用 |
|---|---|
| synchronized 方法 | 类是你写的,整段方法都要互斥 |
| synchronized (obj) { … } | 第三方类没有同步方法,又拿不到源码 |
一、使用同步方法
每个对象都有隐式管程。进入它就是调用被 synchronized 修饰的方法。一条线程在同步方法里时,其他试图调用该实例同一(或其他)同步方法的线程必须等。从同步方法返回即退出管程。
先看一个该同步却没同步的例子。Callme.call 在方括号里打印字符串,中间 sleep 1 秒,故意把时间片让出去。三个 Caller 共用同一个 Callme 实例:
没有同步时输出会搅在一起,例如 Hello[Synchronized[World] 再跟几个孤立的 ]。这是竞争:三条线程抢着完成同一方法。sleep 只是让现象稳定复现;真实竞争更复杂、更不可预知。
在 call 前加上 synchronized 后,某一时刻只有一条线程能进 call,输出变成:
多线程下凡是操纵对象内部状态的方法,都要用 synchronized 防竞争。一条线程进入某实例的同步方法后,其他线程不能再进该实例的同步方法;但该实例上未同步的方法仍可被调用。
二、同步语句块
类不是你写的、方法也没加 synchronized 时,把调用放进同步块即可:
object 是被同步对象的引用。只有一条语句时花括号可省略。同步块保证:只有当前线程成功进入 object 的管程后,才能调它的成员方法。把 call 保持非同步,在 Caller.run 里锁 target,效果与给 call 加 synchronized 相同:
一句话总结:共享对象用管程互斥;能改源码就给方法加 synchronized,改不了就把调用包进 synchronized(obj);未同步方法挡不住竞争。
转载请注明来源:Java线程同步






