竞争条件

1.竞争条件:

在java多线程中,当两个或以上的线程对同一个数据进行操作的时候,可能会产生“竞争条件”的现象。这种现象产生的根本原因是因为多个线程在对同一个数据进行操作,此时对该数据的操作是非“原子化”的,可能前一个线程对数据的操作还没有结束,后一个线程又开始对同样的数据开始进行操作,这就可能会造成数据结果的变化未知。



package com.huojg.test;

public class TestThread {  
      
    public static void main(String[] args) {  
        // new 出一个新的对象 t  
        MyThread t = new MyThread();  
        /** 
         * 两个线程是在对同一个对象进行操作 
         */  
        Thread ta = new Thread(t, "Thread-A");  
        Thread tb = new Thread(t, "Thread-B");  
        ta.start();  
        tb.start();  
    }  
}  
  
class MyThread implements Runnable {  
    // 变量 a 被两个线程共同操作,可能会造成线程竞争  
    int a = 10;  
    @Override  
    public void run() {  
        for (int i = 0; i < 5; i++) {  
            a -= 1;  
            try {  
                Thread.sleep(1);  
            } catch (InterruptedException e) {}  
            System.out.println(Thread.currentThread().getName() + " → a = " + a);  
        }  
    }  
}



结果显示:



Thread-A → a = 8
Thread-B → a = 8
Thread-A → a = 6
Thread-B → a = 6
Thread-B → a = 4
Thread-A → a = 4
Thread-B → a = 2
Thread-A → a = 2
Thread-A → a = 0
Thread-B → a = 0



从上面的结果中我们可以看到,在线程A对数据进行了操作之后,他还没有来得及数据进行下一次的操作,此时线程B也对数据进行了操作,导致数据a一次性被减了两次,以至于a为9的时候的值根本没有打印出来,a为0的时候却被打印了两次。

那么,我们要如何才能避免结果这种情况的出现呢?

2.线程锁

如果在一个线程对数据进行操作的时候,禁止另外一个线程操作此数据,那么,就能很好的解决以上的问题了。这种操作叫做给线程加锁。



package com.huojg.test;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 在Java多线程中,当两个或以上的线程对同一个数据进行操作的时候,可能会产生“竞争条件”的现象。这种现象产生的根本原因是因为多个线程在对同一个数据进行操作,此时对该数据的操作是非“原子化”的,
 * 可能前一个线程对数据的操作还没有结束,后一个线程又开始对同样的数据开始进行操作,这就可能会造成数据结果的变化未知。
 * 
 * 
 * 2.线程锁

如果在一个线程对数据进行操作的时候,禁止另外一个线程操作此数据,那么,就能很好的解决以上的问题了。这种操作叫做给线程加锁。
 * */
public class TestThread {  
      
    public static void main(String[] args) {  
        // new 出一个新的对象 t  
        MyThread t = new MyThread();  
        /** 
         * 两个线程是在对同一个对象进行操作 
         */  
        Thread ta = new Thread(t, "Thread-A");  
        Thread tb = new Thread(t, "Thread-B");  
        ta.start();  
        tb.start();  
    }  
}  
  
class MyThread implements Runnable {  
    // 声明锁  
    private Lock lock = new ReentrantLock();  
    // 变量 a 被两个线程共同操作,可能会造成线程竞争  
    int a = 10;  
    @Override  
    public void run() {  
          // 加锁  
        lock.lock();  
        for (int i = 0; i < 5; i++) {  
            a -= 1;  
            try {  
                Thread.sleep(1);  
            } catch (InterruptedException e) {}  
            System.out.println(Thread.currentThread().getName() + " → a = " + a);  
        }  
        lock.unlock();
    }  
}



 

运行结果:



Thread-A → a = 9
Thread-A → a = 8
Thread-A → a = 7
Thread-A → a = 6
Thread-A → a = 5
Thread-B → a = 4
Thread-B → a = 3
Thread-B → a = 2
Thread-B → a = 1
Thread-B → a = 0



上面的代码给出了给线程枷锁的方式,可以看到,在线程对数据进行操作之前先给此操作加一把锁,那么在此线程对数据进行操作的时候,其他的线程无法对此数据进行操作,只能“阻塞”在一边等待当前线程对数据操作结束后再对数据进行下一次的操作,当前线程在数据的操作完成之后会解开当前的锁以便下一个线程操作此数据

用synchronized关键字加锁来对方法进行加锁:结果一样;

 

总结:

Java中的多线程,当多个线程对一个数据进行操作时,可能会产生“竞争条件”的现象,这时候需要对线程的操作进行加锁,来解决多线程操作一个数据时可能产生问题。加锁方式有两种,一个是申明Lock对象来对语句快进行加锁,另一种是通过synchronized 关键字来对方法进行加锁。以上两种方法都可以有效解决Java多线程中存在的竞争条件的问题。