最近客户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
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();
}
}
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();
}
}
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();
}
}
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();
}
}
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();
}
}