Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 250|回复: 0

[Java线程学习]java线程同步问题入门

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-10-30 23:58:27 | 显示全部楼层 |阅读模式
    本文适合读者群为对java多线程有一定了解的人(知道Thread和Runnable)。线程同步是一个经常被人谈及的话题。这篇博客讲的就是java的线程同步问题。 首先,我们来看下面的一个类。
    1. public class Plus implements Runnable{   
    2.       
    3.     private long num = 0;      
    4.     public void run(){   //线程的任务
    5.         a();   
    6.     }   
    7.     private void a() {   
    8.         for(int i=0;i< 100;i++){//将num每次加1,加100次
    9.             num++;   
    10.                
    11.             if(num%1000==0)   
    12.                 System.out.println("num:"+num);   
    13.         }   
    14.     }      
    15.     public static void main(String args[]){
    16.          Plus p=new Plus();
    17.          Thread t=new Thread(p);
    18.          t.start();
    19.    }
    20. }  
    复制代码

      
       
       

         
       

         
       
      
        这是一个很简单的实现Runnable接口的类,它单线程运行的结果就是将其属性num从0加到一百。if语句的意思是,如果num成为1000的整数倍,则输出num的值。
    1. public class Test {   
    2.   
    3.     /**  
    4.      * @param args  
    5.      */  
    6.     public static void main(String[] args) {   
    7.         Test test = new Test();   
    8.            
    9.         // 根据类名得到实现Runnable接口的对象            
    10.         Runnable rb = test.getRunnable("Plus4");   
    11.            
    12.         //并发执行   
    13.         test.runInGroup(rb,4000);   //启动4000个线程执行同一任务
    14.                   
    15.     }   
    16.       
    17.     /**  
    18.      * 将同一个Runnable对象产生多个线程,并发执行  
    19.      * @param rb Runnable对象  
    20.      * @param num 线程的数量  
    21.      */  
    22.     public void runInGroup(Runnable rb,int num){   
    23.            
    24.         ThreadGroup thg = new ThreadGroup("TestGroup");   
    25.         for(int i=0;i< num;i++){   
    26.             Thread th = new Thread(thg,rb);   
    27.             th.start();
    28.            
    29.         }   
    30.            
    31.       
    32.     }   
    33.       
    34.     /**  
    35.      * 根据类的名字获得线程实例  
    36.      * @param className  
    37.      * @return 线程对象,如发生异常,则返回null  
    38.      */  
    39.     public Runnable getRunnable(String className){   
    40.            
    41.       //  className = "com.cici.threads."+className;   
    42.            
    43.         try {   
    44.                
    45.             Runnable rb = (Runnable)Class.forName(className).newInstance();   
    46.             return rb;   
    47.                
    48.         } catch (InstantiationException e) {   
    49.             // TODO Auto-generated catch block   
    50.             e.printStackTrace();   
    51.         } catch (IllegalAccessException e) {   
    52.             // TODO Auto-generated catch block   
    53.             e.printStackTrace();   
    54.         } catch (ClassNotFoundException e) {   
    55.             // TODO Auto-generated catch block   
    56.             e.printStackTrace();   
    57.         }   
    58.            
    59.         return null;   
    60.     }   
    61.   
    62. }  
    复制代码
    运行:
    C:ex>java Test
    num:1000
    num:2000
    num:3000
    num:4000
    num:5000
    num:6000
    num:7000
    。。。。。。
    num:401000
    num:402000
    num:403000
    num:404000

        这一段代码的意思为实例化一个Plus对象,并将其在4000个线程中运行。按照我们惯性的思维,num的值应该变成400000,所以控制台最后输出的值应该是。。。399000,400000。但是实践才能出真知,有心的读者可以试一试最后的结果是什么。我机器上的结果是num的值最后是404 000,至于你信不信,反正我是信了。这种问题在java里面可以归结为线程同步问题。在我的电脑上,本实验中在10~100这个数量级上还没有发生过这种问题,当线程数到1000的时候,这个问题就比较明显了。 查阅资料,我们知道,当多个线程对互斥资源同时进行操作时,就有可能发生同步问题。在java语言中,我们可以有许多办法解决这个问题,以下列举几个。 1.synchronized方法
    1. public class Plus1 implements Runnable{   
    2.       
    3.     private long num = 0;   
    4.       
    5.     public void run(){   
    6.         a();   
    7.     }   
    8.   
    9.     private synchronized void a() {   
    10.         for(int i=0;i< 100;i++){   
    11.             num++;   
    12.             if(num%1000==0)   
    13.                 System.out.println("num:"+num);   
    14.         }   
    15.     }   
    16.       
    17. }  
    复制代码
    这个类与本文中第一个类的区别很小,就是在a方法的定义中加了synchronized关键字,意思是这个方法同时只能被一个线程调用,这样,我们就解决了上面的问题。 2.synchronized代码块
    1. public class Plus2 implements Runnable{   
    2.       
    3.     private int num = 0;   
    4.     private byte[] lock = new byte[0];   
    5.       
    6.     public void run(){   
    7.         a();   
    8.     }   
    9.   
    10.     private void a() {   
    11.   
    12.   
    13.         for(int i=0;i< 100;i++){            
    14.             synchronized(lock){   
    15.                   
    16.                 num++;                 
    17.                 if(num%1000==0)   
    18.                     System.out.println("num:"+num);   
    19.             }   
    20.         }   
    21.     }   
    22.       
    23. }  
    复制代码
       这个类与上一个类又非常相似,只不过是synchronized关键字换了一个位置,用它将一个代码块包了起来,并且传入一个lock对象,从对象的命名来看,它就相当于一把锁,我们可以这么理解,synchronized代码块就是一栋房子,而lock就是他房门的锁(我们认为这个房子只有一扇门),每个人进来的时候就把门锁上,出去的时候就把门打开,如果里面有人,外面的人就会等待里面的人出来把门打开。一个房间里只有一个人,也就是这段代码只能同时被一个线程执行。 3.java.util.concurrent.lock包中的类
    1. import java.util.concurrent.locks.Lock;   
    2. import java.util.concurrent.locks.ReentrantLock;   
    3.   
    4. public class Plus3 implements Runnable{   
    5.       
    6.     private long num = 0;      
    7.     private Lock lock = new ReentrantLock();   
    8.       
    9.     public void run(){   
    10.         a();   
    11.     }   
    12.   
    13.     private void a() {   
    14.         for(int i=0;i< 100;i++){   
    15.   
    16.             lock.lock();   
    17.                
    18.             num++;   
    19.             if(num%1000==0)   
    20.                 System.out.println("num:"+num);   
    21.                
    22.             lock.unlock();   
    23.         }   
    24.     }   
    25.       
    26. }  
    复制代码
    如果你理解了synchronized代码块,那么恭喜,上面一段的代码你也懂了。还是加锁的意思,只不过使用了专门的类。

    4.java.util.concurrent.atomic包中的类
    1. import java.util.concurrent.atomic.AtomicLong;   
    2.   
    3. public class Plus4 implements Runnable{   
    4.       
    5.     private AtomicLong num = new AtomicLong(0);   
    6.       
    7.     public void run(){   
    8.         a();   
    9.     }   
    10.   
    11.     private void a() {   
    12.         for(int i=0;i< 100;i++){   
    13.                
    14.             num.addAndGet(1);              
    15.             if((num.get())%1000==0)   
    16.                 System.out.println("num:"+num);   
    17.         }   
    18.     }   
    19.       
    20. }  
    复制代码
       atomic的意思是原子,意为不可分割的。对一个atomicLong类型的对象调用addAndGet方法逻辑上和对一个long类型的值进行++操作是完全一样的。原子操作可以说是从根源上解决了同步问题。如果每一台计算机上的操作都是原子操作,那么这个计算机就是实时系统,实时系统是没有同步问题的。  


      
      
       
       

         
       

         
       
      
    复制代码

    源码下载:http://file.javaxxz.com/2014/10/30/235826968.zip
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-5 22:52 , Processed in 0.415263 second(s), 46 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表