• 多线程06-多线程共享数据的方式(经验小结)


         1.案例分析-01

          通过代码实现火车票出售的例子

          在实现代码之前先对问题进行分析:火车票出售应该是在多个窗口进行的(即多个线程),以一个车的班次来说,该班次的火车票张数即为多个窗口共享的数据

    即这份共享数据为出售特定班次的火车票,这个动作在多个窗口都是不变的,变更的只有火车票的剩余张数.代码实现如下:

    package org.lkl.thead;
    
    /**
     * 
     * Function : 多线程共享数据
     * 
     * @author : Liaokailin CreateDate : 2014-6-13 version : 1.0
     */
    public class MultiThreadShareData {
        public static void main(String[] args) {
            SellTicket s = new SellTicket(); // 共享对象
            new Thread(s, "1").start();
            new Thread(s, "2").start();
            new Thread(s, "3").start();
            new Thread(s, "4").start();
            new Thread(s, "5").start();
        }
    
    }
    
    /**
     * 出售火车票  
     */
    class SellTicket implements Runnable {
        private int ticket = 100;
    
        @Override
        public void run() {
    
            while (true) {
                synchronized (SellTicket.class) {
                if (ticket > 0) {
                
                        try {
                            Thread.sleep(30);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                        ticket-- ;
                        System.out.println("窗口" + Thread.currentThread().getName()
                                + "出售合肥到北京火车票剩余车票" + ticket);
                    }else{
                        break ;
                    }
                }
            }
    
        }
    }

       通过上面的代码可以发现,多线程共享的数据是卖火车票,而这个动作在各个线程中都是不变的,因此可以做如下的小结:

       多线程中各个线程执行的代码相同,那么可以使用同一个Runnable对象来实现共享数据。

          2. 案例分析-02

             和上面问题乡对应的是 如果多个线程中执行的代码不同呢? 该如何去处理数据之间的共享?

             例如: 

                   设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1 。

              通过问题可以发现,线程中执行的代码显示是不同的,一个是j++ 一个是j--,在前面我们说过 要同步互斥的代码或者算法应该分别放在独立的方法中,并且这些方法都放在同一个类中,这样子

    比较容易实现他们之间的同步互斥和通信

              那么可以设计如下的业务类: 

    class Business{
        private int j = 0;
        
        public synchronized void increment(){
            j++ ;
            System.out.println(Thread.currentThread().getName()+",j="+j);
        }
        public synchronized void decrement(){
            j-- ;
            System.out.println(Thread.currentThread().getName()+",j="+j);
        }
    }

         要实现多个线程的不同操作,可以将不同的操作防止在不同的Runnable中 然后将不同的Runnable对象传递给不同的线程

            

    package org.lkl.thead;
    
    
    /**
     *   设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1 
     */
    public class ShareDataFoo {
        public static void main(String[] args) {
            Business b = new Business() ;
            for(int i= 0 ;i<2;i++){
                new Thread(new OperatorFoo01(b)).start();
                new Thread(new OperatorFoo02(b)).start();
                
            }
            
        }
    
        
    }
    class OperatorFoo01 implements Runnable{
        private Business business;
        public OperatorFoo01(Business business){
            this.business = business ;
        }
        @Override
        public void run() {
            business.increment() ;
        }
    }
    
    class OperatorFoo02 implements Runnable{
        private Business business;
        public OperatorFoo02(Business business){
            this.business = business ;
        }
        @Override
        public void run() {
            business.decrement() ;
        }
    }
    
    class Business{
        private int j = 0;
        
        public synchronized void increment(){
            j++ ;
            System.out.println(Thread.currentThread().getName()+",j="+j);
        }
        public synchronized void decrement(){
            j-- ;
            System.out.println(Thread.currentThread().getName()+",j="+j);
        }
    }

           运行代码得到的结果: 

    Thread-0,j=1
    Thread-1,j=0
    Thread-2,j=1
    Thread-3,j=0

    我们可以做个小结:

       如果每个线程执行的代码不同,这时候需要不同的Runnable对象,有如下两种方式来实现这些Runnable对象之间的数据共享

          1.将共享数据封在在一个对象中,然后将这个对象逐一传递给各个不同Runnable对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥

    和通行 (上面我们的例子就是采用这样的方式)

          2.将这些Runnable对象作为某个类的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作也分配给外部类,以便对共享数据进行的各个操作的互斥和通信,作为内部类的各个

    Runnable对象调用外部类的这些方法

          对于第二点同样以上面的例子得到如下代码:

    package org.lkl.thead;
    
    
    /**
     *   设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1 
     */
    public class ShareDataFoo2 {
        public static void main(String[] args) {
            ShareDataFoo2 foo = new ShareDataFoo2() ;
            
            for(int i= 0 ;i<2;i++){
                new Thread(foo.new OperAdd()).start();
                new Thread(foo.new OperSub()).start();
                
            }
            
        }
        
        private int j = 0 ;
        
        public synchronized  void increment(){
            j++ ;
            System.out.println(Thread.currentThread().getName()+",j="+j);
        }
        
        public synchronized void decrement(){
            j-- ;
            System.out.println(Thread.currentThread().getName()+",j="+j);
        }
    
         class OperAdd implements Runnable{
            @Override
            public void run() {
                increment() ;
            }
        }
        
         class OperSub implements Runnable{
            @Override
            public void run() {
                decrement() ;
            }
        }
        
        
    }

          也可以对上面的两种方法进行组合:

         将共享数据封装到另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象本身上来完成,该对象作为这个外部类中的成员变量或者方法中的局部变量,每个线程的Runnable对象

    作为外部类中的成员内部类或局部内部类,如下面代码:

      

    package org.lkl.thead;
    
    
    /**
     *   设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1 
     */
    public class ShareDataFoo3 {
        public static void main(String[] args) {
            ShareDataFoo3 foo = new ShareDataFoo3() ;
            for(int i= 0 ;i<2;i++){
                new Thread(foo.new OperatorFoo01()).start();
                new Thread(foo.new OperatorFoo02()).start();
                
            }
            
        }
    
        private Business2 business = new Business2() ;
        class OperatorFoo01 implements Runnable{
         
            @Override
            public void run() {
                business.increment() ;
            }
        }
    
        class OperatorFoo02 implements Runnable{
            @Override
            public void run() {
                business.decrement() ;
            }
        }
        
        
    }
    
    
    class Business2{
        private int j = 0;
        
        public synchronized void increment(){
            j++ ;
            System.out.println(Thread.currentThread().getName()+",j="+j);
        }
        public synchronized void decrement(){
            j-- ;
            System.out.println(Thread.currentThread().getName()+",j="+j);
        }
    }

     

  • 相关阅读:
    Hyper-V中的VM如何使用Pass-through Disk
    LDF文件丢失, 如何仅用MDF文件恢复数据库呢?
    PowerShell中的一个switch的例子
    NetBiosDomainNamesEnabled与SharePoint User Profile Service Application
    在Windows Server 2008 R2上安装Exchange 2013过程中遇到的一些问题
    C语言位域精解(转)
    uniq命令 (转)
    sort命令
    curl命令(测试连接命令)
    C10K——千万级并发实现的秘密:内核不是解决方案,而是问题所在!(转)
  • 原文地址:https://www.cnblogs.com/liaokailin/p/3786320.html
Copyright © 2020-2023  润新知