JAVA 多线程——线程竞争

下面再来看一个关于线程竞争的例子,记得学过了操作系统课程里对线程的进程做过了一定程度的讲解,但当时对于所谓的同步和互斥方法也并不是很了解。最好的方法还是通过代码来理解

class Acount
{
    public static int money = 100;
    public static void save(int count)
    {
        money += count;
    }
    public static void get(int count)
    {
        money -= count;
    }
    public static void show()
    {
        System.out.println(money);
    }
    public static int getMoney()
    {
        return money;
    }
}

首先这里提供了一个银行账号,我们可以通过静态方法来进行取钱、存钱的基本操作。

再来看下面的实现run的接口

class MyRunnable implements Runnable 
{
     private int i = 0;

        @Override
     public void run() 
     {
        for (i = 0; i < 10; i++) 
        {
            if(Acount.getMoney() > 0)
            {
               Acount.get(80);
            }
               Acount.show();
            try
            {
               Thread.sleep(50);
            } 
            catch (InterruptedException e)
            {
                // TODO Auto-generated catch block
               e.printStackTrace();
            }
               Acount.save(80);
               Acount.show();
            }
       }
}

实现了runable的接口的类,提供了对于这个账号的操作方法。
每一次在取钱之前,先判断是否会透支,若透支,则不取钱了。
以下是Main方法

public class Main
{
    public static void main(String[] args)
    {
        Thread thread1 = new Thread(new MyRunnable());
        Thread thread2 = new Thread(new MyRunnable());
        thread1.start();
        thread2.start();

    }
}

有两个账号对其经行操作

-60
-60
20
-60
20
-60
20
-60
20
-60

这是是运行结果,可以看出来在判断在大于80的情况依然取钱了,这是由于是判断钱够不够,再取钱,所以这两个步骤之前有空隙。导致了这一种情况。

  • 利用lock来解决
    这种情况可以利用lock来进行解决,具体代码如下
class  MyRunnable implements Runnable 
{
     private int i = 0;
     private static final Lock loc = new ReentrantLock();

        @Override
     public  void run() 
     {
        loc.lock();
        for (i = 0; i < 10; i++) 
        {
            if(Acount.getMoney() > 80)
            {
               Acount.get(80);

               Acount.show();
                try
                {
                   Thread.sleep(50);
                } 
                catch (InterruptedException e)
                {
                    // TODO Auto-generated catch block
                   e.printStackTrace();
                }
                   Acount.save(80);
                   Acount.show();
                }
        }
        loc.unlock();
       }

}

几乎是没有什么不同的,知识在run方法中加入了lock的lock()和unLock()方法,来保证一定要执行完这一整套,才让其他的线程来访问这一套方法。

  • synchronized 方法
    对于这个一直把我搞得好糊涂,有时候用synchronized(this)发现不能做到吧{代码}这一块中做到原子访问。后面看别人的博客发现了synchronized(this)是对于对象而言的。
    简而言之 就是synchronized(this)是对于该方法属于的对象而言的,在run()方法中使用synchronized(this)只是会使对于MyRunable的对象进行锁定,而我在main中是使用了两个对象,所以不会起到锁的效果。
    因此应该改成synchronized(lock) 其中lock得是一个静态的属性,若不是静态的属性会发生什么情况?请看下面的代码
class  MyRunnable implements Runnable 
{
     private int i = 0;
     private static final Lock loc = new ReentrantLock();
     private   Acount asd = new Acount();
        @Override
     public  void run() 
     {
        synchronized (asd)
        {
              for (i = 0; i < 10; i++) 
            {
                if(Acount.getMoney() > 80)
                {
                  System.out.println(Thread.currentThread());
                   Acount.get(80);
                   Acount.show();
                    try
                    {
                       Thread.sleep(50);
                    } 
                    catch (InterruptedException e)
                    {
                        // TODO Auto-generated catch block
                       e.printStackTrace();
                    }
                       Acount.save(80);
                       Acount.show();
                    }
                  System.out.println(Thread.currentThread());
            }

        }


     }

}

运行之后依然会不同步,这是因为这个对象每一个类的单独有一个。不能保证共享的情况,若把它改成static便可以共享了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: 你提供的引用内容中包含了一个Java程序的代码片段,该代码片段展示了如何使用多线程进行文件下载。这段代码中,首先创建了一个`MultiThreadDownload`对象,传入了下载路径、保存路径和线程数。然后调用`download()`方法开始下载文件。\[1\] 在代码的第二个引用中,根据文件的长度计算了每个线程下载的起始位置和结束位置。通过循环遍历线程数,计算每个线程的起始位置和结束位置,并进行相应的下载操作。\[2\] 在代码的第三个引用中,首先根据访问的URL路径创建了一个`HttpURLConnection`对象,然后调用`getContentLengthLong()`方法获取文件的字节大小。接着使用`RandomAccessFile`对象调用`setLength()`方法设置本地文件的长度,这个文件是一个空数据文件,通过多线程进行对`RandomAccessFile`对象的本地文件随机位置写入数据。最后关闭文件和断开连接。\[3\] 综上所述,这段代码展示了如何使用多线程进行Java文件的下载操作,并且通过设置文件的长度和使用`RandomAccessFile`对象实现了多线程写入数据的功能。 #### 引用[.reference_title] - *1* *2* *3* [【Java】网络编程——多线程下载文件](https://blog.csdn.net/qq_42470947/article/details/105889839)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值