当前位置:首页 > 后端开发 > Java多线程中的竞争条件、锁以及同步的概念

Java多线程中的竞争条件、锁以及同步的概念

7个月前 (05-23)25

Java多线程中的竞争条件、锁以及同步的概念

1.竞争条件:

在Java多线程中,当两个或以上的线程对同一个数据进行操作的时候,可能会产生“竞争条件”的现象。这种现象产生的根本原因是因为多个线程在对同一个数据进行操作,此时对该数据的操作是非“原子化”的,可能前一个线程对数据的操作还没有结束,后一个线程又开始对同样的数据开始进行操作,这就可能会造成数据结果的变化未知。

竞争条件参考以下的例子:

 

[java]   view plain   copy
 
  1. public class TestThread {  
  2.   
  3.     public static void main(String[] args) {  
  4.         // new 出一个新的对象 t  
  5.         MyThread t = new MyThread();  
  6.           
  7.         Thread ta = new Thread(t, "Thread-A");  
  8.         Thread tb = new Thread(t, "Thread-B");  
  9.         ta.start();  
  10.         tb.start();  
  11.     }  
  12. }  
  13.   
  14. class MyThread implements Runnable {  
  15.     // 变量 a 被两个线程共同操作,可能会造成线程竞争  
  16.     int a = 10;  
  17.     @Override  
  18.     public void run() {  
  19.         for (int i = 0; i < 5; i++) {  
  20.             a -= 1;  
  21.             try {  
  22.                 Thread.sleep(1);  
  23.             } catch (InterruptedException e) {}  
  24.             System.out.println(Thread.currentThread().getName() + " → a = " + a);  
  25.         }  
  26.     }  
  27. }  
最终运行结果如下:

 

Thread-A → a = 8
Thread-B → a = 7
Thread-A → a = 6
Thread-B → a = 5
Thread-A → a = 4
Thread-B → a = 3
Thread-A → a = 2
Thread-B → a = 1
Thread-A → a = 0
Thread-B → a = 0

从上面的结果中我们可以看到,在线程A对数据进行了操作之后,他还没有来得及数据进行下一次的操作,此时线程B也对数据进行了操作,导致数据a一次性被减了两次,以至于a为9的时候的值根本没有打印出来,a为0的时候却被打印了两次。

那么,我们要如何才能避免结果这种情况的出现呢?

2.线程锁

如果在一个线程对数据进行操作的时候,禁止另外一个线程操作此数据,那么,就能很好的解决以上的问题了。这种操作叫做给线程加锁。

 

[java]   view plain   copy
 
  1. import java.util.concurrent.locks.Lock;  
  2. import java.util.concurrent.locks.ReentrantLock;  
  3.   
  4. public class TestThread {  
  5.   
  6.     public static void main(String[] args) {  
  7.         // new 出一个新的对象 t  
  8.         MyThread t = new MyThread();  
  9.           
  10.         Thread ta = new Thread(t, "Thread-A");  
  11.         Thread tb = new Thread(t, "Thread-B");  
  12.         ta.start();  
  13.         tb.start();  
  14.     }  
  15. }  
  16.   
  17. class MyThread implements Runnable {  
  18.     // 声明锁  
  19.     private Lock lock = new ReentrantLock();  
  20.       
  21.     // 变量 a 被两个线程共同操作,可能会造成线程竞争  
  22.     int a = 10;  
  23.     @Override  
  24.     public void run() {  
  25.                 // 加锁  
  26.         lock.lock();  
  27.         for (int i = 0; i < 5; i++) {  
  28.             a -= 1;  
  29.             try {  
  30.                 Thread.sleep(1);  
  31.             } catch (InterruptedException e) {}  
  32.             System.out.println(Thread.currentThread().getName() + " → a = " + a);  
  33.         }  
  34.                 // 解锁  
  35.         lock.unlock();  
  36.     }  
  37. }  


上面的代码给出了给线程枷锁的方式,可以看到,在线程对数据进行操作之前先给此操作加一把锁,那么在此线程对数据进行操作的时候,其他的线程无法对此数据进行操作,只能“阻塞”在一边等待当前线程对数据操作结束后再对数据进行下一次的操作,当前线程在数据的操作完成之后会解开当前的锁以便下一个线程操作此数据。

 

加锁之后的运行结果如下所示,运行结果符合了我们一开始的要求了。

Thread-A → a = 9
Thread-A → a = 8
Thread-A → a = 7
Thread-A → a = 6
Thread-A → a = 5
Thread-B → a = 4
Thread-B → a = 3
Thread-B → a = 2
Thread-B → a = 1
Thread-B → a = 0

3.线程同步

从JDK1.0开始,Java中的每一个对象都拥有一个内部锁,如果一个方法用关键字"synchronized"声明,那么对象的锁将保护整个方法。synchronized关键字使得我们不需要再去创建一个锁对象,而只需要在声明一个方法时加上此关键字,那么方法在被一个线程操作时就会自动的被上锁,这种操作的结果和目的与手动创建Lock对象来对数据进行加锁的结果和目的相类似。

用synchronized关键字加锁来对方法进行加锁:

 

[java]   view plain   copy
 
  1. import java.util.concurrent.locks.Lock;  
  2. import java.util.concurrent.locks.ReentrantLock;  
  3.   
  4. public class TestThread {  
  5.   
  6.     public static void main(String[] args) {  
  7.         MyThread t = new MyThread();  
  8.         Thread ta = new Thread(t, "Thread-A");  
  9.         Thread tb = new Thread(t, "Thread-B");  
  10.         ta.start();  
  11.         tb.start();  
  12.     }  
  13. }  
  14.   
  15. class MyThread implements Runnable {  
  16.     int a = 10;  
  17.         // synchronized 关键字对方法进行加锁  
  18.     @Override  
  19.     public synchronized void run() {  
  20.         for (int i = 0; i < 5; i++) {  
  21.             a -= 1;  
  22.             try {  
  23.                 Thread.sleep(1);  
  24.             } catch (InterruptedException e) {}  
  25.             System.out.println(Thread.currentThread().getName() + " → a = " + a);  
  26.         }  
  27.     }  
  28. }  
其结果和第二节中的结果一致。

 

 

总结:

">synchronized 关键字来对方法进行加锁。以上两种方法都可以有效解决Java多线程中存在的竞争条件的问题。

作者:家有小壮壮、
来源链接:https://www.cnblogs.com/liqiang0728/p/11072332.html

“Java多线程中的竞争条件、锁以及同步的概念” 的相关文章

多线程处理大量数据 java

项目场景: 简述项目相关背景: 例如:获取大量数据并处理,生成execl文件导出 问题描述: 5W条数据处理后生成exe...

java double类型判空,简单封装JAVA空判断

在项目开发过程中,面对各种各样的对象,如果稍不注意,就会发生NULL空指针报错;是不是很烦恼,特别是对重要的参数判读; 经过总结,把各种类型的空判断进行了简...

Java中空对象(null)引用方法及属性简单分析!

Java中空对象(null)引用方法及属性简单分析!

在Java中,对象往往包含属性、方法及一个存储空间,若一个对象的属性和方法是空的则可认为这是一个空对象。 一个空对象也是对象,同样通过对象名引用方法或属性,但和一般对象...

java多线程分发问题——多线程求和

最近读了<java编程思想>的分发部分,又碰到一个网友想利用多线程求和并且汇总求最后结果,具体要求是这样的“写十个线程,第一个线程求1到10的和,第二个11到2...

java中List集合分批处理

java中List集合分批处理

java中List集合分批处理 在项目中存在list集合数据量过大,需要对这个list集合进行分批处理,自己写了一个list分批处理的一个算法...

idea JavaFx项目搭建报错 类文件具有错误的版本55.0,应为52.0

idea JavaFx项目搭建报错 类文件具有错误的版本55.0,应为52.0

JavaFx项目编译提示:..类文件具有错误的版本55.0,应为52.0.. 项目场景: 问题描述:...

Java设计模式学习记录-GoF设计模式概述

Java设计模式学习记录-GoF设计模式概述

前言 最近要开始学习设计模式了,以前是偶尔会看看设计模式的书或是在网上翻到了某种设计模式,就顺便看看,也没有仔细的学习过。前段时间看完了JVM的知识,然后就想着JVM那么费劲的东西...

IDEA出现java: 错误: 不支持发行版本 15

IDEA出现java: 错误: 不支持发行版本 15

java: 错误: 不支持发行版本 15解决方法(英文版的道理也一样) 首先不要慌 打开文件—>项目结构–>项目一定要一致 接着...

Java你现在正在使用哪个版本

Java你现在正在使用哪个版本 1.  Java5(应该没了吧。。。)...

IDEA java版本降级编译,解决JDK版本导致Unsupported major.minor version 52.0 error

Intellij IDEA使用教程相关系列 目录 具体的操作,这位博友整理得很详细https://blog.csdn.net/huyishero/article...