day13

来源:互联网 发布:淘宝贷利息多少 编辑:程序博客网 时间:2024/05/01 01:09
一、 
  ■ 阻塞状态 
  
    ▲ 初始状态                ▲阻塞状态          ▲终止状态 
        \                  /           ^ 1           ^ 
         \                /             \ 2sleep    / 
          \start        /                \ 3join   /stop 
           \           /                  \       / 
            V        V                     \     / 
         ▲ 可运行状态 _ _ _ _ OS选中 _ _ _ _\ ▲运行状态 
          (只缺CPU)   \  CPU到期或调用yield 
           
    下面为线程中的7中非常重要的状态:(有的书上也只有认为前五种状态:而将“锁池”和“等待池”都看成是“阻塞”状态的特殊情况:这种认识也是正确的,但是将“锁池”和“等待池”单独分离出来有利于对程序的理解) 
    1,初始状态,线程创建,线程对象调用start()方法。 
    2,可运行状态,也就是等待Cpu资源,等待运行的状态。 
    3,运行状态,获得了cpu资源,正在运行状态。 
    4,阻塞状态,也就是让出cpu资源,进入一种等待状态,而且不是可运行状态,有三种情况会进入阻塞状态。 
      1)如等待数据输入(输入设备进行处理,而CPU不处理),则放入阻塞,直到输入完毕,阻塞结束后会进入可运行状态。 
      2)线程休眠,线程对象调用sleep()方法,阻塞结束后会进入可运行状态。 
     public static void sleep(long millis) 
               throws InterruptedException 
    括号中以毫秒为单位, 使线程停止一段时间,间隔期满后,线程不一定立即恢复执行。 
    当main()运行完毕,即使在结束时时间片还没有用完,CPU也放弃此时间片,继续运行其他程序。 
  try{ 
  Thread.sleep(1000); 
  }catch(InterruptedException e){ 
  e.printStackTrace(e); 
  } 
  线程中有异常,只能trycatch,子类中不能抛出比父类更多的异常,父类run方法没有抛出异常。 
      3)线程对象2调用线程对象1的join()方法,那么线程对象2进入阻塞状态,直到线程对象1中止。 
       public final void join() throws InterruptedException 
    表示其他运行线程放弃执行权,进入阻塞状态,直到调用线程结束。 
    实际上是把并发的线程变为串行运行。 
t1 num 
t2 char  if(c=='m') -> t1.join() 
//t2对t1调用join,t2进入了阻塞状态 
//当条件成立时,t1加入打印数字,一直到打印完,此时t2继续运行 

    5,中止状态,也就是执行结束。 
    6,锁池状态 
    7,等待队列 
  
二、 共享数据的并发处理 
(一) 
  ■ 数据的错误发生 
  多线程并发访问同一个对象(临界资源) 
  破坏了原子操作,就会发生数据不一致的情况 

(二) 共享数据的并发处理 
  ■ 多线程同时并发访问的资源叫做临界资源。 
    多个线程同时访问对象并要求操作相同资源时分割了原子操作就会出现问题。(原子操作,不可再分的操作)会出现数据的不一致或数据不完整,为避免这种现象采用对访问的线程做限制的方法。 

  ■ 互斥锁机制,利用每个对象都有一个monitor(锁标记),当线程拥有这个锁标记时才能访问这个资源,没有锁标记便进入锁池。任何一个对象系统都会为其创建一个互斥锁,这个琐是为了分配给线程的,防止打断原子操作。每个对象的锁只能分配给一个线程。 

  ■ Synchronized用法 
    1.Synchronized修饰代码块(同步代码块), 
     public void push(char c){ 
         synchronized(this)//只有持有当前对象锁标记的线程才能访问这个代码块 
       { 
       ... 
         } 
     } 

      对括号内的对象加锁,只有拿到锁标记的对象才能执行该代码块 
    2.Synchronized修饰方法 
       public synchronized void push(char c) { 
            ... 
       } 
       在整个方法里,对当前对象的加锁,只有拿到锁标记的对象才能执行该方法。 

  ■ 
    ▲ 初始状态                ▲阻塞状态          ▲终止状态 
        \                  /          ┍ 1           ┓ 
         \                /             \ 2sleep    / 
          \start        /                \ 3join   /stop 
           \           /                  \       / 
            ┙       ┕                    \     / 
         ▲ 可运行状态  _ _ _ OS选中 _ _ _\ ▲运行状态 
          (只缺CPU)    \  CPU到期或调用yield 
                    ┍                        / 
                      \                      / 
                       \        Synchronized/ 
                        \                  / 
                         \               ┕ 
                              ▲ 锁池状态 
          

    锁池:一个空间,每个对象都有一个,用来存放等待锁标记的线程 
    当一个对象中有了锁标记,不会释放其它对象的锁标记。 

    当t1线程正在访问对象O的同步方法时,别的线程t2不能访问O的任何同步方法,但还是可以访问其它的非同步方法 

  ■ ArrayList Vector 
              list 
            △   △ 
            /     \ 
           /       \ 
          /         \ 
      ArrayList    Vector(所有方法都做成了同步) 
        
  ■ 构造方法   ×对象没有完全构造好了,没有当前对象概念 
    抽象方法   ×抽象方法没有代码块,没用 
    静态方法   √是对类对象的加锁 
          
    ☆注意:构造方法不能Synchronized修饰 
    静态方法可以用Synchronized修饰(是对类对象加锁,类对象会在反射时讲到) 
    抽象方法不能用Synchronized修饰,不影响子类覆盖,子类在覆盖这个方法是可以加Synchronized,也可以不加Synchronized,所以根据Java不允许写废代码的特点是不能写在一起。 


  ■ 练习: 
    1、用数组实现个栈;一个线程负责入栈一个线程负责出栈;长度为6 
    char[6], 
    T1, Push A~Z 
    T2, Pop 
    长度为6,并且不允许扩充 

    
----------------- 
注意:对当前对象加锁,一个代码块或者方法是同步的(Synchronized),当前对象的锁标记没有分配出去时,有一个线程来访问这个代码块时,就会的到这个对象的锁标记,直到这个线程结束才会释放着个锁标记,其他想访问这个代码块或者是方法线程就会进入这个对象锁池,如果没有得到当前对象的锁标记,就不能访问这个代码块或者是方法。当一个线程想要获得某个对象锁标记而进入锁池,这个线程又持有其他对象的锁标记,那么这个线程也不会释放持有的锁标记。 

注:方法的Synchronized特性本身不会被继承,只能覆盖。 
线程因为未拿到锁标记而发生阻塞进入锁池(lock pool)。每个对象都有自己的一个锁池的空间,用于放置等待运行的线程。由系统决定哪个线程拿到锁标记并运行。 

使用互斥锁的注意事项 

举例:男孩和女孩例子,每个女孩是一个对象,每个男孩是个线程。每个女孩都有自己的锁池。每个男孩可能在锁池里等待。 
Class Girl{ 
Public void hand(){ 


Public syncronized void kiss(){ 



Class Boy extends Thread{ 
Public void run(){ 




注意:只读不用加同步,只写也不用加同步,只有读写操作兼而有之时才加同步。 

注意:在java.io包中Vector 和 HashTable 之所以是线程安全的,是因为每个方法都有synchronized修饰。Static 方法可以加 synchronized , 锁的是类对象。但是Vector 是 jdk 1.0 的  ArrayList 是 jdk1.2 所以实际应用还是使用ArrayList。 

注意:内同步,外同步,内同步,即,类内的方法加同步(synchronized)修饰,外同步即,在需要控制只能由一个线程进行访问时,把需要控制的方法写在同步代码块里。
原创粉丝点击