精华内容
下载资源
问答
  • 01457 人力资源竞争力培训-快速构筑人力资源竞争力(DOC 5页).doc
  • 在分析我国煤炭人力资源现状的基础上,结合煤炭行业人力资源需求,依据相关理论构建了煤炭企业人力资源竞争力评价理论模型,提出煤炭企业人力资源竞争力评价理论指标体系,并进一步凝练出了具有一定代表性的应用指标体系...
  • GCD资源竞争Demo

    2014-01-23 21:05:06
    GCD资源竞争Demo。注意,别运行imagegcd2.m,这是个反面教材。
  • 主要介绍了Java多线程下解决资源竞争的7种方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 在“入世”后,我国企业正面临激烈的国际市场竞争,由于人力资源开发与管理具有的复杂性和不确定性以及由此形成的竞争优势难以模仿等,使得人力资源开发已成为企业获取竞争力的重要途径,人力资源竞争力成为企业生存...
  • 介绍了基于资源竞争决策的森林火灾扑救智能决策支持系统的结构、开发原理以及模型建立,同时提 出了资源竞争决策的思想。资源竞争决策是一种能够应用于智能决策支持系统中的复杂的决策方法,具有应用 领域广、适应性强...
  • 资源竞争与并发控制

    2013-02-16 17:10:38
    非常浅显的讲述《资源竞争与并发控制》。 1.应用层并发控制?不是重点 2.Lost Update?乐观锁与悲观锁 3.隔离级别:脏读Dirty Read+幻读Phantom Read 4.InnoDB锁与多版本控制? 5.意向锁? 6.事务传播行为?
  • 协程并发资源竞争问题
  • 首先要明确一点, Python中的多线程并不是真正地多个任务同步执行, 而是给每个任务分配一部分的执行时间, 轮流执行, 因此资源竞争的问题就会随之而来 比如当有两个线程或者多个线程对同一全局变量同时操作时, 问题就...

    首先要明确一点, Python中的多线程并不是真正地多个任务同步执行, 而是给每个任务分配一部分的执行时间, 轮流执行, 因此资源竞争的问题就会随之而来
    比如当有两个线程或者多个线程对同一全局变量同时操作时, 问题就会产生

    资源竞争产生的问题

    import threading
    import time
    
    def add_a(count):
        global num
        print('a是'+str(num))
        for i in range(count):
            num += 1
        print('a最终是'+str(num))
    
    
    def add_b(count):
        global num
        print('b是'+str(num))
        for i in range(count):
            num += 1
        print('b最终是'+str(num))
    
    
    def main():
        t1 = threading.Thread(target=add_a, args=(1000000,))
        t2 = threading.Thread(target=add_b, args=(1000000,))
        t1.start()
        t2.start()
        time.sleep(1)
        print(num)
    
    
    num = 0
    lock = threading.Lock()
    
    if __name__ == '__main__':
        main()
        
    

    在这里插入图片描述
    这里我们会发现, 我们给两个函数传递的参数是1000000,每个函数都是进行100w次的+1操作, 按照我们的常识来说, 最后的结果应该是200w才对, 但是结果却是1514861(这里的结果并不是固定的)
    产生这种结果的原因是因为python的解释器会把一个简单的+1操作分成多步:

    1. 获取num的值
    2. 将num的值+1
    3. 将运算完成的值赋给num

    又因为这是多线程的, 所以cpu在处理两个线程的时候, 是采用雨露均沾的方式, 可能在线程一刚刚将num值+1还没来得及将新值赋给num时, 就开始处理线程二了, 因此当线程二执行完全部的num+=1的操作后, 可能又会开始对线程一的未完成的操作, 而此时的操作停留在了完成运算未赋值的那一步, 因此在完成对num的赋值后, 就会覆盖掉之前线程二对num的+1操作

    解决问题

    在threading中有一个Lock类,通过调用Lock类中的acquire()方法, 可以将后面的代码保护起来一直执行, 其他的线程会处于监听状态,直到监听到那个线程调用了release()方法解锁, 才会继续争夺对cpu的使用权

    import threading
    import time
    
    
    def add_a(count):
        global num
        print('a是'+str(num))
        for i in range(count):
            lock.acquire()
            num += 1
            lock.release()
        print('a最终是'+str(num))
    
    
    def add_b(count):
        global num
        print('b是'+str(num))
        for i in range(count):
            lock.acquire()
            num += 1
            lock.release()
        print('b最终是'+str(num))
    
    
    def main():
        t1 = threading.Thread(target=add_a, args=(1000000,))
        t2 = threading.Thread(target=add_b, args=(1000000,))
        t1.start()
        t2.start()
        time.sleep(1)
        print(num)
    
    num = 0
    lock = threading.Lock()
    
    if __name__ == '__main__':
        main()
    
    

    在这里插入图片描述

    展开全文
  • 多线程可以共享全局变量,由此引出资源竞争而导致的问题。#资源竞争导致的问题复现 import threading num = 0 def work1(): global num for i in range(1000000):#数要足够大才能显现出问题 num += 1 def work...

    在Python中多线程的创建、调用方式与Java基本一致。多线程可以共享全局变量,由此引出资源竞争而导致的问题。


    #资源竞争导致的问题复现
    import threading
    
    num = 0
    
    def work1():
        global num
        for i in range(1000000):#数要足够大才能显现出问题
            num += 1
    
    def work2():
        global num
        for i in range(1000000):
            num += 1
    
    if __name__ == "__main__":
        w1 = threading.Thread(target = work1)
        w2 = threading.Thread(target = work2)
        w1.start()
        w2.start()
        w1.join()
        w2.join()
        print(num)
    #测试输出结果:

    1629026

    问题原因分析:

    for循环执行次数比较小时输出结果正确,而次数一但多起来问题就会显现。

    线程在CPU上一般是并发执行,在第一个线程执行时间很短时的情况下,CPU还没有进行线程执行权切换时就线程已经执行完毕退出,此时程序输出结果正确,一但线程执行时间变长就有可能就会导致线程1正在执行时CPU把执行权交给线程2,这样就会导致计算结果出现差错。

    解决方案:

    使用互斥锁,每次只允许一个进程获取、修改全局变量。

    #使用线程锁解决线程的资源竞争问题
    import threading
    
    num = 0
    
    def work1(lock):
        global num
        for i in range(1000000):
            lock.acquire()
            num += 1
            lock.release()
    
    def work2(lock):
        global num
        for i in range(1000000):
            lock.acquire()
            num += 1
            lock.release()
    
    if __name__ == "__main__":
        lock = threading.Lock()
        w1 = threading.Thread(target = work1,args = (lock,))
        w2 = threading.Thread(target = work2,args = (lock,))
        w1.start()
        w2.start()
        w1.join()
        w2.join()
        print(num)
    


    展开全文
  • java资源竞争问题(线程互斥)

    千次阅读 2014-05-22 13:31:56
    资源竞争 (线程互斥)  1、什么是资源竞争   有这样一种资源,在某一时刻只能被一个线程所使用:比如打印机、某个文件等等,如果多个线程不加控制的同时使用这类资源,必然会导至错误。   下面的例子模拟了一...
    资源竞争 (线程互斥)
     
    
    1、什么是资源竞争  
      有这样一种资源,在某一时刻只能被一个线程所使用:比如打印机、某个文件等等,如果多个线程不加控制的同时使用这类资源,必然会导至错误。 
      下面的例子模拟了一个打印机,多个线程不加控制的同时使用这个打印机: 
    Java代码   收藏代码
    1. public class Printer  
    2. {  
    3.     public void print(int printer, String content)  
    4.     {  
    5.         System.out.println("Start working for [" +printer+"]");  
    6.         Thread.yield();  
    7.         System.out.println("===================");  
    8.         Thread.yield();  
    9.         System.out.println(content);  
    10.         Thread.yield();  
    11.         System.out.println("===================");  
    12.         Thread.yield();  
    13.         System.out.println("Work complete for [" +printer+"]\n");  
    14.     }  
    15.       
    16.     public static void main(String[] args)  
    17.     {  
    18.         Printer p = new Printer();  
    19.           
    20.         for (int i=0; i<3; i++)  
    21.             new Thread(new MyThread(p)).start();  
    22.     }  
    23. }  
    24.   
    25. class MyThread implements Runnable  
    26. {  
    27.     private static int counter = 0;  
    28.     private final int id = counter++;  
    29.       
    30.     private Printer printer;  
    31.       
    32.     public MyThread(Printer printer)  
    33.     {  
    34.         this.printer = printer;  
    35.     }  
    36.       
    37.     @Override  
    38.     public void run()  
    39.     {  
    40.         printer.print(id, "Content of " + id);  
    41.     }  
    42.       
    43. }  

      输出结果(sample) 
    Output代码   收藏代码
    1. Start working for [0]  
    2. Start working for [1]  
    3. ===================  
    4. ===================  
    5. Start working for [2]  
    6. Content of 0  
    7. Content of 1  
    8. ===================  
    9. ===================  
    10. ===================  
    11. Content of 2  
    12. Work complete for [0]  
    13.   
    14. Work complete for [1]  
    15.   
    16. ===================  
    17. Work complete for [2]  

      从结果可以看到,打印机的输出完全乱套了,各个线程想要打印的内容全部参杂在一起了。 


    2、解决资源竞争问题 
      原则上要解决这类问题并不难,只需要一个锁的机制。任何线程在使用打印机前必须先对打印机上锁;在使用完打印机后释放锁;如果线程尝试对打印机上锁时别的线程已经上了锁,则该线程必须等待别的线程先释放锁。 
      Java中,解决上述资源共享类的问题是通过关键字synchronized实现的。java中的对象都有一个‘锁’,这样,任何一个线程尝试访问对象的synchronized方法时,必须要先获得对象的'锁',否则必须等待。 
      一个对象可能会有多个synchronized方法,比如synchronized a()方法和synchronized b()方法。当一个线程获得了对象的锁,进行a()方法、或b()方法了,那么在线程释放该对象的锁之前,别的线程是不能访问该对象的其它synchronized方法的。 
      下面例子是之前的例子的改良版本,只需要简单的把Printer对象的print方法定义成synchronized的就可以达到我们的要求了: 

    Java代码   收藏代码
    1. public class Printer  
    2. {  
    3.     public synchronized void print(int printer, String content)  
    4.     {  
    5.         System.out.println("Start working for [" +printer+"]");  
    6.         Thread.yield();  
    7.         System.out.println("===================");  
    8.         Thread.yield();  
    9.         System.out.println(content);  
    10.         Thread.yield();  
    11.         System.out.println("===================");  
    12.         Thread.yield();  
    13.         System.out.println("Work complete for [" +printer+"]\n");  
    14.     }  
    15.       
    16.     public static void main(String[] args)  
    17.     {  
    18.         Printer p = new Printer();  
    19.           
    20.         for (int i=0; i<3; i++)  
    21.             new Thread(new MyThread(p)).start();  
    22.     }  
    23. }  
    24.   
    25. class MyThread implements Runnable  
    26. {  
    27.     private static int counter = 0;  
    28.     private final int id = counter++;  
    29.       
    30.     private Printer printer;  
    31.       
    32.     public MyThread(Printer printer)  
    33.     {  
    34.         this.printer = printer;  
    35.     }  
    36.       
    37.     @Override  
    38.     public void run()  
    39.     {  
    40.         printer.print(id, "Content of " + id);  
    41.     }  
    42.       
    43. }  

      输出结果 
    Output代码   收藏代码
    1. Start working for [0]  
    2. ===================  
    3. Content of 0  
    4. ===================  
    5. Work complete for [0]  
    6.   
    7. Start working for [2]  
    8. ===================  
    9. Content of 2  
    10. ===================  
    11. Work complete for [2]  
    12.   
    13. Start working for [1]  
    14. ===================  
    15. Content of 1  
    16. ===================  
    17. Work complete for [1]  

      从结果的输出可以看出来,被模拟的打印机资源在某一时刻,仅被一个线程所使用。 


    3、临界区 
      有些时候,你可能不需要隔离整个方法,而只需要隔离方法中的部分代码,这部分被隔离的代码就叫做临界区。临界区中的代码在某一时刻,只能被一个线程访问。 
      下面的例子,是用临界区的方式实现了前面的例子: 

    Java代码   收藏代码
    1. public class Printer  
    2. {  
    3.     public void print(int printer, String content)  
    4.     {  
    5.         synchronized(this)  
    6.         {  
    7.             System.out.println("Start working for [" +printer+"]");  
    8.             Thread.yield();  
    9.             System.out.println("===================");  
    10.             Thread.yield();  
    11.             System.out.println(content);  
    12.             Thread.yield();  
    13.             System.out.println("===================");  
    14.             Thread.yield();  
    15.             System.out.println("Work complete for [" +printer+"]\n");  
    16.         }  
    17.     }  
    18.       
    19.     public static void main(String[] args)  
    20.     {  
    21.         Printer p = new Printer();  
    22.           
    23.         for (int i=0; i<3; i++)  
    24.             new Thread(new MyThread(p)).start();  
    25.     }  
    26. }  
    27.   
    28. class MyThread implements Runnable  
    29. {  
    30.     private static int counter = 0;  
    31.     private final int id = counter++;  
    32.       
    33.     private Printer printer;  
    34.       
    35.     public MyThread(Printer printer)  
    36.     {  
    37.         this.printer = printer;  
    38.     }  
    39.       
    40.     @Override  
    41.     public void run()  
    42.     {  
    43.         printer.print(id, "Content of " + id);  
    44.     }  
    45.       
    46. }  

      可以看到,在Java中临界区也是通过synchronized关键字实现的。在synchronized关键字后面,要传一个对象参数,任何线程要进入临界区时必须先要获得该对象的锁,退出临界区时要释放该对象的锁,这样别的线程才有机会进入临界区。 
      从上面两个例子可以看出,临界区和synchronized方法,其原理都是一样的,都是通过在对象上加锁来实现的,只不过临界区来得更加灵活,因为它不光可以对this对象加锁,也可以对任何别的对象加锁。 


    4、Lock 
      Java1.5提供了一个显示加锁的机制,比起synchronized方式来说,显示加锁的方法可能让代码看上去更加复杂,但是也带来了更好的灵活性。 
      下面的例子,用Lock的机制实现了前面的例子: 

    Java代码   收藏代码
    1. public class Printer  
    2. {  
    3.     private Lock lock = new ReentrantLock();  
    4.       
    5.     public void print(int printer, String content)  
    6.     {  
    7.         lock.lock();  
    8.           
    9.         try  
    10.         {  
    11.             System.out.println("Start working for [" +printer+"]");  
    12.             Thread.yield();  
    13.             System.out.println("===================");  
    14.             Thread.yield();  
    15.             System.out.println(content);  
    16.             Thread.yield();  
    17.             System.out.println("===================");  
    18.             Thread.yield();  
    19.             System.out.println("Work complete for [" +printer+"]\n");  
    20.         }  
    21.         finally  
    22.         {  
    23.             lock.unlock();  
    24.         }  
    25.     }  
    26.       
    27.     public static void main(String[] args)  
    28.     {  
    29.         Printer p = new Printer();  
    30.           
    31.         for (int i=0; i<3; i++)  
    32.             new Thread(new MyThread(p)).start();  
    33.     }  
    34. }  
    35.   
    36. class MyThread implements Runnable  
    37. {  
    38.     private static int counter = 0;  
    39.     private final int id = counter++;  
    40.       
    41.     private Printer printer;  
    42.       
    43.     public MyThread(Printer printer)  
    44.     {  
    45.         this.printer = printer;  
    46.     }  
    47.       
    48.     @Override  
    49.     public void run()  
    50.     {  
    51.         printer.print(id, "Content of " + id);  
    52.     }  
    53.       
    54. }  

      使用Lock的时候,必须要注意两点: 
    • 锁的释放必须放在finally块里面,以保证锁被正确的释放;
    • 如果被隔间的方法或临界间需要返回一个值,那么return语句应该放在try块中,从而不至于使unlock发生得过早而导至错误的发生。

    使用显示的Lock机制,可以让程序更加的灵活。比如上面的例子中,如果尝试使用打印机的时候,打印机正被别的线程所使用,那么早取消本次打印。要实现这样的功能,使用synchronized可能不太容易实现,但是使用Lock机制的话,就非常简单了: 

    Java代码   收藏代码
    1. public class Printer  
    2. {  
    3.     private Lock lock = new ReentrantLock();  
    4.       
    5.     public void print(int printer, String content)  
    6.     {  
    7.         boolean isLocked = lock.tryLock();  
    8.           
    9.         if (!isLocked)  
    10.             return;  
    11.           
    12.         try  
    13.         {  
    14.             System.out.println("Start working for [" +printer+"]");  
    15.             Thread.yield();  
    16.             System.out.println("===================");  
    17.             Thread.yield();  
    18.             System.out.println(content);  
    19.             Thread.yield();  
    20.             System.out.println("===================");  
    21.             Thread.yield();  
    22.             System.out.println("Work complete for [" +printer+"]\n");  
    23.         }  
    24.         finally  
    25.         {  
    26.             if(isLocked)  
    27.                 lock.unlock();  
    28.         }  
    29.     }  
    30.       
    31.     public static void main(String[] args)  
    32.     {  
    33.         Printer p = new Printer();  
    34.           
    35.         for (int i=0; i<3; i++)  
    36.             new Thread(new MyThread(p)).start();  
    37.     }  
    38. }  
    39.   
    40. class MyThread implements Runnable  
    41. {  
    42.     private static int counter = 0;  
    43.     private final int id = counter++;  
    44.       
    45.     private Printer printer;  
    46.       
    47.     public MyThread(Printer printer)  
    48.     {  
    49.         this.printer = printer;  
    50.     }  
    51.       
    52.     @Override  
    53.     public void run()  
    54.     {  
    55.         printer.print(id, "Content of " + id);  
    56.     }  
    57.       
    58. }  

      Lock.tryLock()方法尝试对lock对象加锁并返回一个boolean值,如果成功了,返回true,表明当前没有别的线程在使用打印机,那么当前线程将获得lock对象的锁,并继续打印;如果失败了,返回false,表明别的线程正在使用打印机,当前线程将简单的返回,而不是等待别的线程释放锁。

    展开全文
  • // 令当前线程去竞争资源 acquire(1); } 2. acquire令当前线程竞争资源的方法 // 位于AQS下的acquire:令当前线程去竞争资源的方法 public final void acquire(int arg) { // 条件1:!tryAcquire(arg)方法 尝试...

    文章参考:小刘老师源码

    AQS成员方法解析

    1. lock加锁方法

    // 位于ReentrantLock类的静态内部类Sync中:加锁方法
    final void lock() {
        // 令当前线程去竞争资源
        acquire(1);
    }
    

    2. acquire令当前线程竞争资源的方法

    // 位于AQS下的acquire:令当前线程去竞争资源的方法
    public final void acquire(int arg) {
        // 条件1:!tryAcquire(arg)方法 尝试获取锁,获取成功返回true,获取失败返回false
        // 条件2.1:addWaiter方法 将当前线程封装成node入队
        // 条件2.2:入队后调用 acquireQueued方法 (该方法包含挂起当前线程、以及线程唤醒后相关的逻辑)
        // 		   (令当前线程不断去竞争资源,直到成功获取锁才停止自旋)
        // acquireQueued方法返回boolean类型,true:表示挂起过程中线程中断唤醒过,false:表示未被中断唤醒过
        if (!tryAcquire(arg) && 
            // Node.EXCLUSIVE 当前节点是独占模式
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    

    3. tryAcquire尝试获取锁的方法

    // 位于ReentrantLock类的静态内部类Sync中:位于尝试获取锁的方法,不会阻塞线程
    // 返回true -> 尝试获取锁成功 | 返回false -> 尝试获取锁失败
    protected final boolean tryAcquire(int acquires) {
        // 当前线程
        final Thread current = Thread.currentThread();
        // AQS中的state(加锁状态)值
        int c = getState();
        // 如果条件成立:c == 0 表示当前AQS处于无锁状态
        if (c == 0) {
            // 因为fairSync是公平锁,任何时候都需要检查一下在当前线程之前,队列中是否有等待者
            // 条件1:hasQueuedPredecessors 判断FIFO队列是否为空 
            // true -> 表示当前线程前面有等待者线程,当前线程需要入队等待
            // false -> 表示当前线程前面没有等待者线程,直接可以尝试获取锁
            if (!hasQueuedPredecessors() &&
                // 条件2:compareAndSetState(0, acquires) 基于CAS去更新state的值
                // state更新成功:说明当前线程抢占锁成功!
                // state更新失败:说明多个线程存在竞争,当前线程竞争失败,未能抢到锁的持有权
                compareAndSetState(0, acquires)) {
                // 条件1、2均成立时:说明当前线程抢夺锁的持有权成功!
                // 设置当前线程为独占线程(锁的持有者线程)
                setExclusiveOwnerThread(current);
                // true -> 当前线程尝试获取锁成功
                return true;
            }
        }
        // current == getExclusiveOwnerThread():用于判断当current !=0 或者 >0 的情况下
        // 当前线程是否是持有锁的线程(独占线程),因为ReentrantLock是可重入的锁,获取锁的线程可以再次进入~
        // 如果条件成立:说明当前线程就是独占锁的线程
        else if (current == getExclusiveOwnerThread()) {
            // 获取当前线程的加锁状态,并累加
            int nextc = c + acquires;
            // 越界判断...当冲入的深度很深时,会导致 nextc < 0,因为 int值达到MAX最大之后,再+1,会变复数
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            // 更新加锁状态
            setState(nextc);
            // true -> 当前线程尝试获取锁成功
            return true;
        }
        // false -> 尝试获取锁失败的情况:
        // 1.CAS加锁失败 且 当前线程前面有等待的线程
        // 2.state > 0 且 当前线程不是占用锁的线程
        return false;
    }
    

    4.addWaiter将当前线程添加到阻塞队列的方法

    // 位于AQS下:将当前线程添加到阻塞队列的方法
    // 最终返回包装当前线程的node
    private Node addWaiter(Node mode) {
        // 构建Node,把当前线程封装到Node中,mode:Node节点的模式,例如Node.EXCLUSIVE 当前节点是独占模式
        Node node = new Node(Thread.currentThread(), mode);
        // 线程快速入队方式:
        // 获取队尾节点,保存到pred
        Node pred = tail;
        if (pred != null) {// 如果条件成立:说明队列中已经有node了
            // 令当前节点node的前驱等于pred
            node.prev = pred;
            // 基于CAS更新队尾tail
            if (compareAndSetTail(pred, node)) {
                // tail更新成功:前驱节点等于node,完成双向绑定
                pred.next = node;
                // 返回node
                return node;
            }
        }
        // 线程完整入队方式(自旋入队):
        // 执行到这里有以下2种情况:
        // 1.tail == null 当前队列是空队列
        // 2.cas设置当前newNode 为 tail 时失败了,被其他线程抢先一步了
        // 自旋入队,只有入队成功才结束自旋:
        enq(node);
        
        // 返回node
        return node;
    }
    

    5. enq当前线程完整入队的方法(自旋入队)

    private Node enq(final Node node) {
        // 自旋~ 只有封装当前线程的node入队成功,才会跳出循环
        for (;;) {
            Node t = tail;
            // 第1种情况:空队列 ===> 即,当前线程是第一个抢占锁失败的线程
            // 当前持有锁的线程(注:tryAcquire方法直接获取到锁的线程,在该方法逻辑中,并没有将持锁线程入队,
            // 而按理说阻塞队列的head节点就应该是当前持有锁的线程才对)并没有设置过任何 node,
            // 所以作为该线程的第一个后驱next,需要给它擦屁股(给持锁线程补一个node节点并设置为阻塞队列的head
            // head节点任何时候,都代表当前占用锁的线程)
            if (t == null) {
                // 如果compareAndSetHead条件成立:说明当前线程给当前持有锁的线程,补充head操作成功了!
                if (compareAndSetHead(new Node()))
                    // tail = head 表示当前队列只有一个元素,这里就表名当前持锁的线程被放入阻塞队列且为head了~
                    tail = head;
                	// 注意:并没有直接返回,还会继续自旋,下次再进入循环时阻塞队列已经不为空,且head为持锁线程节点了...
            } else {
                // 其他情况,说明:当前队列中已经有node了,这里是一个追加node的过程
    
                // 如何入队呢?和 addWaiter方法入队逻辑一样~
                // 1.找到newNode的前置节点 pred
                // 2.更新newNode.prev = pred
                // 3.CAS更新tail为 newNode
                // 4.更新 pred.next = newNode
    
                // 前置条件:队列已经有等待者node了(不为空),当前node并不是第一个入队的node
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    // 如果条件成立,说明当前线程成功入队!
                    t.next = node;
                    // 注意:入队成功,一定要return终止无限for循环~
                    // 返回这个节点t
                    return t;
                }
            }
        }
    }
    

    6. acquireQueued真正去竞争资源的方法

    acquireQueued需要做什么呢?

    • 1.当前节点如果没有被park挂起,则 ===> 挂起当前线程。
    • 2.线程唤醒后 ===> 需要做一些线程唤醒之后的逻辑。
    // 位于AQS中:真正去竞争资源的方法
    // 参数final Node node:封装当前线程的node,且当前时刻该node已经入队成功了
    // 参数arg:当前线程抢占资源成功后,更新state值时要用到
    // 返回true:表示挂起过程中线程中断唤醒过,返回false:表示未被中断唤醒过
    final boolean acquireQueued(final Node node, int arg) {
        // true:表示当前线程抢占锁成功
        // false:表示当前线程抢占锁失败,需要执行出队逻辑
        boolean failed = true;
        try {
            // 当前线程是否被中断
            boolean interrupted = false;
            // 自旋~
            for (;;) {
                // 什么情况下回执行到这里?
                // 1.进入for循环时,在线程尚未被park前会执行
                // 2.线程park后,被唤醒之后也会执行
                
                // 获取当前节点的前驱节点
                final Node p = node.predecessor();
                // p == head 条件成立时:说明当前node为head的节点的后驱(head.next),head.next在任何时候都有权利去争夺锁。
                // tryAcquire 尝试去获取锁,如果条件成立,说明head对应的线程已经释放锁了,而作为head的后驱节点的线程,刚好可以获取锁。
                // tryAcquire 如果条件不成立:说明head对应的线程尚未释放锁,而作为head的后驱节点的线程,这时候仍需要继续park挂起~
                if (p == head && tryAcquire(arg)) {
                    // 拿到锁~
                    // 设置封装当前线程的节点为head节点(head无论什么时候都是持锁线程的节点)
                    setHead(node);
                    // 将上一个线程对应的node的next引用设置为null,帮助GC回收。即,老head出队~
                    p.next = null; // help GC
                    // 当前线程获取锁的过程中,没有发生异常
                    failed = false;
                    // 返回当前线程的中断标记~
                    return interrupted;
                }
                
                // shouldParkAfterFailedAcquire: 判断当前线程获取锁资源失败后,是否需要挂起
                // true: 需要挂起 | false:不需要挂起
                if (shouldParkAfterFailedAcquire(p, node) &&
                    // parkAndCheckInterrupt: 挂起当前线程,并在该线程唤醒之后,返回当前线程的interrupted中断标记
                    // 唤醒该线程的方式:
                    // 1.正常唤醒:其他线程调用 unpark方法,唤醒该线程
                    // 2.其他线程给当前挂起的线程一个中断信号(中断挂起)
                    parkAndCheckInterrupt())
                    // interrupted = true 表示当前node对应的线程是被中断信号唤醒的
                    interrupted = true;
            }
        } finally {
            // 当failed为true时:
            if (failed)
                // node节点的取消线程资源竞争
                cancelAcquire(node);
        }
    }
    

    7.shouldParkAfterFailedAcquire方法

    // 位于AQS中: 判断当前线程获取锁资源失败后,是否需要挂起
    // true: 需要挂起 | false:不需要挂起
    // 参数1:Node pred 当前线程的前驱节点
    // 参数2:Node node 封装当前线程的节点
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        // 获取前驱节点的状态waitStatus
        // 0: 默认状态 | -1:Signal状态(表示当前节点释放锁后会唤醒它的第一个后驱节点) |
        // >0:表示当前节点是CANCELED状态
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)// 如果条件成立,则表示前驱节点是可以唤醒当前线程节点的节点
            // 返回true后,在acquireQueue方法中会继续调用parkAndCheckInterrupt方法去park当前线程节点
            // 注意:一般情况下,第一次来到shouldParkAfterFailedAcquire方法中时,ws不会是-1
            return true;
        // 如果ws>0条件成立:表示当前节点是CANCELED状态
        if (ws > 0) {
            // 该循环是一个找pred.waitStatus > 0 的前驱节点的过程:
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            // 找到符合条件的前驱节点后,令其下一个节点为当前线程的node
            // 隐含着一种操作:即,CANCELED状态的节点会被出队 
            pred.next = node;
        } else {
            // 当前node前驱节点的状态就是0,即默认状态这种情况
            // 将当前线程node的前驱节点的状态,强制设置为SIGNAL,表示该节点释放锁后会唤醒它的第一个后驱节点
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
    
    // 位于AQS中:挂起当前线程节点
    private final boolean parkAndCheckInterrupt() {
        // 线程挂起
        LockSupport.park(this);
        return Thread.interrupted();
    }
    

    总结:

    • 1.如果当前节点的前置节点是 CANCELED取消状态,则:
      • 第1次来到这个方法时,会越过取消状态的节点。
      • 第2次返回true,然后park挂起当前线程。
    • 2.如果当前节点的前置节点是 0 默认状态,则:
      • 当前线程会设置前置节点的状态为 -1
      • 第2次自旋来到这个方发时,会返回true,然后park挂起当前线程。

    展开全文
  • //进程0 while(True){ while(turn!=0); critical_section(); turn = 1; noncritical_section(); }
  • 资源竞争问题 如上,我创建类两个线程t1,t2.我不知道谁先执行结束,我如何保证number是100,而不是0呢,这里我们引入C++ 11当中的技术叫做条件量 condition_variable ,当然我们还需要其他工具 mutex ,来保证...
  • java多线程(二)解决共享资源竞争

    千次阅读 2016-05-12 20:18:31
    public EvenChecker(IntGenerator g, int id){ //所有任务都使用同一个Generator(访问同一个资源) generator = g; this.id = id; } @Override public void run() { while( !generator.isCanceled() )...
  • 我能想到的有三种方式:1)原子操作。2)副本。如ThreadLocal、CopyOnWrite等。3)不可变。
  • 竞争性编程 竞争性编程资源 参考
  • 比较竞争对手人力资源状况
  • 竞争性编程:竞争性编程的一站式资源
  • golang并发资源竞争

    千次阅读 2018-03-20 13:16:10
    我们这里的runtime.Gosched()是让当前goroutine暂停的意思,退回执行队列,让其他等待的goroutine运行,目的是让我们演示资源竞争的结果更明显。注意,这里还会牵涉到CPU问题,多核会并行,那么资源竞争的效果更明显...
  • 论述了IT资源与持续竞争优势之间的关系,客观地评价了IT资源在企业资源观下的重要意义。
  • 在总结煤炭资源型城市特点、分析城市竞争力影响因素和绿色发展影响因素的基础上,利用德尔菲法初步形成了煤炭资源型城市绿色竞争力影响因素识别体系,分为绿色资源、绿色环境、绿色经济、绿色社会4个方面,共包括50项...
  • 应用可持续性分析工具——企业资源观(RBV),对信息技术(IT)资源与持续竞争优势的关系进行理论评述。分别就IT有形资源、IT无形资源、IT人力资源及其互补作用对持续竞争优势的贡献做了详细的分析。研究表明,要...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 348,611
精华内容 139,444
关键字:

资源竞争