• 多线程的交互


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

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

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

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

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

    public class TestSync implements Runnable {
      Timer timer = new Timer();
      public static void main(String[] args) {
        TestSync test = new TestSync();
        Thread t1 = new Thread(test);
        Thread t2 = new Thread(test);
        t1.setName("t1"); 
        t2.setName("t2");
        t1.start(); 
        t2.start();
      }
      public void run(){
        timer.add(Thread.currentThread().getName());
      }
    }
    
    class Timer{
      private static int num = 0;
      public synchronized void add(String name){ 
          //synchronized (this) {  //锁定当前对象
            num ++;
            try {Thread.sleep(1);} 
            catch (InterruptedException e) {}
            System.out.println(name+", 你是第"+num+"个使用timer的线程");
          //}
      }
    }

    死锁问题:

    public class TestDeadLock implements Runnable {
        public int flag = 1;
        static Object o1 = new Object(), o2 = new Object();
        public void run() {
    System.out.println("flag=" + flag);
            if(flag == 1) {
                synchronized(o1) {
                    try {
                        Thread.sleep(500);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    synchronized(o2) {
                        System.out.println("1");    
                    }
                }
            }
            if(flag == 0) {
                synchronized(o2) {
                    try {
                        Thread.sleep(500);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    synchronized(o1) {
                        System.out.println("0");
                    }
                }
            }
        }    
        
        public static void main(String[] args) {
            TestDeadLock td1 = new TestDeadLock();
            TestDeadLock td2 = new TestDeadLock();
            td1.flag = 1;
            td2.flag = 0;
            Thread t1 = new Thread(td1);
            Thread t2 = new Thread(td2);
            t1.start();
            t2.start();
            
        }
    }

    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方法。

    关于锁:synchronized与volatile

    恐怕比较一下volatile和synchronized的不同是最容易解释清楚的。volatile是变量修饰符,而synchronized则作用于一段代码或方法;看如下三句get代码:

    int i1; int geti1() {return i1;}
    volatile int i2; int geti2() {return i2;}
    int i3; synchronized int geti3() {return i3;}
      geti1()得到存储在当前线程中i1的数值。多个线程有多个i1变量拷贝,而且这些i1之间可以互不相同。换句话说,另一个线程可能已经改 变了它线程内的i1值,而这个值可以和当前线程中的i1值不相同。事实上,Java有个思想叫“主”内存区域,这里存放了变量目前的“准确值”。每个线程 可以有它自己的变量拷贝,而这个变量拷贝值可以和“主”内存区域里存放的不同。因此实际上存在一种可能:“主”内存区域里的i1值是1,线程1里的i1值 是2,线程2里的i1值是3——这在线程1和线程2都改变了它们各自的i1值,而且这个改变还没来得及传递给“主”内存区域或其他线程时就会发生。
      而geti2()得到的是“主”内存区域的i2数值。用volatile修饰后的变量不允许有不同于“主”内存区域的变量拷贝。换句话说,一个变量经 volatile修饰后在所有线程中必须是同步的;任何线程中改变了它的值,所有其他线程立即获取到了相同的值。理所当然的,volatile修饰的变量 存取时比一般变量消耗的资源要多一点,因为线程有它自己的变量拷贝更为高效。
      既然volatile关键字已经实现了线程间数据同步,又要synchronized干什么呢?呵呵,它们之间有两点不同。首 先,synchronized获得并释放监视器——如果两个线程使用了同一个对象锁,监视器能强制保证代码块同时只被一个线程所执行——这是众所周知的事 实。但是,synchronized也同步内存:事实上,synchronized在“主”内存区域同步整个线程的内存。因此,执行geti3()方法做 了如下几步:
    1. 线程请求获得监视this对象的对象锁(假设未被锁,否则线程等待直到锁释放)
    2. 线程内存的数据被消除,从“主”内存区域中读入(Java虚拟机能优化此步。。。[后面的不知道怎么表达,汗])
    3. 代码块被执行
    4. 对于变量的任何改变现在可以安全地写到“主”内存区域中(不过geti3()方法不会改变变量值)
    5. 线程释放监视this对象的对象锁
      因此volatile只是在线程内存和“主”内存间同步某个变量的值,而synchronized通过锁定和解锁某个监视器同步所有变量的值。显然 synchronized要比volatile消耗更多资源。

  • 相关阅读:
    增加/删除新用户并添加root权限
    LINUX学习之路-1
    了解参考基因组及注释信息
    RNA-seq操作实战
    如何利用数据库下载参考基因组
    RNA-Seq比对软件HISAT2的用法
    小白-批量下载SRR数据
    sratoolkit 的使用
    批量查看QC结果的工具----multiqc
    fastQC 质控结果解读
  • 原文地址:https://www.cnblogs.com/Lune-Qiu/p/7269708.html
Copyright © 2020-2023  润新知