java 线程 小结

来源:互联网 发布:淘宝网宝贝链接打不开 编辑:程序博客网 时间:2024/05/16 06:25

 线程:
    进程的概念:
      进程是指在同一个操作系统(OS)中执行的子程序。
    多进程:同一个操作系统中执行的多个并行的子程序。
    线程:同一进程中执行的子程序流。
    多线程:同一个进程中执行的多个并行的子程序流。
    
     多进程的好处:提高CPU的使用率。
     多线程的好处:提高CPU的使用率。
     时间片方式(主流):全称叫CPU时间片,指的是CPU的一段执行时间,时间片分配给进程/线程,时间很短但能做很多事情。
                       有时间片的会抢占CPU,进程与进程抢,线程与线程抢。时间片用完了会再分。不管如何抢占,CPU的使用率也不会调到100%
    并发是针对的时间段。严格来说在一个时间点是无并发的。人所感受到的都是时间段
    
    线程与进程:
      多进程是独立的堆空间,独立的栈空间。进程与进程是互不影响的
      多线程是栈空间独立,堆空间共享。线程与线程是可以影响的。
      线程是由进程内部的一些顺序执行的代码组成的。
      没有进程不会有线程。一个进程可以有多个线程,有一个主线程,其它线程做为附加线程。
      
    JAVA中如何调用进程
      进程与操作系统相关,不能跨平台。JAVA中不推荐调进程。
      
      与进程相关的两个类:都在java.lang包中
         Runtime类(运行环境): exec()调用本地程序。
         Process:进程类,是exec()的返回类型。
         Runtime.getRuntime();返回与当前 Java 应用程序相关的运行时对象。
      
      线程模型:
        虚拟CPU(CPU时间片):Thead
        代码和数据由Runnable提供,run()运行代码,数据是子类属性
      
      写线程的方法:(课堂代码:TestThread.java,TestRunnable.java)
           1、继承Thread 类:
               1、extends Thead
               2、重写run()方法
               3、new 本类.start();
             好处:编写简单,可读性好
           2、实现Runnable接口
               1、implements Runnable
               2、重写run()方法
               3、new 本类,new Thread(本类对象)
               4、Thread对象.start();
             好处:保留了类继承,代码和逻辑分离,便于实现多线程。               
       run()和start()是不一样的:
           run()叫线程体方法,自身其实就是普通方法,run()不会启动线程
           start()叫线程启动的方法,向线程调度来说明当前线程ready(准备好了)。而不是正在执行的状态。
                     
      优先级:1--10,1最小,10最大,在某些操作系统中优先级会失效的。
              小优先级代表时间片小。
             
      守护线程依赖于其它线程
      join使自身加入主线程
      
     线程中几个重要的方法: (课堂代码:TestDaemon.java,TestSleep.java,TestJoin.java)
      Thread.currentThread()取当前线程(实现接口方式有时用这个)
      sleep():暂停线程,本线程不会抢,除非sleep运行完,这个是静态方法。自己让出CPU,其它的来抢。
      yield():暂停线程,给本类或>=本类优先级的。只给优先级高的让,一般优先级低的抢不到
      join():让本线程加入主线程。在线程内部、程序依然是顺序执行的,乱序体现在不同线程。
         tj.start();
        tj.join();要按这个顺序执行,若把join放在前面是无意义的。
      setDaemon(true)后台/守护线程 
      
   线程状态图:
       
    ▲ 初始状态              ▲阻塞状态               ▲终止状态
        /                 /           ┍ 1等待输入     ┓
         /               /sleep时间到    / 2sleep     /
          /start        /                 / 3join    /stop
           /           /                   /        /
            ┙       ┕                     /      /
         ▲ 可运行状态  _ _ _ OS选中 _ _ _/   ▲运行状态
          (只缺CPU)    /  CPU到期或调用yield
                    ┍                        /    /
                      /                      /      /wait
                       /        Synchronized/        /
                        /                  /          /
                         /                /            /
                          /              /              /
                           /           ┕               ┙
                             ▲ 锁池状态 <------ ▲等待队列
                                          notify/notifyall
      
        
        
.

同一个线程只能用start()启动一次。
 多线程编码多半采用实现接口的方式
 一个线程必须有自己的虚拟cpu
 一个线程可以和其他线程共享代码和数据
 
 synchronized可以加在方法层也可以加在代码块
 当对一个方法或一个代码块加上后,意味着线程在同步代码中必须采用串行访问,不能再并行。
 Synchronized用法
    1.Synchronized修饰代码块(同步代码块),
      public void push(char c){
          synchronized(this)//只有持有当前对象锁标记的线程才能访问这个代码块,this就是对象锁,锁定代码块
         {
           ...
          }
      }

      对括号内的对象加锁,只有拿到锁标记的对象才能执行该代码块
    2.Synchronized修饰方法
        public synchronized void push(char c) {
             ...
        }
       在整个方法里,对当前对象的加锁,只有拿到锁标记的对象才能执行该方法。
      
 对共享数据的读和写要一起同步,一般来说属性表示共享数据。
 同步的问题:串行,大大的降低效率。
 尽可能不要在方法层使用同步,使用synchronized。
 所有的对象都有对象锁,一个钥匙。
 
 池:池就是内存常驻
   常量池、线程池、DB连接池、等待池、同步池、对象池
   好处:快
   坏处:占去了部分内存
 
 同步最怕死锁
 对象锁正在使用的对象不能直接操作。
 
 interrupt()也会从运行状态到阻塞状态
 stop()存在潜在的安全隐患,所以不再用。
 1、对象-->锁--->对立
 2、同步依赖对象锁(对象)、锁对象相同,同步语句串行
 
 避免死锁:要保持顺序锁,不要回调,反向打开
 
 因为线程的死锁,从而引发要多线程的通信
 死锁:每个线程不释放自己拥有的资源,却申请别的线程拥有的资源,会造成死锁问题
       没有获得加锁对象的锁标记的线程,不能访问只有获得该对象所标记才能访问的同步方法,但可以访问这个对象的非同步的方法。

 例:     t1                           t2
     synchronized(o1){              synchronized(o2){
  synchronized(o2){           synchronized(o1){
  }                               }
     }                              }

          t1和t2会形成死锁
 
  Vector,Hashtable,StringBuffer 这些是同步的。
  ArrayList,HashSet,StringBuild 这些是不同步的。
 
  得到一个同步的ArrayList: List list=Collections.synchronizedList(new ArrayList());----外同步
                           List list=new Vector();-----外同步
 
  wait();参数可选,可以是0个,1个,2个。 对象锁,放锁,自己等。
    用 notify(); notifyAll(); 唤醒wait();
   notify()随机唤醒一个,notifyAll()全部唤醒,随机选。
   调用wait()方法表示让正在执行的线程停止执行进入对象的等待队列去等待,同时释放掉对象的锁
   注意:只能对加锁的资源进行wait()和notify()。
  1) wait():交出锁和CPU的占用;
  2) notify():将从对象的等待池中移走一个任意的线程,并放到锁池中,那里的对象一直在等待,直到可以获得对象的锁标记。
  3) notifyAll(): 将从等待池中移走所有等待那个对象的线程并放到锁池中,只有锁池中的线程能获取对象的锁标记,锁标记允许线程从上次因调用wait()而中断的地方开始继续运行
   
   怎么用线程?
    1、extends Thread,无共享时使用
    2、implements Runnable,有共享时使用
    
   同步有什么用:
     线程是共享堆、独享栈的,所以有可能发生两个线程同时操作同一块内存。
     同步可以使共同的操作变成单独操作。
     
   同步原理:对象锁+同步
         对象锁是对象所特有的锁,简单类型不是对象,没有锁。
    同步的优缺点:
      为了安全,同步不得不用。缺点就是效率非常低下。
    多线程编码:
      1、实现线程采用Runnable接口。
      2、有共享数据的冲突用同步 
      3、避免死锁(采用顺序锁,不要回调)
      4、若使用wait,notify进行交互时的原则
         (1)用notifyAll代替notify
         (2)wait是在同步语句的内部,用while循环
   
    在什么情况下放锁(交钥匙):
       1、同步代码执行完毕
       2、异常未处理
       3、wait()方法