zoukankan      html  css  js  c++  java
  • JAVA 基础之 多线程

    关键:多线程同步,有两种角度。

    1.从JVM的角度来看,instance  = new instance () 不是一个原子操作,在jvm被分成三步,分配内存,设置intance引用指向实例,初始化,JVM乱序执行。需要考虑线陈并发处理问题。可以通过volatile控制语句的原子性。

    2.从多条语句之间的角度来看: if(instance =null) {instance  = new instance ()} 这样两句话,如何保证同步。使用sychornized 关键字。通过锁,保证同步代码块 。

    1. 主线程

    Static Thread currentThread();  //currentThread() 是Thread类的共有静态方法

    Thread t = Thread.currentThread();  //获得主线程

    Static void Sleep();  //Thread 类的 静态方法,是当前线程睡眠 , 单位毫秒,可修改单位

    2. 线程的实现 :

      1) Runnable 接口

      2)扩展Thread类

    class MyThread extends Thread{
      public int x = 0;
    
      public void run(){
        System.out.println(++x);
      }
    }
    
    class R implements Runnable{
      private int x = 0;
      public void run(){
        System.out.println(++x);
      }
    }
    
    public class Test {
      public static void main(String[] args) throws Exception{
        
        for(int i=0;i<10;i++){
          Thread t = new MyThread();
          t.start();
        }
        Thread.sleep(10000);//让上面的线程运行完成
        R r = new R();
        for(int i=0;i<10;i++){
          Thread t = new Thread(r);
          t.start();
        }
      }
    }

    总结:Thread和Runnable是实现java多线程的2种方式,runable是接口,thread是类,建议使用runable实现java多线程,不管如何,最终都需要通过thread.start()来使线程处于可运行状态。

    认识Thread的start和run

    1) start:

    用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的 start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到spu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。

    2) run:

    run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。

    sleep & yield 
      sleep()方法让线程睡眠一段时间,时间过后,线程才会去争取cpu时间片。 
      Java1.5提供了一个新的TimeUnit枚举,让sleep的可读性更好。 
      yield()方法让线程主动让出cpu,让cpu重新调度以决定哪个线程可以使用cpu。

    3. 线程同步: 

    http://www.cnblogs.com/yyyyy5101/archive/2011/07/20/2112157.html

      1) 同步方法 : synchronize关键字修饰 需要保证同步的方法(临界区)

      2) 同步语句 :  synchronized块

      在synchronized关键字后面,要传一个对象参数,任何线程要进入临界区时必须先要获得该对象的锁,退出临界区时要释放该对象的锁,这样别的线程才有机会进入临界区。 
      临界区和synchronized方法,其原理都是一样的,都是通过在对象上加锁来实现的,只不过临界区来得更加灵活,因为它不光可以对this对象加锁,也可以对任何别的对象加锁。

      在使用Java多线程中会需要使用Synchronized块同步方法。我们不仅可以通过synchronized块来同步一个对象变量。也可以使用synchronized块来同步类中的静态方法和非静态方法。

    AD: 2013大数据全球技术峰会低价抢票中

    synchronized关键字有两种用法。第一种就是在《使用Synchronized关键字同步类方法》一文中所介绍的直接用在方法的定义中。另外一种就是synchronized块。我们不仅可以通过synchronized块来同步一个对象变量。也可以使用synchronized块来同步类中的静态方法和非静态方法。

    synchronized块的语法如下:

    1. public void method()  
    2. {  
    3.     … …  
    4.     synchronized(表达式)  
    5.     {  
    6.         … …  
    7.     }  

    一、非静态类方法的同步   

    从《使用Synchronized关键字同步类方法》一文中我们知道使用synchronized关键字来定义方法就会锁定类中所有使用synchronzied关键字定义的静态方法或非静态方法,但这并不好理解。而如果使用synchronized块来达到同样的效果,就不难理解为什么会产生这种效果了。如果想使用synchronized块来锁定类中所有的同步非静态方法,需要使用this做为synchronized块的参数传入synchronized块国,代码如下:

    通过synchronized块同步非静态方法

    1. public class SyncBlock  
    2.  {  
    3.       public void method1()  
    4.       {  
    5.           synchronized(this)  // 相当于对method1方法使用synchronized关键字  
    6.           {  
    7.               … …  
    8.           }  
    9.       }  
    10.       public void method2()  
    11.       {  
    12.           synchronized(this)  // 相当于对method2方法使用synchronized关键字  
    13.           {  
    14.               … …  
    15.           }  
    16.       }  
    17.       public synchronized void method3()    
    18.       {  
    19.           … …  
    20.       }  
    21.   } 

    在上面的代码中的method1和method2方法中使用了synchronized块。而第017行的method3方法仍然使用synchronized关键字来定义方法。在使用同一个SyncBlock类实例时,这三个方法只要有一个正在执行,其他两个方法就会因未获得同步锁而被阻塞。在使用synchronized块时要想达到和synchronized关键字同样的效果,必须将所有的代码都写在synchronized块中,否则,将无法使当前方法中的所有代码和其他的方法同步。

    除了使用this做为synchronized块的参数外,还可以使用SyncBlock.this作为synchronized块的参数来达到同样的效果。

    在内类(InnerClass)的方法中使用synchronized块来时,this只表示内类,和外类(OuterClass)没有关系。但内类的非静态方法可以和外类的非静态方法同步。如在内类InnerClass中加一个method4方法,并使method4方法和SyncBlock的三个方法同步,代码如下:

    使内类的非静态方法和外类的非静态方法同步

    1. public class SyncBlock  
    2. {  
    3.     … …  
    4.     class InnerClass  
    5.     {  
    6.         public void method4()  
    7.         {  
    8.             synchronized(SyncBlock.this)  
    9.             {  
    10.                 … …   
    11.             }  
    12.         }  
    13.     }  
    14.     … …  

    在上面SyncBlock类的新版本中,InnerClass类的method4方法和SyncBlock类的其他三个方法同步,因此,method1、method2、method3和method4四个方法在同一时间只能有一个方法执行。

    Synchronized块不管是正常执行完,还是因为程序出错而异常退出synchronized块,当前的synchronized块所持有的同步锁都会自动释放。因此,在使用synchronized块时不必担心同步锁的释放问题。

    二、静态类方法的同步

    由于在调用静态方法时,对象实例不一定被创建。因此,就不能使用this来同步静态方法,而必须使用Class对象来同步静态方法。代码如下:

    通过synchronized块同步静态方法

    1. public class StaticSyncBlock  
    2.   {  
    3.       public static void method1()  
    4.       {  
    5.           synchronized(StaticSyncBlock.class)    
    6.           {  
    7.               … …  
    8.           }  
    9.       }  
    10.       public static synchronized void method2()    
    11.       {  
    12.           … …  
    13.       }  
    14.   } 

    在同步静态方法时可以使用类的静态字段class来得到Class对象。在上例中method1和method2方法同时只能有一个方法执行。除了使用class字段得到Class对象外,还可以使用实例的getClass方法来得到Class对象。上例中的代码可以修改如下:

    使用getClass方法得到Class对象(每一个加载过的类在JVM中都有Class对象,包括基本数据类型和void关键字)

    1. public class StaticSyncBlock  
    2. {  
    3.     public static StaticSyncBlock instance;   
    4.     public StaticSyncBlock()  
    5.     {  
    6.         instance = this;  
    7.     }  
    8.     public static void method1()  
    9.     {  
    10.        synchronized(instance.getClass())  
    11.        {  
    12.               
    13.        }  
    14.     }  
    15.        
    16. }  

    在上面代码中通过一个public的静态instance得到一个StaticSyncBlock类的实例,并通过这个实例的getClass方法得到了Class对象(一个类的所有实例通过getClass方法得到的都是同一个Class对象,因此,调用任何一个实例的getClass方法都可以)。我们还可以通过Class对象使不同类的静态方法同步,如Test类的静态方法method和StaticSyncBlock类的两个静态方法同步,代码如下:

    Test类的method方法和StaticSyncBlock类的method1、method2方法同步

    1. public class Test  
    2. {  
    3.     public static void method()  
    4.     {  
    5.         synchronized(StaticSyncBlock.class)  
    6.         {  
    7.                
    8.         }  
    9.     }  

    注意:在使用synchronized块同步类方法时,非静态方法可以使用this来同步,而静态方法必须使用Class对象来同步。它们互不影响。当然,也可以在非静态方法中使用Class对象来同步静态方法。但在静态方法中不能使用this来同步非静态方法。这一点在使用synchronized块同步类方法时应注意。


    对象锁

    当使用同步块时,如果方法下的同步块都同步到一个对象上的锁,则所有的任务(线程)只能互斥的进入这些同步块。
    Resource1.java演示了三个线程(包括main线程)试图进入某个类的三个不同的方法的同步块中,虽然这些同步块处在不同的方法中,但由于是同步到同一个对象(当前对象 synchronized (this)),所以对它们的方法依然是互斥的。
    package com.zj.lock;
    import java.util.concurrent.TimeUnit;
     
    public class Resource1 {
        public void f() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in f()");
           synchronized (this) {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in f()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           }
        }
     
        public void g() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in g()");
           synchronized (this) {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in g()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           }
        }
     
        public void h() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in h()");
           synchronized (this) {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in h()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           }
        }
     
        public static void main(String[] args) {
           final Resource1 rs = new Resource1();
     
           new Thread() {
               public void run() {
                  rs.f();
               }
           }.start();
     
           new Thread() {
               public void run() {
                  rs.g();
               }
           }.start();
     
           rs.h();
        }
    }//匿名内部类

     匿名类是不能有名称的类,所以没办法引用它们。必须在创建时,作为new语句的一部分来声明它们。这就要采用另一种形式的new语句,如下所示: new <类或接口> <类的主体> 这种形式的new语句声明一个新的匿名类,它对一个给定的类进行扩展,或者实现一个给定的接口。它还创建那个类的一个新实例,并把它作为语句的结果而返回。要扩展的类和要实现的接口是new语句的操作数,后跟匿名类的主体。如果匿名类对另一个类进行扩展,它的主体可以访问类的成员、覆盖它的方法等等,这和其他任何标准的类都是一样的。如果匿名类实现了一个接口,它的主体必须实现接口的方法。

    附带问题:为什么局部内部类(即:定义在方法中的内部,包括匿名内部类),使用的参数必须为final?

    答 :局部变量的生命周期与局部内部类的对象的生命周期的不一致性!

    假设:
    1. 我们在方法内定义了一个 匿名的 Thread 子类,他使用了方法的局部参数,然后我让这个线程运行去,因为是不同的线程,那么当我这个方法的启动线程 的语句执行过了,而且我修改了这个参数或局部变量,那么那个线程启动执行的时候是不是会出现莫名其妙的问题:运行时刻能访问到的变量太难以捉摸了,我是该 复制一份过去给新线程运行时使用还是到时候再来取呢(再来取时已经物是人非了)?

    2. 设方法f被调用,从而在它的调用栈中生成了变量i,此时产生了一个局部内部类对象inner_object,它访问了该局部变量i .当方法f()运行结束后,局部变量i就已死亡了,不存在了.但:局部内部类对象inner_object还可能   一直存在(只能没有人再引用该对象时,它才会死亡),它不会随着方法f()运行结束死亡.这时:出现了一个"荒唐"结果:局部内部类对象 inner_object要访问一个已不存在的局部变量i!
    Java 为了消除这个编程中可能出现的歧义,使用方法内的内部类时如果访问了方法的参数或局部变量,那么它应该是 final 的。 

    final 原理,当变量是final时,通过将final局部变量"复制"一份

    另详解: http://feiyeguohai.iteye.com/blog/1500108

    如果满足下面的一些条件,使用匿名内部类是比较合适的:

    ?只用到类的一个实例。 ?类在定义后马上用到。

    ?类非常小(SUN推荐是在4行代码以下)

    ?给类命名并不会导致你的代码更容易被理解。

    在使用匿名内部类时,要记住以下几个原则:

    ?匿名内部类不能有构造方法。

    ?匿名内部类不能定义任何静态成员、方法和类。

    ?匿名内部类不能是public,protected,private,static。

    ?只能创建匿名内部类的一个实例。

    ?一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。

    ?因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。


    4.线程间通信 :

      notify() wait() notifyAll();

    Java中如何实现线程同步 
      在Java中,线程的同步涉及到synchronized和三个方法wait()、notify()和notifyAll()。 

      synchronized在上篇中已经讲过了,这里就不再重复了。 

      wait()方法与Thread类的sleep()和yield()方法类似,都是让当线程睡眠,或者说是暂停执行;与之不同的是wait()方法会释放掉当前对象的锁,也因为此wait()方法必须在synchronized块里才能被调用。 

      notify()和notifyAll()方法用于唤醒之前调用wait()方法睡眠的线程,与wait()方法一样,notify()和notifyAll()也必须在synchronized块里才能被调用。 

      注意wait()、notify()和notifyAll()三个方法都是Object类的,而不是Thread类的。因为这三个方法都涉及的锁的操作,而锁的操作适用于所有的对象。

  • 相关阅读:
    DataTable.Compute功能
    ip的划分,超详细
    静态页 htm传参数
    [你必须知道的.NET] 第四回:后来居上:class和struct
    [你必须知道的.NET] 第八回:品味类型值类型与引用类型(上)-内存有理
    [你必须知道的.NET] 第五回:深入浅出关键字把new说透
    作废
    XML Schema <第三篇>
    XML基础<第一篇>
    NHibernate之配置文件属性说明
  • 原文地址:https://www.cnblogs.com/alexlo/p/2959507.html
Copyright © 2011-2022 走看看