- 浏览: 1115979 次
文章分类
- 全部博客 (379)
- S2SH (16)
- stuts2 (0)
- java语言 (81)
- JSP (17)
- <html>元素 (11)
- javaweb (4)
- web容器 (3)
- ext (23)
- javaScript (48)
- ant (1)
- liferay (1)
- sql (9)
- css (42)
- 浏览器设置 (3)
- office_world (1)
- eclipse (4)
- 其它 (28)
- 操作系统 (5)
- android (6)
- Struts2 (11)
- RegEx (3)
- mysql (5)
- BigDATA (1)
- Node.js (1)
- Algorithm (10)
- Apache Spark (1)
- 数据库 (5)
- linux (2)
- git (1)
- Adobe (3)
- java语言,WebSocket (1)
- Maven (3)
- SHELL (1)
- XML (2)
- 数学 (2)
- Python (2)
- Java_mysql (1)
- ReactJS (6)
- 养生 (4)
- Docker (1)
- Protocols (3)
- java8 (2)
- 书籍 (1)
- Gradle (2)
- AngularJS (5)
- SpringMVC (2)
- SOAP (1)
- BootstrapCSS (1)
- HTTP协议 (1)
- OAuth2 (1)
最新评论
-
Lixh1986:
Java并发编程:自己动手写一把可重入锁https://blo ...
Java之多线程之Lock与Condition -
Lixh1986:
http://win.51apps.com.cn/https: ...
temp -
ztwsl:
不错,支持很好
HttpServletRequest和ServletRequest的区别 -
guodongkai:
谢谢您能将知识精华汇编总结,让初学者们从原理中学会和提高。
javaScript之function定义 -
kangwen23:
谢谢了,顶顶
struts2中的ValueStack学习
Java之线程同步与安全(Thread Synchronize & Safe)
一、问题背景
Java语言提供了多线程的功能。
多线程创建于相同的Object,多线程间共享Object的变量或属性。
但是,当线程对共享的数据进行读写时,会导致数据的不一致(data inconsistency)。
二、线程同步情景分析
数据不一致的原因是由数据操作的非原子性引起的。
即:更新任何属性或变量,非一步完成,而是需要三部:
1、读取现在的值。
2、进行必要的操作以得到要更新的值。
3、把更新的值写入到引用的变量或属性中。
来看一个简单的例子:
多个线程共享一个数据,并对其进行修改。
上述循环中,使用两个线程对 count 变量的值进行增加,每个线程各增加四次。
最后 count 的值应该是 8。但是在实际的多次运行中,它的值在 6,7,8 之间。
发生这种情况,即使 count++ 看起来是单原子操作,实际不是。
从而导致数据不一致。
三、Java中保证线程同步的方法
为了在多线程运行环境中确保数据的一致,Java提供了一些方法。
下面是主要的几种:
1、使用 synchronized 关键字(最广泛使用)
2、使用 java.util.concurrent.atomic 包下的原子操作包装类。
例如: AtomicInteger
3、使用 java.util.concurrent.locks 包下的锁类
4、使用 线程安全的集合类。
例如: ConcurrentHashMap
5、使用 volatile 关键字确保读的一致性。(不能确保写一致)
确保每个线程是从内存中读取值,而不是线程缓存中。
三·一 使用 synchronized 关键字
JVM 可以确保被 synchronized 修饰的代码块每次只能被一个线程访问执行。
内部通过锁住一个对象或类来实现。
1、synchronized 可以在被锁定的资源或未被锁定的资源情况下工作。
但是,在任何线程执行 synchronized 代码块之前,
它需要首先获取这个对象的锁才可以执行。
而线程执行非 synchronized 代码块不需要获取对象的锁。
当然,线程在代码块执行完毕后,需要释放该锁。
这样其它处于等待状态的线程可以获取对象的锁以执行。
2、synchronized 的使用方式有两种:
1)用在类方法的声明上。使整个方法成为 synchronized 方法。
此时会锁定 Object (实例),如果是静态方法则锁类。
为了提高运行性能,请尽量少使用该策略。
2)单独使用。形成一个 synchronized 代码块。
只锁定方法中需要锁定的代码块。
此时,需要提供资源,以从该资源中取得锁。
资源可以是 ABC.class 或 类的一个属性。
synchronized(this) 会锁定当前整个实例,
即同时获取了当前实例的锁 + 实例所有属性的锁。
注意:为了提高执行性能,被锁的对象应该是最小范围的。
例如在一个类中有多个 synchronized 的代码块,其中
一块锁定了整个实例。则其它代码块则不能被其它线程执行。
3、synchronized 是以降低性能为代价的(本来可以并行执行,先只能串行执行)。
所以非必需,不要用。
4、synchronized 只在同一个 JVM 中是有效的。
5、synchronized 会造成[死锁]现象。
synchronized 代码块同时锁定多个对象。多个线程互相等待其它线程释放对象的锁。
6、synchronized 不能用于 构造方法 和 局部变量。
7、synchronized 锁定的实例,应该避免是 常量池 中的对象。
这些常量池中的对象可能被其它 synchronized 代码块引用。例如:String pool
8、synchronized 通常锁定一个虚设的 private 的类属性,对代码块进行锁定。
因为 private 的引用指向的实例始终是一个。不会变。
下面的改进使 count 属性线程安全的代码:
让我们看一些 synchronized 的例子,看看我们能学到什么:
例子一:
如果能够拿到 MyObject 的实例,并且在锁定该实例无限长的时间。
其它代码则无法执行该实例的方法。尤其在单例模式中。
例子二:
更有甚者:
输出结果:
Lock class instance!
M: Hello, Word
分析:
C: Hello, Word
这一句没有输出,因为 MyObject.class 对象被其它 Lock 锁定
根据结果可以看出:
要锁定的对象可以是类(class),也可以是类的实例(instance)。
但是类和类的实例在锁的问题上不相关,也没有权限上你大我小的隶属关系。
锁对象和锁类的区别?
synchronized(X.class)
使用 class类 所为锁定类, 那是因为只有一个 class 类在 JVM 中被 classLoader 加载。
如果一个线程要执行该代码块,必须拥有 class 类的锁。
此时只能有一个线程在执行该代码块。
synchronized(this)
使用类的实例为锁定对象。
如果一个线程要执行该代码块,只需拥有实例的锁即可。
此时可以有多个线程在执行该代码块。
例子三:
注意:
lock 属性是 public 的。通过替换它指向引用的对象,多个线程就可以执行 synchronized 块的代码。
类似情况:private 的属性,但是有 public 的 set() 方法。
例子四:
注意:没有 static 关键字,则不等价。
例子五:
输出:
Time taken= 1501
[1:t1, 2:t2, 3:t2:t3, 4:t1, 5:t1:t2, 6:t3:t2]
加上同步锁:
输出:
Time taken= 1502
[1:t1:t2:t3, 2:t2:t1:t3, 3:t1:t2:t3, 4:t1:t2:t3, 5:t2:t1:t3, 6:t1:t2:t3]
继续阅读:
使用 java.util.concurrent.lock.Lock 实现线程同步
-
转载请注明,
原文出处:http://lixh1986.iteye.com/blog/2351243
-
引用:
http://www.journaldev.com/1061/thread-safety-in-java
一、问题背景
Java语言提供了多线程的功能。
多线程创建于相同的Object,多线程间共享Object的变量或属性。
但是,当线程对共享的数据进行读写时,会导致数据的不一致(data inconsistency)。
二、线程同步情景分析
数据不一致的原因是由数据操作的非原子性引起的。
即:更新任何属性或变量,非一步完成,而是需要三部:
1、读取现在的值。
2、进行必要的操作以得到要更新的值。
3、把更新的值写入到引用的变量或属性中。
来看一个简单的例子:
多个线程共享一个数据,并对其进行修改。
public class ThreadSafety { public static void main(String[] args) throws InterruptedException { ProcessingThread pt = new ProcessingThread(); Thread t1 = new Thread(pt, "t1"); t1.start(); Thread t2 = new Thread(pt, "t2"); t2.start(); //wait for threads to finish processing t1.join(); t2.join(); System.out.println("Processing count="+pt.getCount()); } } class ProcessingThread implements Runnable{ private int count; @Override public void run() { for(int i=1; i < 5; i++){ processSomething(i); count++; } } public int getCount() { return this.count; } private void processSomething(int i) { // processing some job try { Thread.sleep(i*1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
上述循环中,使用两个线程对 count 变量的值进行增加,每个线程各增加四次。
最后 count 的值应该是 8。但是在实际的多次运行中,它的值在 6,7,8 之间。
发生这种情况,即使 count++ 看起来是单原子操作,实际不是。
从而导致数据不一致。
三、Java中保证线程同步的方法
为了在多线程运行环境中确保数据的一致,Java提供了一些方法。
下面是主要的几种:
1、使用 synchronized 关键字(最广泛使用)
2、使用 java.util.concurrent.atomic 包下的原子操作包装类。
例如: AtomicInteger
3、使用 java.util.concurrent.locks 包下的锁类
4、使用 线程安全的集合类。
例如: ConcurrentHashMap
5、使用 volatile 关键字确保读的一致性。(不能确保写一致)
确保每个线程是从内存中读取值,而不是线程缓存中。
三·一 使用 synchronized 关键字
JVM 可以确保被 synchronized 修饰的代码块每次只能被一个线程访问执行。
内部通过锁住一个对象或类来实现。
1、synchronized 可以在被锁定的资源或未被锁定的资源情况下工作。
但是,在任何线程执行 synchronized 代码块之前,
它需要首先获取这个对象的锁才可以执行。
而线程执行非 synchronized 代码块不需要获取对象的锁。
当然,线程在代码块执行完毕后,需要释放该锁。
这样其它处于等待状态的线程可以获取对象的锁以执行。
2、synchronized 的使用方式有两种:
1)用在类方法的声明上。使整个方法成为 synchronized 方法。
此时会锁定 Object (实例),如果是静态方法则锁类。
为了提高运行性能,请尽量少使用该策略。
2)单独使用。形成一个 synchronized 代码块。
只锁定方法中需要锁定的代码块。
此时,需要提供资源,以从该资源中取得锁。
资源可以是 ABC.class 或 类的一个属性。
synchronized(this) 会锁定当前整个实例,
即同时获取了当前实例的锁 + 实例所有属性的锁。
注意:为了提高执行性能,被锁的对象应该是最小范围的。
例如在一个类中有多个 synchronized 的代码块,其中
一块锁定了整个实例。则其它代码块则不能被其它线程执行。
3、synchronized 是以降低性能为代价的(本来可以并行执行,先只能串行执行)。
所以非必需,不要用。
4、synchronized 只在同一个 JVM 中是有效的。
5、synchronized 会造成[死锁]现象。
synchronized 代码块同时锁定多个对象。多个线程互相等待其它线程释放对象的锁。
6、synchronized 不能用于 构造方法 和 局部变量。
7、synchronized 锁定的实例,应该避免是 常量池 中的对象。
这些常量池中的对象可能被其它 synchronized 代码块引用。例如:String pool
8、synchronized 通常锁定一个虚设的 private 的类属性,对代码块进行锁定。
因为 private 的引用指向的实例始终是一个。不会变。
下面的改进使 count 属性线程安全的代码:
//dummy object variable for synchronization private Object mutex=new Object(); //using synchronized block to read, increment and update count value synchronously synchronized (mutex) { count++; }
让我们看一些 synchronized 的例子,看看我们能学到什么:
例子一:
public class MyObject { // Locks on the object's monitor public synchronized void doSomething() { // ... } } public class Hack{ public static void main(String[] args){ // Hackers code MyObject myObject = Factory.getMyObject(); synchronized (myObject) { while (true) { // Indefinitely delay myObject Thread.sleep(Integer.MAX_VALUE); } } } }
如果能够拿到 MyObject 的实例,并且在锁定该实例无限长的时间。
其它代码则无法执行该实例的方法。尤其在单例模式中。
例子二:
更有甚者:
package com.gentleman.sychronized; public class Hack { public static void main(String[] args) throws Exception{ new Thread(new R1()).start(); Thread.sleep(1000); new Thread(new R2()).start(); new Thread(new R3()).start(); } } class MyObject { public void sayHello(){ synchronized(MyObject.class){ System.out.println("C: Hello, Word"); } } public synchronized void sayHello2(){ System.out.println("M: Hello, Word"); } } class R1 implements Runnable{ @Override public void run(){ synchronized (MyObject.class) { System.out.println("Lock class instance!"); while (true) { try { Thread.sleep(Integer.MAX_VALUE); } catch (InterruptedException e) { e.printStackTrace(); } } } } } class R2 implements Runnable{ @Override public void run() { MyObject myObject = new MyObject(); myObject.sayHello(); } } class R3 implements Runnable{ @Override public void run() { MyObject myObject = new MyObject(); myObject.sayHello2(); } }
输出结果:
Lock class instance!
M: Hello, Word
分析:
C: Hello, Word
这一句没有输出,因为 MyObject.class 对象被其它 Lock 锁定
根据结果可以看出:
要锁定的对象可以是类(class),也可以是类的实例(instance)。
但是类和类的实例在锁的问题上不相关,也没有权限上你大我小的隶属关系。
锁对象和锁类的区别?
synchronized(X.class)
使用 class类 所为锁定类, 那是因为只有一个 class 类在 JVM 中被 classLoader 加载。
如果一个线程要执行该代码块,必须拥有 class 类的锁。
此时只能有一个线程在执行该代码块。
synchronized(this)
使用类的实例为锁定对象。
如果一个线程要执行该代码块,只需拥有实例的锁即可。
此时可以有多个线程在执行该代码块。
例子三:
public class MyObject { public Object lock = new Object(); public void doSomething() { synchronized (lock) { // ... } } } //untrusted code MyObject myObject = new MyObject(); //change the lock Object reference myObject.lock = new Object();
注意:
lock 属性是 public 的。通过替换它指向引用的对象,多个线程就可以执行 synchronized 块的代码。
类似情况:private 的属性,但是有 public 的 set() 方法。
例子四:
static void myMethod() { synchronized(MyClass.class) { //code } } // 等价于: static synchronized void myMethod() { //code }
注意:没有 static 关键字,则不等价。
void myMethod() { synchronized(this) { //code } } // 等价于: synchronized void myMethod() { //code }
例子五:
import java.util.Arrays; public class T { public static void main(String[] args) throws InterruptedException { String[] arr = {"1","2","3","4","5","6"}; HashMapProcessor hmp = new HashMapProcessor(arr); Thread t1=new Thread(hmp, "t1"); Thread t2=new Thread(hmp, "t2"); Thread t3=new Thread(hmp, "t3"); long start = System.currentTimeMillis(); //start all the threads t1.start();t2.start();t3.start(); //wait for threads to finish t1.join();t2.join();t3.join(); System.out.println("Time taken= "+(System.currentTimeMillis()-start)); //check the shared variable value now System.out.println(Arrays.asList(hmp.getMap())); } } class HashMapProcessor implements Runnable{ private String[] strArr = null; public HashMapProcessor(String[] m){ this.strArr=m; } @Override public void run() { for(int i=0; i < strArr.length; i++){ processSomething(i); addThreadName( i, Thread.currentThread().getName()); } } private void addThreadName(int i, String name) { strArr[i] = strArr[i] +":"+name; } private void processSomething(int index) { // processing some job try { Thread.sleep(index * 100); } catch (InterruptedException e) { e.printStackTrace(); } } public String[] getMap() { return strArr; } }
输出:
Time taken= 1501
[1:t1, 2:t2, 3:t2:t3, 4:t1, 5:t1:t2, 6:t3:t2]
加上同步锁:
private Object lock = new Object(); private void addThreadName2(int i, String name) { synchronized(lock){ strArr[i] = strArr[i] +":"+name; } }
输出:
Time taken= 1502
[1:t1:t2:t3, 2:t2:t1:t3, 3:t1:t2:t3, 4:t1:t2:t3, 5:t2:t1:t3, 6:t1:t2:t3]
继续阅读:
使用 java.util.concurrent.lock.Lock 实现线程同步
-
转载请注明,
原文出处:http://lixh1986.iteye.com/blog/2351243
-
引用:
http://www.journaldev.com/1061/thread-safety-in-java
发表评论
-
java 将文件夹所有的文件合并到指定的文件夹下
2020-06-30 19:17 1083场景:将文件夹所有的文件合并到指定的文件夹下 另外:如果想效 ... -
多线程-线程池的四种创建方式
2020-04-01 18:38 509多线程-线程池的四种创建方式 https://blog.cs ... -
Java基础之:nio
2019-11-13 15:38 500一、理论讲解: 史上最强Java NIO入门:担心从入门到放弃 ... -
Java 分布式之:RPC 基本概念
2019-11-13 15:07 478转载: https://www.jianshu.com/p/ ... -
Java之 volatile 关键字原理详解
2019-11-07 15:36 569一、什么是 volatile ? ... -
POI实现excell批注背景图片(仿html浮窗显示图片)
2019-10-21 08:17 715POI实现excell批注背景图片(仿html浮窗显示图片) ... -
Java之设计模式之 Observer 观察者
2019-07-04 17:21 1095观察者设计模式 Java 已经实现了该模式,并且提供了使用类 ... -
HashMap, LinkedHashMap and TreeMap
2019-03-01 11:04 685https://stackoverflow.com/a/177 ... -
Java lib 操作 excel 插入图片
2019-01-19 12:46 883https://poi.apache.org/componen ... -
数据库连接池C3P0
2018-05-29 16:50 907一、名字的由来 很多 ... -
Java8之集合(Collection)遍历 forEach()、stream()
2018-05-29 14:39 20759package java8.collections; ... -
Junit Vs main on "java.util.concurrent.Executors"
2017-11-10 16:44 840Same code with different result ... -
Java之大数据学习路线
2017-11-03 10:08 5727三个月大数据研发学习 ... -
Java中创建对象的5种方式
2017-10-26 14:21 844一、Java之5种创建对象的方式 ————————————— ... -
Log4j和Slf4j的比较
2017-06-23 12:41 1420一直搞不清 Log4j 和 SLF4j 的关系。今天才若有所 ... -
Java之Java7新特性之try资源句式
2017-04-20 14:58 5388Java之Java7新特性之try资源句式 一、【try资源 ... -
Java之 java.util.concurrent 包之ExecutorService之submit () 之 Future
2017-03-04 21:27 3843一、如何使用 ExecutorService.submit() ... -
Java之 java.util.concurrent 包之Executor与ExecutorService
2017-03-04 21:18 2709一、问题: execute() 与 submit() 的区别? ... -
JAVAEE之单用户登录
2017-02-05 11:55 1063单用户登录是系统中数据一直性的解决方案之一。 问题背景: 试 ... -
Java之多线程之线程池之线程重复使用
2017-02-04 13:33 5574一、问题背景 在使用多线程时,如果要开启一个任务,则就需要新 ...
相关推荐
Java中的`synchronized`关键字是用于实现线程同步的关键机制,主要目的是解决多线程环境下的数据安全问题。当多个线程访问共享资源时,如果没有适当的同步控制,可能会导致数据不一致或者竞态条件等问题。线程同步...
总之,Delphi VCL线程同步是多线程编程中不可或缺的一部分,Synchronize方法提供了简单且安全的方式来更新主线程中的UI。理解其工作原理和限制,结合适当的同步策略,可以编写出高效且稳定的多线程应用程序。
Java 线程同步控制机制 线程同步是 Java 编程中的一种机制,用于控制多个线程之间的资源访问顺序,以避免线程之间的冲突和数据不一致。线程同步的目的就是避免线程“同步”执行,即让多个线程之间排队操作共享资源...
4. **delphi-thread-gui 示例**:这个示例可能涉及在多线程中与图形用户界面(GUI)的交互。在 Delphi 中,主线程通常负责处理 GUI 事件,而其他线程不应直接更新界面组件,以免引发异常。可以使用 `Synchronize` 或...
在 Delphi 中,多线程编程常常涉及到线程同步,以确保多个线程安全地访问共享资源或执行特定操作。`Synchronize` 方法是 Delphi 中用于在主线程中安全执行代码的一种机制,尤其适用于 UI 更新。然而,在 DLL 或 ...
Java中synchronize关键字的使用与线程安全测试 Java语言中,synchronize关键字是一个非常重要的概念,它主要用于解决线程安全问题。在多线程编程中,经常会出现线程安全问题,因为多个线程可能会访问同一个共享资源...
Java实现线程同步方法及原理详解 Java实现线程同步方法及原理详解是 Java 编程中非常重要的一部分。在多线程编程中,线程同步机制是必不可少的,否则可能会引发一些不可预期的结果。Java提供了三种机制来实现线程...
本实验旨在深入理解线程与进程的概念,并通过实践操作掌握线程同步的基本方法。同时,本实验还将探讨在Windows环境下如何根据具体需求选择使用进程或线程。 #### 二、实验环境搭建 本实验在**Windows XP**环境下...
本文档将详细介绍 JAVA 多线程的概念、特点、创建方式、线程生命周期、同步机制、线程通信等知识点,并通过实例分析和代码示例来深入讲解 JAVA 多线程的应用场景和实现方法。 一、JAVA 多线程概念 在 Java 中,...
### Java中的方法重载与重写以及线程概念详解 #### 一、方法重载(Overloading) 在Java中,**方法重载**是指在同一个类中定义多个具有相同名称但参数列表不同的方法。这里提到的“参数列表不同”不仅包括参数的...
本文将深入探讨如何在Delphi XE中有效地处理这些问题,包括线程的创建、管理、中断,以及线程安全和UI同步。 首先,我们需要了解什么是线程。线程是程序执行的最小单元,每个线程都有自己的堆栈和程序计数器,可以...
本文将深入探讨如何在 Delphi 中创建多线程,并且着重讲解如何传递参数给线程以及如何在主线程中同步显示由线程处理后的数据。 一、多线程基础 在 Delphi 中,我们可以使用 TThread 类来创建和管理线程。TThread 是...
2. **Synchronize 方法**:在线程内部调用Synchronize方法,可以安全地在主线程中执行代码,确保对用户界面的操作不会导致异常。 3. **Mutex(互斥量)**:互斥量是一种同步对象,允许同一时间只有一个线程访问共享...
在 Delphi 中,线程(Thread)是程序执行的基本单元,它允许并发处理,使得应用程序可以在同一时间执行多个任务。线程管理对于高效且响应迅速的软件开发至关重要。本篇文章将深入探讨 Delphi 中线程的创建、同步、...
Java对象锁和类锁是Java多线程编程中至关重要的概念,它们是通过`synchronized`关键字来实现的,用于确保代码在并发环境下的线程安全。在这个全面解析中,我们将深入探讨这两个锁机制,理解它们的工作原理以及如何在...
Java 多线程的实现方法有继承 Thread 类和实现 Runnable 接口两种,同步的实现方法有使用 synchronized 关键字和使用 wait、notify、notifyAll 方法。了解线程的基本概念、状态以及状态之间的关系是掌握 Java 多线程...
"线程下载文件与主窗体同步下载进度演示"这个项目就是针对这种情况的一个实例,它展示了如何在后台线程中执行文件下载任务,同时在主窗体上实时更新下载进度,让用户能够清晰地看到下载的状态。下面我们将详细探讨这...
Synchronize确保了即使在多线程环境下,更新界面的操作也是安全的。 6. **代码注释**:对于初学者来说,详细的代码注释非常重要,它能帮助理解代码的逻辑和作用。在Delphi中,注释可以使用`{}`或`//`来实现,对于...
在Linux操作系统中,线程间同步是多线程编程中的一个重要概念,用于确保多个线程在访问共享资源时能够有序进行,避免数据竞争和不一致性。本文将详细讲解Linux线程间同步的各种机制和实现方法。 一、互斥量(Mutex...
不过,可以推测这可能与线程的四种基本操作有关:创建(Create)、启动(Start)、同步(Synchronize)和销毁(Destroy)。 综上所述,"xiancheng.rar"压缩包中的源代码可能涵盖了Java多线程编程的基本要素,包括...