首页 > 代码库 > 多线程的交互

多线程的交互

当多个线程同时共享访问同一数据时,每个线程都尝试操作该数据,从而导致改数据被破坏,这种现象称为争用条件。

同步的实现:wait(),notify(),notifyAll()   

当一个线程要访问共享资源,首先要拿到锁后进入临界区,如果发现某些条件不符合,调用wait方法释放锁资源,线程进入锁对象上的Wait Set,

拿到锁的当前运行进程执行完时调用notify()会唤醒锁资源所持有的等待区域中的一条线程(随机),使该线程有机会竞争CPU资源;

                                   调用notifyAll()会唤醒锁资源所持有的等待区域中的所有线程,使这些线程有机会竞争CPU资源;

EnergySystem:

/**
 * 宇宙的能量系统
 * 遵循能量守恒定律:
 * 能量不会凭空创生或消失,只会从一处转移到另一处
 */
public class EnergySystem {
    
    //能量盒子,能量存贮的地方
     private final double[] energyBoxes;
     private final Object lockObj = new Object();
     
     /**
      * 
      * @param n    能量盒子的数量
      * @param initialEnergy 每个能量盒子初始含有的能量值
      */
     public EnergySystem(int n, double initialEnergy){
         energyBoxes = new double[n];
         for (int i = 0; i < energyBoxes.length; i++)
             energyBoxes[i] = initialEnergy;
     }
     
     /**
      * 能量的转移,从一个盒子到另一个盒子
      * @param from 能量源
      * @param to     能量终点 
      * @param amount 能量值
      */
     public void transfer(int from, int to, double amount){
         synchronized(lockObj){
//             if (energyBoxes[from] < amount)
//                 return;
            //while循环,保证条件不满足时任务都会被条件阻挡
             //而不是继续竞争CPU资源
             while (energyBoxes[from] < amount){
                 try {
                    //条件不满足, 将当前线程放入Wait Set
                    lockObj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
             }
             System.out.println(Thread.currentThread().getName());
             energyBoxes[from] -= amount;
             //System.out.printf("从%d转移%10.2f单位能量到%d", from, amount, to);
             energyBoxes[to] += amount;
            // System.out.printf("      能量总和:%10.2f%n", getTotalEnergies());
            //唤醒所有在lockObj对象上等待的线程
             lockObj.notifyAll();
         }
     }
      // 获取能量世界的能量总和
     public double getTotalEnergies(){
         double sum = 0;
         for (double amount : energyBoxes)
             sum += amount;
         return sum;
     }
      // 返回能量盒子的长度
     public  int getBoxAmount(){
         return energyBoxes.length;
     }
}

EnergyTransferTask:

public class EnergyTransferTask implements Runnable{

    //共享的能量世界
    private EnergySystem energySystem;
    //能量转移的源能量盒子下标
    private int fromBox;
    //单次能量转移最大单元
    private double maxAmount;
    //最大休眠时间(毫秒)
    private int DELAY = 10;
    
    public EnergyTransferTask(EnergySystem energySystem, int from, double max){
        this.energySystem = energySystem;
        this.fromBox = from;
        this.maxAmount = max;
    }
    
    public void run() {
        try{
            while (true){
                int toBox = (int) (energySystem.getBoxAmount()* Math.random());
                double amount = maxAmount * Math.random();
                energySystem.transfer(fromBox, toBox, amount);
                Thread.sleep((int) (DELAY * Math.random()));
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
技术分享
public class EnergySystemTest {

    //将要构建的能量世界中能量盒子数量
    public static final int BOX_AMOUNT = 100;
    //每个盒子初始能量
    public static final double INITIAL_ENERGY = 1000;

    public static void main(String[] args){
        EnergySystem eng = new EnergySystem(BOX_AMOUNT, INITIAL_ENERGY);
        for (int i = 0; i < BOX_AMOUNT; i++){
            EnergyTransferTask task = new EnergyTransferTask(eng, i, INITIAL_ENERGY);
            Thread t = new Thread(task,"TransferThread_"+i);
            t.start();    
            System.out.println(t.activeCount());
        }
    }

}
EnergySystemTest

输出结果:

技术分享

在for循环中from是递加的,但结果并不是从0,1,2.......按顺序转移?

虽然进程按顺序创造task但start方法不会等到run方法执行完就会继续执行下面的代码,所以导致创建了很多线程但他们随机执行run方法。

 

多线程的交互