最近客户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关键字和Lock接口实现原理 Java 中的 synchronized 关键字和 Lock 接口是两种常用的线程同步机制,它们都可以用来解决并发问题。下面我们将详细介绍 synchronized 关键字和 Lock 接口的实现原理。...
Java中的`synchronized`...总之,`synchronized`关键字在Java多线程编程中扮演着关键角色,确保了共享资源的安全访问,避免了竞态条件和数据不一致。理解和熟练运用`synchronized`对于编写健壮的并发程序至关重要。
Java 线程同步机制中 synchronized 关键字的理解 Java 的线程同步机制是为了解决多个线程共享同一片存储空间所带来的访问冲突问题。其中,synchronized 关键字是 Java 语言中解决这种冲突的重要机制。 ...
### Java synchronized 关键字原理与自定义锁实现详解 #### 一、Java synchronized 关键字原理 `synchronized` 是 Java 中的关键字之一,用于实现线程间的同步控制,确保共享资源的安全访问。它主要应用于以下两种...
java多线程中synchronized关键字的用法 解压密码 www.jiangyea.com
在多线程编程中,确保线程安全是至关重要的。Java提供了多种机制来处理并发问题,其中synchronized...通过深入理解synchronized关键字,开发者可以更好地处理Java中的并发问题,构建出更加健壮和高效的多线程应用程序。
`synchronized`关键字的实现依赖于Java虚拟机(JVM)的监视器锁(Monitor Lock)机制。每把锁都有一个拥有者(Owner)和一个计数器。当一个线程请求获取锁时,如果锁的计数器为0,则线程可以获取该锁,并成为该锁的拥有者...
但是,很多开发者对 synchronized 关键字的理解并不够深入,本文将通过实例解析 Java 中的 synchronized 关键字与线程平安问题,帮助开发者更好地理解和使用 synchronized 关键字。 首先,需要清晰的是 ...
synchronized 关键字和 Lock 都可以实现线程安全,但是它们有不同的使用场景和实现原理。synchronized 关键字是 Java 语言层面的同步机制,而 Lock 是 Java 库提供的同步机制。 synchronized 关键字的注意事项 在...
Java中的synchronized关键字是一种实现锁的方式,它是在JVM层面实现的非公平锁。synchronized关键字可以用于实现线程同步,以下是使用synchronized的四种方式: 1. 作用在方法上,保证了访问同一个对象的同一个方法...
本19页的Java多线程教程涵盖了Java并发编程的基础,包括线程的创建、状态转换、线程间通信、死锁避免、线程池以及`synchronized`关键字的详细解释和实例。通过学习,开发者将能够熟练掌握如何在多线程环境中正确使用...
本文深入探讨了Java中用于解决并发...通过对synchronized关键字的深入分析,本文为Java开发者提供了对并发编程中关键同步工具的全面理解,特别是在高并发场景下如何有效使用synchronized以确保线程安全和提高程序性能。
在Java并发编程中,Lock接口与synchronized关键字都是实现同步的重要工具。它们虽然都用于控制多线程对共享资源的访问,但在使用方式、功能特性及灵活性方面存在显著差异。 #### 二、使用方式对比 1. **...
Synchronized 关键字是 Java 并发编程中最基本的同步机制,它可以保证线程安全,包括原子性、可见性和有序性。Synchronized 关键字可以修饰方法或代码块,使得在同一时刻只有一个线程可以执行该方法或代码块,从而...
Java中的`synchronized`关键字是用于实现线程同步的关键机制,它的主要目的是确保在多线程环境中,对共享资源的访问能够保持数据的一致性和完整性。本文将深入探讨`synchronized`的两种主要用法:synchronized方法和...
通过`synchronized`关键字,开发者可以控制代码块或方法的并发访问,从而确保数据的一致性和程序的正确性。 #### 使用场景 1. **同步代码块**:可以通过`synchronized`关键字来声明同步代码块,即通过指定对象锁来...
Java 中的 synchronized 关键字是用于解决多线程并发问题的重要工具之一。它可以被用于方法、代码块和变量上,以实现对共享资源的互斥访问控制。本文将对 Java 中的 synchronized 用法进行详细的解释和分析。 一、...
总的来说,`synchronized`关键字是Java中解决并发问题的基本手段之一,它通过保证代码的原子性、可见性和有序性,确保了多线程环境下的数据安全性。但在实际开发中,我们需要根据具体场景权衡其性能和功能,选择最...
本文主要介绍了 Java 中的 synchronized 关键字的使用方法和原理,包括同步块、同步方法、非静态同步方法、静态同步方法等。通过本文的学习,读者可以更好地理解 synchronized 关键字的使用方法和原理,並且可以更好...
在 Java 中,synchronized 关键字可以作用于 instance 变量、object reference(对象引用)、static 函数和 class literals(类名称字面常量)身上。 Synchronized 关键字的作用是取得对象的锁,而不是把一段代码或...