首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

Java synchronized 关键字跟Lock的随笔

2012-08-27 
Java synchronized 关键字和Lock的随笔最近客户300个人同时按下一个按钮,在执行到一个业务模块的时候出现

Java synchronized 关键字和Lock的随笔

最近客户300个人同时按下一个按钮,在执行到一个业务模块的时候出现了脏读。

?

package org.test.thread;public class Worker {public void executeJob() {// statement A check()....// statement B....// dao.save();}}
?

?

比如上面的代码,第一个线程走到B了,第二个线程走到了A,check的地方。比如重复性check。如果第一个线程和第二个线程的查询主键相同。那么当线程1走到dao.save的地方,线程2刚好跳过check,也就是check无效化了。

?

于是,考虑用同步来解决。

?

到JDK5为止,java中实现线程安全起码有3种方法,ThreadLocal,synchronized关键字,Lock。?

?

ThreadLocal是一种用空间换时间的策略。即为每一个线程创建副本。不适用于我们现在碰到的问题。所以不展开。

?

synchronized关键字:

在java中,他可以是方法修饰符,也可以成为独立的同步块。而加在方法前时,有两种方法,一般的方法和同步方法。两种效果不同。如:

如下这种,

?

package org.test.thread;public class Worker {public synchronized void executeJob() {}}

?

?相当于

?

?

package org.test.thread;public class Worker {public void executeJob() {synchronized(this) {}}}
?

而加在静态方法前

?

?

package org.test.thread;public class Worker {public static synchronized void executeJob() {}}

?则相当于

?

package org.test.thread;public class Worker {public void executeJob() {synchronized(this.getClass()) {}}}

?虽然我不推荐你这么认为。因为静态方法加同步和一般方法中用同步块锁定类,这两种方法的用法是完全不一样的,虽然他们锁定的对象是一样的(Class)。

?

要弄清楚他的锁定对象,我们来做个实验:

?

package org.test.thread;public class Worker {public synchronized void executeA(String name) {for (int i = 0; i < 10; i++) {System.out.println(name + "-executeA-" + i);try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}public synchronized void executeB(String name) {for (int i = 0; i < 10; i++) {System.out.println(name + "-executeB-" + i);try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}public synchronized static void executeC(String name) {for (int i = 0; i < 10; i++) {System.out.println(name + "-executeC-" + i);try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}private class SynchronizerWorkerA extends Thread {public void run() {executeA("thread1");}}private class SynchronizerWorkerB extends Thread {public void run() {executeB("thread2");}}private class SynchronizerWorkerC extends Thread {public void run() {executeC("thread3");}}public static void main(String args[]) {Worker worker = new Worker();worker.new SynchronizerWorkerA().start();worker.new SynchronizerWorkerB().start();worker.new SynchronizerWorkerC().start();}}

?

结果:

?

thread1-executeA-0

thread3-executeC-0

thread1-executeA-1

thread3-executeC-1

thread1-executeA-2

thread3-executeC-2

thread1-executeA-3

thread3-executeC-3

thread1-executeA-4

thread3-executeC-4

thread1-executeA-5

thread3-executeC-5

thread1-executeA-6

thread3-executeC-6

thread1-executeA-7

thread3-executeC-7

thread1-executeA-8

thread3-executeC-8

thread1-executeA-9

thread3-executeC-9

thread2-executeB-0

thread2-executeB-1

thread2-executeB-2

thread2-executeB-3

thread2-executeB-4

thread2-executeB-5

thread2-executeB-6

thread2-executeB-7

thread2-executeB-8

thread2-executeB-9


可以看到,A方法和C方法可以同时调用,而B则被锁着,直到A方法的锁释放后才能被调用。原因在于静态方法的锁锁定class,而一般方法的锁锁定对象(效果上可以这么认为)
让我们把代码修改一下:
package org.test.thread;public class Worker {public synchronized void executeA(String name) {for (int i = 0; i < 10; i++) {System.out.println(name + "-" + i);try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}public synchronized static void executeB(String name) {for (int i = 0; i < 10; i++) {System.out.println(name + "-" + i);try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}private class SynchronizerWorkerA extends Thread {private String name = null;public SynchronizerWorkerA(String name) {this.name = name;}public void run() {executeA(name);}}private class SynchronizerWorkerB extends Thread {private String name = null;public SynchronizerWorkerB(String name) {this.name = name;}public void run() {executeB(name);}}public static void main(String args[]) {new Worker().new SynchronizerWorkerA("thread1").start();new Worker().new SynchronizerWorkerA("thread2").start();new Worker().new SynchronizerWorkerB("thread3").start();new Worker().new SynchronizerWorkerB("thread4").start();}}
?知道有什么区别吗?我们在一个类的不同对象之间用了同步。结果:thread1-0thread2-0thread3-0thread1-1thread2-1thread3-1thread1-2thread2-2thread3-2thread3-3thread2-3thread1-3thread3-4thread2-4thread1-4thread1-5thread3-5thread2-5thread2-6thread1-6thread3-6thread1-7thread2-7thread3-7thread1-8thread3-8thread2-8thread1-9thread3-9thread2-9thread4-0thread4-1thread4-2thread4-3thread4-4thread4-5thread4-6thread4-7thread4-8thread4-9
可以看到,一般方法的同步在不同对象之间没有效果。而静态方法则有作用。(锁定的方式不一样)
理解了这点,我们来看最后一种,Lock
Lock:JDK5提供了吞吐量比synchronized更好的方法-----Lock接口。他的实现类有两个
让我们看如下代码:
package org.test.thread;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class Worker extends Thread {private final Lock reentrantLock = new ReentrantLock();private String name;public Worker(String name) {this.name = name;}public void executeJob() {try {reentrantLock.lock();for (int i = 0; i < 10; i++) {System.out.println(name + "-" + i);try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}} finally {reentrantLock.unlock();}}public void run() {executeJob();}public static void main(String args[]) {new Worker("thread-1").start();new Worker("thread-2").start();}}
?结果:thread-1-0thread-2-0thread-1-1thread-2-1thread-1-2thread-2-2thread-1-3thread-2-3thread-2-4thread-1-4thread-1-5thread-2-5thread-1-6thread-2-6thread-1-7thread-2-7thread-1-8thread-2-8thread-1-9thread-2-9
可以看到,lock没有起作用,这是为啥?原因在于我们锁定了不同对象。让我们将代码稍微改改
package org.test.thread;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class Worker {private final Lock reentrantLock = new ReentrantLock();public void executeJob(String name) {try {reentrantLock.lock();for (int i = 0; i < 10; i++) {System.out.println(name + "-" + i);try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}} finally {reentrantLock.unlock();}}private class WorkerThread extends Thread {private String name = null;public WorkerThread(String name) {this.name = name;}public void run() {executeJob(name);}}public static void main(String args[]) {Worker worker = new Worker();worker.new WorkerThread("thread1").start();worker.new WorkerThread("thread2").start();}}
?结果为:thread1-0thread1-1thread1-2thread1-3thread1-4thread1-5thread1-6thread1-7thread1-8thread1-9thread2-0thread2-1thread2-2thread2-3thread2-4thread2-5thread2-6thread2-7thread2-8thread2-9看起作用了吧。或者将原代码的lock对象改为static的
package org.test.thread;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class Worker extends Thread {private final static Lock reentrantLock = new ReentrantLock();private String name;public Worker(String name) {this.name = name;}public void executeJob() {try {reentrantLock.lock();for (int i = 0; i < 10; i++) {System.out.println(name + "-" + i);try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}} finally {reentrantLock.unlock();}}public void run() {executeJob();}public static void main(String args[]) {new Worker("thread-1").start();new Worker("thread-2").start();}}
?结果同上
当然,我们也可以用synchronized轻松实现Lock接口所带来的功能:
package org.test.thread;public class Worker extends Thread {private static Object reentrantLock = new Object();private String name;public Worker(String name) {this.name = name;}public void executeJob() {synchronized (reentrantLock) {for (int i = 0; i < 10; i++) {System.out.println(name + "-" + i);try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}}public void run() {executeJob();}public static void main(String args[]) {new Worker("thread-1").start();new Worker("thread-2").start();}}
?Lock与synchronized的区别(仅列出主要的)1.当块被同步,如果不想等了,synchronized无法停止等待,也没办法得到锁,而Lock可以用tryLock方法轻松做到.2.性能上Lock更优添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能

热点排行