热门标签 | HotTags
当前位置:  开发笔记 > 人工智能 > 正文

java线程同步操作实例详解

这篇文章主要介绍了java线程同步操作,结合实例形式分析了Java线程同步与锁机制相关原理、操作技巧与注意事项,需要的朋友可以参考下

本文实例讲述了java线程同步操作。分享给大家供大家参考,具体如下:

java线程同步

public class Hello {
  public static void main(String[] args) {
    MyRun myRun0 = new MyRun();
    new Thread(myRun0, "Thread0").start();
    new Thread(myRun0, "Thread1").start();
    new Thread(myRun0, "Thread2").start();
  }
}
class MyRun implements Runnable {
  private int k = 0;
  @Override
  public void run() {
    for (int i = 0; i <3; i++) {
      System.out.println(Thread.currentThread().getName() + "**********" + i);
      k++;
      if (k <= 3) {
        if ("Thread0".equals(Thread.currentThread().getName())) {
          try {
            Thread.sleep(100);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
        System.out.println(Thread.currentThread().getName() + "," + k);
      }
    }
  }
}

输出结果

Thread0**********0
Thread1**********0
Thread2**********0
Thread1,2
Thread2,3
Thread1**********1
Thread2**********1
Thread2**********2
Thread1**********2
Thread0,7
Thread0**********1
Thread0**********2

说明多线程在某些场景是存在问题的,有时候需要线程同步。

同步 synchronized

同步代码块,synchronized(obj){},obj是一个对象,在这里就相当于一把锁,表示一旦有进程抢到了这把锁的钥匙(就是进入了代码块),其他进程将无法进入该锁的代码块(当前代码块其他进程一定是进不来了,其他地方的代码块如果也是用了这把锁,同样进不去),只有代码块执行完,释放锁后,所有进程再重新抢钥匙。

注意,上同一把锁的代码块都会被锁住,这些代码块可能写在不同方法不同位置上。

被同步代码块包住的代码多个线程只能顺次进入。

synchronized (this) {
  k++;
  if (k <= 3) {
    if ("Thread0".equals(Thread.currentThread().getName())) {
      try {
        Thread.sleep(100);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    System.out.println(Thread.currentThread().getName() + "," + k);
  }
}

this表示当前对象,这里考虑的只是运行这个方法,不涉及其它类也不涉及这个类的其它地方需要同步问题,所以用this也是可以的。k增加和输出一个流程内只能有一个线程在访问,所以可以得到想要的输出结果

输出结果

Thread0**********0
Thread1**********0
Thread2**********0
Thread0,1
Thread0**********1
Thread2,2
Thread2**********1
Thread1,3
Thread1**********1
Thread0**********2
Thread2**********2
Thread1**********2

对方法进行同步,如果存在多线程,每个线程顺次访问该方法

注意,如果一个类里面存在多个同步方法,那么这些同步方法的锁是一个,都是当前对象,所以不同线程想同时访问同一对象的不同方法也是不行的,因为这些方法都上了同一把锁,但是钥匙只有一把,只能一个线程持有。

@Override
public synchronized void run() {
  for (int i = 0; i <3; i++) {
    System.out.println(Thread.currentThread().getName() + "**********" + i);
    k++;
    if (k <= 3) {
      if ("Thread0".equals(Thread.currentThread().getName())) {
        try {
          Thread.sleep(100);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
      System.out.println(Thread.currentThread().getName() + "," + k);
    }
  }
}

输出结果

Thread0**********0
Thread0,1
Thread0**********1
Thread0,2
Thread0**********2
Thread0,3
Thread2**********0
Thread2**********1
Thread2**********2
Thread1**********0
Thread1**********1
Thread1**********2

死锁

public class Hello {
  public static void main(String[] args) {
    A a = new A();
    B b = new B();
    new Thread(new MyRun(a,b)).start();
    new Thread(new MyRun1(a,b)).start();
  }
}
class MyRun implements Runnable{
  private A a;
  private B b;
  public MyRun(A a, B b) {
    this.a = a;
    this.b = b;
  }
  @Override
  public void run(){
    a.say(b);
  }
}
class MyRun1 implements Runnable {
  private A a;
  private B b;
  public MyRun1(A a, B b) {
    this.a = a;
    this.b = b;
  }
  @Override
  public void run() {
    b.say(a);
  }
}
class A{
  public synchronized void say(B b){
    System.out.println("A要知道B的信息");
    b.info();
  }
  public synchronized void info(){
    System.out.println("这是A");
  }
}
class B{
  public synchronized void say(A a){
    System.out.println("B要知道A的信息");
    a.info();
  }
  public synchronized void info(){
    System.out.println("这是B");
  }
}

如果两个线程同时进入了两个say方法,就是出现死锁。

关键点在于一个对象的多个同步方法具有相同的锁,都是当前对象。也就是x线程在访问a对象的say方法过程中,y线程是无法访问a对象的info方法的,因为开锁的钥匙已经被x线程抢占了。

上面的程序,如果线程x,y同时进入了两个say方法,a对象同步方法的锁被线程x抢占,b对象同步方法的锁被线程y抢占,此时线程x无法访问b对象的同步方法,线程y无法访问a对象的同步方法。代码中恰好想要访问,所以就出现死锁了。

更多java相关内容感兴趣的读者可查看本站专题:《Java进程与线程操作技巧总结》、《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总》

希望本文所述对大家java程序设计有所帮助。


推荐阅读
  • 本文详细解析了LeetCode第300题——最长递增子序列的解题方法,特别是如何使用动态规划来高效解决问题。文章不仅提供了详细的代码实现,还探讨了常见的错误理解和正确的解题思路。 ... [详细]
  • 第三周课堂测试1、使用汇编语言编写指令时,用一些简单的容易记忆的符号来代替二进制指令,比机器语言更为方便,属于高级语言。(B ... [详细]
  • Java数组面试常见问题及解析
    在Java编程面试中,数组作为基础且重要的知识点,经常成为考察的重点。本文将探讨数组的基础知识和相关面试题,帮助考生更好地准备面试。 ... [详细]
  • 本文介绍了一个基础算法题目,旨在通过求解特定范围内所有数字的阶乘之和来提升编程技能。重点在于理解和实现双重循环结构。 ... [详细]
  • 本文详细介绍了在使用Node.js处理JWT时遇到的'invalid algorithm'错误的解决方案。问题源于生成和验证token时使用的算法不一致,具体表现为生成token时使用HS256算法,而在验证时误用了RS256算法。 ... [详细]
  • 计算机视觉初学者指南:如何顺利入门
    本文旨在为计算机视觉领域的初学者提供一套全面的入门指南,涵盖基础知识、技术工具、学习资源等方面,帮助读者快速掌握计算机视觉的核心概念和技术。 ... [详细]
  • 【Java数据结构和算法】008栈
    目录0、警醒自己一、栈的应用场景和介绍1、栈的应用场景一个实际的场景:我的思考:2、栈的介绍入栈演示图:出栈演示图 ... [详细]
  • 深入理解Git与GitHub:分支管理与冲突解决
    本文详细探讨了Git中的分支管理技术,包括如何创建、切换和合并分支,以及如何有效解决分支合并时可能遇到的冲突。同时,文章还介绍了Git的基本原理,如哈希算法的应用和文件管理机制。 ... [详细]
  • 本章节深入讨论了线性光与感知均匀性的概念,强调了灰度图像中每个像素值如何反映视觉亮度的主观性质。文章进一步解析了亮度与光强度、辐射等物理量的区别,并探讨了这些概念在数字图像处理中的应用。 ... [详细]
  • 本文介绍了两种在MATLAB中用于识别和提取图像中封闭孔洞及其边界的高效方法。第一种方法通过图像填充和差分操作实现;第二种方法则基于Flood-Fill泛洪算法。 ... [详细]
  • 本文探讨了如何通过积累团队管理经验、促进团队成员的学习成长、建立公正的绩效考核体系以及明确奖惩机制来提升团队的整体效能。同时,文章还强调了领导者应具备的关键能力和如何通过团队成员的表现来评估领导者的管理水平。 ... [详细]
  • 多用户密码验证与加密登录系统
    本文介绍了一种基于多用户密码文件的加密登录方法,通过读取用户密码文件并使用简单的加密算法实现安全登录。文中详细描述了程序的设计思路及其实现过程。 ... [详细]
  • 本文通过一个经典问题——使用最少的老鼠在限定时间内找出含有毒药的瓶子,深入探讨了二分法的应用及其背后的逻辑原理。不仅展示了二分法在非传统排序问题中的有效应用,还扩展讨论了三分法的可能性与限制。 ... [详细]
  • 时序数据是指按时间顺序排列的数据集。通过时间轴上的数据点连接,可以构建多维度报表,揭示数据的趋势、规律及异常情况。 ... [详细]
  • 本文详细探讨了 HAProxy 的基本概念及其与 LVS(Linux Virtual Server)的比较,特别是在内核空间与用户空间的工作差异。文章还深入介绍了 HAProxy 独有的数据结构——弹性二叉树,以及其在高并发场景下的表现。 ... [详细]
author-avatar
大美妞Buling-buling
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有