Java线程的互斥

众所周知,在编程的过程中不可避免的会用到多线程,既然有多线程就会考虑到互斥问题,那么我们来看一下Java多线程中的互斥。

下面先举个多线程容易出现的问题。比如,你的银行账户中有1000元,现在你正在网上银行正在给朋友转账(200元),但是同时有朋友在给你转账(500元),当你正要进行转账的前一刻,朋友首先转账成功,那么此时你账户中的钱应该是原来的钱加上朋友转账的钱,即1000+500=1500元,但是此时你正在转账的时刻已经取得了银行账户的目前余额(1000元),然后你按下转账按钮,此时计算账户余额时是使用之前的余额(1000元)减去转出的钱(200元)等于800元,账户重置成800,那这个问题就比较严重了。所以在多线程中,有些片段的执行时需要保持原子操作的,即在此片段执行完成之前其他线程不能来执行该片段,那么这就要用到多线程的互斥。

下面先看如下代码:

package com.alex.lip.iteye.traditional.synchronizeded;

public class TraditionalThreadSynchronized {

 public static void main(String[] args) {
  new TraditionalThreadSynchronized().init();
 }

 private void init() {
  OutPuter outputer = new OutPuter();
  new Thread(new Runnable() {

   @Override
   public void run() {
    while (true) {
     try {
      Thread.sleep(10);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
     outputer.output("liping");
    }

   }
  }).start();

  new Thread(new Runnable() {

   @Override
   public void run() {
    while (true) {
     try {
      Thread.sleep(10);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
     outputer.output("weixiaoshan");
    }

   }
  }).start();
 }

 class OutPuter {
  /**
   * 本方法是未使用同步
   *
   * @param name
   */
  public void output(String name) {
   int length = name.length();
   for (int i = 0; i < length; i++) {
    System.out.print(name.charAt(i));
   }
   System.out.println();
  }
 }
}
运行结果:

beijing
shanghai
beijing
shanghai
beijing
shanghai
beijshanghai
ing
shanghai
beijing

在此可以看出,会出现如上的结果,证明在第一个线程执行中cpu就切换执行第二个线程,出现打印数据混乱,虽然是小概率事件,但是却不容忽视,那么怎么改正呢?从运行结果可以看出,程序在打印输出阶段出现共同调用一段代码,即

 for (int i = 0; i < length; i++) {
    System.out.print(name.charAt(i));
   }
   System.out.println();

出现问题,那么我们可以在这一段加锁,使这一段代码只能被一个线程访问即可,那么但是我们要把锁加在哪个位置呢?首先我们可以锁定outputer对象,因为两个线程是使用同一个对象来调用该方法,所以我们可以做如下改正:

  /**
   * 本方法是使用同步代码块,锁定本类的对象
   *
   * @param name
   */
  public void output1(String name) {
   int length = name.length();
   synchronized (this) {
    for (int i = 0; i < length; i++) {
     System.out.print(name.charAt(i));
    }
    System.out.println();
   }
  }

this代表当前对象,此时就避免了多个线程同时访问该方法,既然该代码块是要同步的,那么我们是否可以给整个方法加上同步呢?答案是可以的,看如下代码:

  /**
   * 本代码使用同步方法,方法上的synchronized关键字锁定的是this对象
   *
   * @param name
   */
  public synchronized void output2(String name) {
   int length = name.length();
   for (int i = 0; i < length; i++) {
    System.out.print(name.charAt(i));
   }
   System.out.println();
  }

,假如我们的类是静态的类又如何解决呢?大家都知道,静态类是可以直接调用其中的方法的,不需要创建对象即可,但是静态类也总是要编译后运行的,那么它总要生成对应的字节码,即class文件,所以我们加锁时可以锁定静态类的class即可:

  public void output1(String name) {
   int length = name.length();
   synchronized (OutPuter.class) {
    for (int i = 0; i < length; i++) {
     System.out.print(name.charAt(i));
    }
    System.out.println();
   }
  }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值