最近客户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-0
thread2-0
thread3-0
thread1-1
thread2-1
thread3-1
thread1-2
thread2-2
thread3-2
thread3-3
thread2-3
thread1-3
thread3-4
thread2-4
thread1-4
thread1-5
thread3-5
thread2-5
thread2-6
thread1-6
thread3-6
thread1-7
thread2-7
thread3-7
thread1-8
thread3-8
thread2-8
thread1-9
thread3-9
thread2-9
thread4-0
thread4-1
thread4-2
thread4-3
thread4-4
thread4-5
thread4-6
thread4-7
thread4-8
thread4-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-0
thread-2-0
thread-1-1
thread-2-1
thread-1-2
thread-2-2
thread-1-3
thread-2-3
thread-2-4
thread-1-4
thread-1-5
thread-2-5
thread-1-6
thread-2-6
thread-1-7
thread-2-7
thread-1-8
thread-2-8
thread-1-9
thread-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-0
thread1-1
thread1-2
thread1-3
thread1-4
thread1-5
thread1-6
thread1-7
thread1-8
thread1-9
thread2-0
thread2-1
thread2-2
thread2-3
thread2-4
thread2-5
thread2-6
thread2-7
thread2-8
thread2-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更优
添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能
分享到:
相关推荐
java同步synchronized关键字用法示例
主要介绍了Java synchronized关键字和Lock接口实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
java synchronized关键字原理、自定义一把锁来实现同步等
java多线程中synchronized关键字的用法 解压密码 www.jiangyea.com
synchronized关键字在java中的重要性 以及常用的方法 还有它的详解
Lock接口与synchronized关键字在Java并发编程中都是用于实现同步机制的重要工具,但它们在使用方式、功能特性以及灵活性等方面存在一些显著的差异。 首先,从使用方式上来看,synchronized是Java语言内置的关键字,...
java基本教程之synchronized关键字java多线程教程共19页.pdf.zip
Java多线程synchronized关键字详解(六)共5页.pdf.zip
java的线程同步机制synchronized关键字的理解_.docx
Synchronized关键字的用法
主要介绍了Java synchronized关键字使用方式及特性解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
【Java并发编程】Synchronized关键字实现原理.doc
主要介绍了析JAVA Synchronized关键字的相关知识,文中代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下
实例解析Java中的synchronized关键字与线程平安问题_.docx
主要介绍了Java中synchronized关键字的用法,针对synchronized修饰方法的使用作出了简单讲解和演示,需要的朋友可以参考下
【ASP.NET编程知识】实例解析Java中的synchronized关键字与线程安全问题.docx
主要介绍了简单了解Java synchronized关键字同步,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
线程安全,使用synchronized关键字
synchronized关键字我们大家都知道是线程同步关键字.总结一下日常的使用方法,还有一个坑.