浅谈JAVA同步(一)

同步机制

多线程开发过程中,我们经常会提到同步这个词,我们知道一个多线程应用系统在操作系统的进程(线程)机制下可以同时有多个进程(线程)并发运行,这此进程(线程)要完成的任务可能是互不相关的,但也可能是有联系的。那么当一个进程(线程)要和另一个进程(线程)交流信息时同步就有可能发生了。


JAVA同步机制

JAVA中cpu分给每个线程的时间片是随机的并且在java中好多都是多个线程共用一个资源,JAVA为了实现同步机制提供了synchronized关键字,我们可以使用它来定义被同步的对象以及临界区,临界区的范围是由一组大括号来标识的。而进出临界区时必要的加锁和解锁操作则是由Java内置支持的。


接下来,我们举一个简单的火车购票的例子窥探一下JAVA中synchronized关键字的作用。在这个例子中,火车站有3个窗口售票,总共的票数有10张。

为了方便对比,我们先看一下没有使用synchronized关键字时的效果

这是窗口线程:

package com.test;

public class TicketSource implements Runnable
{
    //票的总数
    private int ticket=10;
    public void run()
    {
        for(int i=1;i<50;i++)
        {
            if(ticket>0)
            {
                //休眠1s秒中,为了使效果更明显,否则可能出不了效果
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"号窗口卖出"+this.ticket--+"号票");
            }
        }
    }
}

这是测试类:

package com.test;

public class Test {
	 public static void main(String args[])
	    {
	        TicketSource mt=new TicketSource();
	        //基于火车票创建三个窗口
	        new Thread(mt,"a").start();
	        new Thread(mt,"b").start();
	        new Thread(mt,"c").start();
	    }
}

我们来看一下输出:



我们可以看到a号窗口和和b号窗口都卖出了4号票,并且c号窗口卖出了0号。

造成这种情况的原因是:

(1)a线程和b线程在ticket=4的时候,a线程取出4号票以后,ticket还没来的及减1,b线程就取出了ticket,此时ticket还等于4。

(2)在ticket=1时,a线程取出了1号票,ticket还没来的及减1,c线程就进入了if判断语句,这时回到了a线程中,ticket减1了,那么当c线程取票的时候就取到了0号票。


那么,当出现了这样的情况,我们可以这样做:当一个线程要使用火车票这个资源时,我们就交给它一把锁,等它把事情做完后在把锁给另一个要用这个资源的线程。这样就不会出现上述情况。 

实现这个锁的功能就需要用到synchronized这个关键字。

synchronized这个关键字有两种用法:

(1)放方法名前形成同步方法

(2)放在块前构成同步块


1.使用同步方法

新的窗口线程:

package com.test;

public class TicketSource2 implements Runnable
{
    //票的总数
    private int ticket=10;
    public void run()
    {
        for(int i=1;i<50;i++)
        {
            try {
                //休眠1s秒中,为了使效果更明显,否则可能出不了效果
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.sale();
        }
    }
    
    public synchronized void sale()
    {
            if(ticket>0)
            {
                System.out.println(Thread.currentThread().getName()+"号窗口卖出"+this.ticket--+"号票");
            }
    }
}

同样的测试类,我们看一下输出:


这样就符合情况了!


2.使用同步块

新的窗口线程:

package com.test;

public class TicketSource3 implements Runnable
{
    //票的总数
    private int ticket=10;
    public void run()
    {
        for(int i=1;i<50;i++)
        {
            synchronized(this){
                if(ticket>0)
                {
                    //休眠1s秒中,为了使效果更明显,否则可能出不了效果
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"号窗口卖出"+this.ticket--+"号票");
                }
                              }
        }
    }
}

同样的测试类,我们看一下输出:


这样就符合情况了!


在下一篇文章中,笔者会提供一些JAVA同步中更为细节的问题分享给大家。

如有问题,欢迎大家指出!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值