java多线程 线程安全

Thread Safety in Java is a very important topic. Java provides multi-threaded environment support using Java Threads, we know that multiple threads created from same Object share object variables and this can lead to data inconsistency when the threads are used to read and update the shared data.

Java中的线程安全是一个非常重要的主题。 Java使用Java线程提供了多线程环境支持,我们知道从同一个对象创建的多个线程共享对象变量,当这些线程用于读取和更新共享数据时,这可能导致数据不一致

线程安全 (Thread Safety)

The reason for data inconsistency is because updating any field value is not an atomic process, it requires three steps; first to read the current value, second to do the necessary operations to get the updated value and third to assign the updated value to the field reference.

数据不一致的原因是因为更新任何字段值都不是原子过程,它需要三个步骤。 首先读取当前值,其次进行必要的操作以获取更新的值,第三次将更新的值分配给字段引用。

Let’s check this with a simple program where multiple threads are updating the shared data.

让我们用一个简单的程序检查一下,其中多个线程正在更新共享数据。

package com.journaldev.threads;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 processingt1.join();t2.join();System.out.println("Processing count="+pt.getCount());}}class ProcessingThread implements Runnable{private int count;@Overridepublic 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 jobtry {Thread.sleep(i*1000);} catch (InterruptedException e) {e.printStackTrace();}}}

In the above program for loop, count is incremented by 1 four times and since we have two threads, its value should be 8 after both the threads finished executing. But when you will run the above program multiple times, you will notice that count value is varying between 6,7,8. This is happening because even if count++ seems to be an atomic operation, its NOT and causing data corruption.

在上面的for循环程序中, count增加1到四倍,并且由于我们有两个线程,因此两个线程执行完后其值应为8。 但是当您多次运行上述程序时,您会注意到计数值在6,7,8之间变化。 发生这种情况是因为即使count ++似乎是一个原子操作,它的NOT也不会导致数据损坏。

Java中的线程安全 (Thread Safety in Java)

Thread safety in java is the process to make our program safe to use in multithreaded environment, there are different ways through which we can make our program thread safe.

Java中的线程安全是使我们的程序在多线程环境中可以安全使用的过程,可以通过多种方法使程序线程安全。

  • Synchronization is the easiest and most widely used tool for thread safety in java.同步是Java中线程安全最简单,使用最广泛的工具。
  • Use of Atomic Wrapper classes from java.util.concurrent.atomic package. For example AtomicInteger从java.util.concurrent.atomic包使用Atomic Wrapper类。 例如AtomicInteger
  • Use of locks from java.util.concurrent.locks package.使用java.util.concurrent.locks包中的锁。
  • Using thread safe collection classes, check this post for usage of ConcurrentHashMap for thread safety.使用线程安全收集类,请检查此帖子以了解ConcurrentHashMap的线程安全用法。
  • Using volatile keyword with variables to make every thread read the data from memory, not read from thread cache.将volatile关键字与变量一起使用,可使每个线程从内存中读取数据,而不是从线程缓存中读取数据。

Java同步 (Java synchronized)

Synchronization is the tool using which we can achieve thread-safety, JVM guarantees that synchronized code will be executed by only one thread at a time. java keyword synchronized is used to create synchronized code and internally it uses locks on Object or Class to make sure only one thread is executing the synchronized code.

同步是我们可以用来实现线程安全性的工具,JVM保证同步的代码一次只能由一个线程执行。 java关键字sync用于创建同步代码,并且在内部使用Object或Class上的锁来确保只有一个线程在执行同步代码。

  • Java synchronization works on locking and unlocking of the resource before any thread enters into synchronized code, it has to acquire the lock on the Object and when code execution ends, it unlocks the resource that can be locked by other threads. In the meantime, other threads are in wait state to lock the synchronized resource.Java同步可在任何线程进入同步代码之前对资源进行锁定和解锁,它必须获得对Object的锁定,并且在代码执行结束时,它会解锁可以被其他线程锁定的资源。 同时,其他线程处于等待状态以锁定同步资源。
  • We can use synchronized keyword in two ways, one is to make a complete method synchronized and another way is to create synchronized block.我们可以通过两种方式使用synced关键字,一种是使完整的方法同步,另一种方法是创建同步块。
  • When a method is synchronized, it locks the Object, if method is static it locks the Class, so it’s always best practice to use synchronized block to lock the only sections of method that needs synchronization.同步方法时,它会锁定Object ;如果方法是静态的,则它会锁定Class ,因此,最佳做法始终是使用同步块来锁定方法中仅需要同步的部分。
  • While creating a synchronized block, we need to provide the resource on which lock will be acquired, it can be XYZ.class or any Object field of the class.创建同步块时,我们需要提供获取锁定的资源,它可以是XYZ.class或该类的任何Object字段。
  • synchronized(this) will lock the Object before entering into the synchronized block.synchronized(this)将在进入同步块之前锁定对象。
  • You should use the lowest level of locking, for example, if there are multiple synchronized block in a class and one of them is locking the Object, then other synchronized blocks will also be not available for execution by other threads. When we lock an Object, it acquires a lock on all the fields of the Object.您应该使用最低级别的锁定 ,例如,如果一个类中有多个同步块,而其中一个正在锁定Object,则其他同步块也将不可用于其他线程执行。 当我们锁定一个对象时,它获得了对该对象所有字段的锁定。
  • Java Synchronization provides data integrity on the cost of performance, so it should be used only when it’s absolutely necessary.Java同步以性能为代价提供数据完整性,因此仅在绝对必要时才应使用它。
  • Java Synchronization works only in the same JVM, so if you need to lock some resource in multiple JVM environment, it will not work and you might have to look after some global locking mechanism.Java同步仅在同一个JVM中起作用,因此,如果您需要在多个JVM环境中锁定某些资源,则它将无法正常工作,因此您可能需要照顾一些全局锁定机制。
  • Java Synchronization could result in deadlocks, check this post about deadlock in java and how to avoid them.Java同步可能会导致死锁,请查看有关Java死锁以及如何避免死锁的文章。
  • Java synchronized keyword cannot be used for constructors and variables.Java同步关键字不能用于构造函数和变量。
  • It is preferable to create a dummy private Object to use for the synchronized block so that it’s reference can’t be changed by any other code. For example, if you have a setter method for Object on which you are synchronizing, it’s reference can be changed by some other code leads to the parallel execution of the synchronized block.最好创建一个虚拟私有对象用于同步块,以使它的引用不能被任何其他代码更改。 例如,如果您有一个要在其上同步的Object的setter方法,则可以通过其他一些代码来更改其引用,从而导致并行执行同步块。
  • We should not use any object that is maintained in a constant pool, for example String should not be used for synchronization because if any other code is also locking on same String, it will try to acquire lock on the same reference object from String pool and even though both the codes are unrelated, they will lock each other.我们不应该使用在常量池中维护的任何对象,例如,不应将String用于同步,因为如果任何其他代码也锁定在同一String上,它将尝试从String池获取对同一引用对象的锁定,并且即使两个代码无关,它们也会互相锁定。

Here are the code changes we need to do in the above program to make it thread-safe.

这是我们在上述程序中需要执行的代码更改,以使其具有线程安全性。

//dummy object variable for synchronizationprivate Object mutex=new Object();...//using synchronized block to read, increment and update count value synchronouslysynchronized (mutex) {count++;}

Let’s see some synchronization examples and what can we learn from them.

让我们看看一些同步示例,以及我们可以从中学到什么。

public class MyObject {// Locks on the object's monitorpublic synchronized void doSomething() { // ...}
}// Hackers code
MyObject myObject = new MyObject();
synchronized (myObject) {while (true) {// Indefinitely delay myObjectThread.sleep(Integer.MAX_VALUE); }
}

Notice that hacker’s code is trying to lock the myObject instance and once it gets the lock, it’s never releasing it causing doSomething() method to block on waiting for the lock, this will cause the system to go on deadlock and cause Denial of Service (DoS).

请注意,黑客的代码正在尝试锁定myObject实例,并且一旦获得了锁定,就永远不会释放它,从而导致doSomething()方法在等待锁定时阻塞,这将导致系统进入死锁并导致拒绝服务( DoS)。

public class MyObject {public Object lock = new Object();public void doSomething() {synchronized (lock) {// ...}}
}//untrusted codeMyObject myObject = new MyObject();
//change the lock Object reference
myObject.lock = new Object();

Notice that lock Object is public and by changing its reference, we can execute synchronized block parallel in multiple threads. A similar case is true if you have private Object but have a setter method to change its reference.

注意,锁对象是公共的,并且通过更改其引用,我们可以在多个线程中并行执行同步块。 如果您有私有Object但有一个setter方法来更改其引用,则情况类似。

public class MyObject {//locks on the class object's monitorpublic static synchronized void doSomething() { // ...}
}// hackers code
synchronized (MyObject.class) {while (true) {Thread.sleep(Integer.MAX_VALUE); // Indefinitely delay MyObject}
}

Notice that hacker code is getting a lock on the class monitor and not releasing it, it will cause deadlock and DoS in the system.

请注意,黑客代码已在类监视器上获得锁定,而没有释放它,这将导致系统中的死锁和DoS。

Here is another example where multiple threads are working on the same array of Strings and once processed, appending thread name to the array value.

这是另一个示例,其中多个线程正在相同的String数组上工作,并且一旦被处理,就将线程名附加到数组值中。

package com.journaldev.threads;import java.util.Arrays;public class SyncronizedMethod {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 threadst1.start();t2.start();t3.start();//wait for threads to finisht1.join();t2.join();t3.join();System.out.println("Time taken= "+(System.currentTimeMillis()-start));//check the shared variable value nowSystem.out.println(Arrays.asList(hmp.getMap()));}}class HashMapProcessor implements Runnable{private String[] strArr = null;public HashMapProcessor(String[] m){this.strArr=m;}public String[] getMap() {return strArr;}@Overridepublic void run() {processArr(Thread.currentThread().getName());}private void processArr(String name) {for(int i=0; i < strArr.length; i++){//process data and append thread nameprocessSomething(i);addThreadName(i, name);}}private void addThreadName(int i, String name) {strArr[i] = strArr[i] +":"+name;}private void processSomething(int index) {// processing some jobtry {Thread.sleep(index*1000);} catch (InterruptedException e) {e.printStackTrace();}}}

Here is the output when I run the above program.

这是我运行上述程序时的输出。

Time taken= 15005
[1:t2:t3, 2:t1, 3:t3, 4:t1:t3, 5:t2:t1, 6:t3]

The String array values are corrupted because of shared data and no synchronization. Here is how we can change addThreadName() method to make our program thread-safe.

由于共享数据且没有同步,因此String数组值已损坏。 这是我们如何更改addThreadName()方法以使程序具有线程安全性的方法。

private Object lock = new Object();private void addThreadName(int i, String name) {synchronized(lock){strArr[i] = strArr[i] +":"+name;}}

After this change, our program works fine and here is the correct output of the program.

进行此更改后,我们的程序可以正常工作,这是程序的正确输出。

Time taken= 15004
[1:t1:t2:t3, 2:t2:t1:t3, 3:t2:t3:t1, 4:t3:t2:t1, 5:t2:t1:t3, 6:t2:t1:t3]

That’s all for thread safety in java, I hope you learned about thread-safe programming and using synchronized keyword.

这就是Java中线程安全的全部内容,希望您了解线程安全编程以及如何使用synced关键字。

翻译自: https://www.journaldev.com/1061/thread-safety-in-java

java多线程 线程安全

java多线程 线程安全_Java中的线程安全相关推荐

  1. java 守护线程 作用_java中守护线程的一些概念和用法

    网上的资料中,守护线程的功能一般都是"只要当前JVM实例中尚存任何一个非守护线程没有结束,守护线程就全部工作:只有当最后一个非守护线程结束是,守护线程随着JVM一同结束工作,Daemon作用 ...

  2. java 全局变量线程安全_Java中的线程安全全局变量

    我试图了解 java中的线程安全机制,我需要一些帮助.我上课了: public class ThreadSafe { private Executor executor = new Scheduled ...

  3. Java 多线程详解(五)------线程的声明周期

    Java 多线程详解(一)------概念的引入:https://blog.csdn.net/weixin_39816740/article/details/80089790 Java 多线程详解(二 ...

  4. java 多线程并发 问题_JAVA多线程和并发基础面试问答

    原文链接 译文连接作者:Pankaj  译者:郑旭东  校对:方腾飞 多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一.在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌 ...

  5. java多线程 文件夹_java多线程读同一个文件

    java多线程同时读取一个文件,这个方法可行吗?不可行. 多线程能够提高效率是因为现在的cpu普遍是多核cpu, 多条线程可以在多个内核中同时执行来提高计算效率.但是计算机磁盘的磁头只有一个,即使多条 ...

  6. python 在主线程开线程_Python开启线程,在函数中开线程的实例

    逻辑处理上分成了多个模块,为了提高效率,前一个模块处理完调用后一个模块操作时使用多线程 我这里遇到的情形是前面取数据后面存到mysql,发现单线程效率很低,改为取数据后开线程存到mysql 开启线程之 ...

  7. python可以开多少线程_Python开启线程,在函数中开线程的实例

    逻辑处理上分成了多个模块,为了提高效率,前一个模块处理完调用后一个模块操作时使用多线程 我这里遇到的情形是前面取数据后面存到mysql,发现单线程效率很低,改为取数据后开线程存到mysql 开启线程之 ...

  8. java中多线程编程案例_Java中多线程编程实战的实现线程_Java编程_Java程序员_课课家...

    java编程语言使多线程如此简单有效,以致于某些程序员说它实际上是自然的.尽管在 Java 中使用线程比在其他语言中要容易得多,仍然有一些概念需要掌握.要记住的一件重要的事情是 main() 函数也是 ...

  9. java中怎么判断一段代码时线程安全还是非线程安全_Java 中的多线程你只要看这一篇就够了...

    引 如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个 ...

最新文章

  1. Spring Cloud(四)服务提供者 Eureka + 服务消费者 Feign
  2. UVA 253 Cube painting
  3. GitHub移动App上线:四大特性,手机端无缝完成git任务
  4. 教你在Linux下构建主、从域名服务器!
  5. 实现mysql按月统计的教程
  6. Docker监控方案(TIG)的研究与实践之Influxdb
  7. 鸿蒙os 芯片制程,华为Mate50将如期发布,屏下镜头+鸿蒙OS,再见iPhone12
  8. 基于jmx监控kafka_0542-6.1.0-非安全环境下Kafka管理工具Kafka Eagle安装使用
  9. php编写六十甲子纳音表_六十甲子纳音表详细说明,看看你属于什么命,属于那个颜色...
  10. Bootstrap媒体对象
  11. 深度学习(5) - 卷积神经网络
  12. Cmake-cmake_minimum_required()
  13. 【监控】使用 Grafana、collectd 和 InfluxDB 打造现代监控系统
  14. 记一次现网k8s中pod连接数据库异常的问题分析及解决实践(tcp_tw_recycle与tcp_tw_reuse内核参数修改)
  15. 黄永成-thinkphp讲解-个人博客讲解25集
  16. 第九课堂: 基于兴趣、技能和经验分享的网络交易平台
  17. 小白也能看懂,30 分钟搭建个人博客!
  18. 什么计算机网络成瘾,计算机网络与网络成瘾.pdf
  19. 线上盲盒电商模式运营
  20. 快递鸟代收货款接口demo-order

热门文章

  1. 预写式日志 - postgresql之WAL(Write Ahead Log)
  2. QQ抢车位外挂(起始篇)--小研究成果展示
  3. 不可磨灭的记忆 CPU发展史经典回顾
  4. [转载] python中字典中追加_python 中字典中的删除,pop 方法与 popitem 方法
  5. [转载] python笔记:4.1.2.1统计量_离散程度_方差和标准差
  6. 超哥笔记 --nginx入门(6)
  7. javascript——js string 转 int 注意的问题——parseInt(转)
  8. 047 Permutations II 有重复数字的全排列
  9. 关于iOS 热更新(热修复)你必须知道的一种方法- JSPatch
  10. ORA-00119: invalid specification for system parameter LOCAL_LISTENER;