黑马程序员->多线程

来源:互联网 发布:linux查看内核版本 编辑:程序博客网 时间:2024/06/07 18:58

--------------------android培训、java培训、期待与您交流! -----------------

 

 

 1.....................多线程
  //////////////////////////////////////////////////////////////////////////////////////
  进程:是一个正在执行中的程序。
    //每一个进程执行都有一个执行顺序。该顺序是一个执行顺序,或者叫一个控制单元。
  线程:就是进程中一个独立的控制单元,线程在控制着进程的执行。
    //一个进程中至少有一个线程。
      如何在代码中创建线程呢?
  1.1.....通过对api的查找,java已经提供了对线程这类事物的描述,就是 Thread 类。
      创建线程的第一种方式: 继承 Thread 类。
      步骤: 1.定义类继承 Thread 类。
       2.复写 Thread 类中的 run 方法
              目的:将自定义的代码存储在run方法,让线程运行。
       3.调用线程的 start 方法。    //该方法有两个作用:启动线程,调用run方法。
    /* 运行结果每一次都不同,因为多个线程都获取cpu 的执行权,cpu执行到谁,谁就运行。明确一点,在某一时刻
     只能有一个程序在运行。cpu在作者快速的切换,一大道看上去是同时运行的效果。我们可以形象的把多线程
     的运行行为在互相抢夺cpu的执行权。
     这就是多线程的一个特性: 随机性。谁抢到谁执行,至于执行多长时间,cpu决定。*/
           只有调用 start 方法才会开启线程。
  1.2........线程运行状态
     被创建-----------》运行-------》stop()消亡
       |---》start()--| |------》sleep(time)冻结(放弃了执行资格)
           |-------》临时状态(阻塞)(具备运行资格,但没有执行权)
  1.3.........获取线程的对象和名称
          原来线程都有自己的名称, Thread -编号,该编号从 0 开始。
          对象.getName()-----> 即可获取线程名字。//等于 Thread。currentThread()
        static Thread currentThread() : 获取当前线程名称。
             getName() : 获取线程名称。
          设置线程名称 : setName 或者构造函数。
  1.4......创建线程第二种方式
     实现 Runnable 接口  
         步骤: 1.定义类实现 Runnable 接口
          2.覆盖 Runnable 接口中的 run 方法。
          3.通过 Thread 类建立线程对象。
          4.将 Runnable 接口中的子类对象作为子类对象传递给 Thread 类的构造函数。
      //自定义的 run 方法所属的对象是Runnable接口的子类对象,所以要让线程去指定对象的run方法,就必须
      //明确该run 方法所属对象。
          5.调用 Thread 类的 start 方法开启线程并调用 Runnable 接口子类的 run 方法。
      实现方式和继承方式的不同:
            实现的好处:避免了单继承的局限性。定义线程时,建议使用实现方式。
         继承 Thread :线程代码存放在 Thread 子类 run 方法中。
          实现 Runnable :线程代码存放在接口的子类的 run 方法中。
  1.5........线程的安全问题//synchronized  synchronized synchronized
     //当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完。
      //另一个线程参与进来执行,导致共享数据的错误。
     解决办法:
     //对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
       Java 对于多线程的安全问题提供了专业的解决方式。
    同步代码块
     synchronized (对象){
      //需要被同步的代码     
     }
     对象如同锁,持有锁的线程可以子啊同步中执行。 米有持有锁的线程即使获取了cpu的执行权,也进不去,因为没有锁。
   同步的前提:
      1,必须要有两个或者两个以上的线程。
      2.必须是多个线程使用同一个锁。
        //必须保证同步中只有一个线程在运行。
      好处:解决了多线程的安全问题。
      弊端:多个线程需要判断锁,较为消耗资源。
      //如何找同步的安全问题?
      1.明确哪些代码是多线程运行代码。
      2.明确共享数据。
      3.明确多线程运行代码中哪些语句是操作共享数据的。
  1.6.....同步函数
      除同步代码块外,还可以直接在函数上加 synchronized 修饰符----》同步函数。
     //函数需要被对象调用,那么函数都有一个所属对象应用,就是this。所有同步的锁是this。
     同步代码块用的锁是 对象,同步函数用的锁是 this 。
     如果同步函数被静态修饰后,锁不可能再使用 this ,因为静态加载时还木有 this 。
     //静态进驻内存后,内存中没有本类对象,但一定有该类对应的字节码文件对象。类名.class 。该对象的类型是Class。
       静态的同步方法,使用的锁是该方法所在类的字节码文件对象。
     死锁:
        同步中嵌套同步,锁不同。(//一定要避免死锁)
  1.7......线程间通信
    //就是多线程同时操作同一个资源,但是操作的动作不同。
    一面线程是存入数据,一面线程是取出数据,要涉及线程间通信问题,(等待唤醒机制),否则会出现取出数据
     //存入的数据不同的错误。
    各线程间应加  wait() 等待和  notify()唤醒语句,并加入一线程标记。
        ||      ||-------->//唤醒,属 Object 方法。还有 notifyall()唤醒全部。
        ||------->/*等待,属 Object 方法。*/还会抛 throws InsterruptedException  异常。
        ||------->一般要用 try ,不能抛。
    wait;
    notify();
    notifyall();
       都使用在同步中,因为要对是有监视器(锁)的线程操作。
       所以要使用在同步中,因为只有同步才有锁。
      //为什么这些操作线程的方法要定义在Object 类中呢?
      因为这些方法在操作同步中线程时,都必须要标示他们所操作线程只有的锁,只有同一个锁上的被等待
      线程,可以被同一个锁上 notify 唤醒。不可以对不同锁中的线程进行唤醒。
       也就是说,等待和唤醒必须是同一个锁。
      
      而锁可以是任意对象,所以可以被任意对象调用的方法,所以定义在 Object 类中。

  1.8.. 生产者消费者的例子// 等待唤醒机制。
         对于多个生产者和消费者。要定义while判断标记。因为要让被唤醒的线程再一次判断标记。
         //为什么到定义notifyAll?
         因为需要唤醒对方线程,只有用notifyAll,容易出现只唤醒本方线程的情况,导致程序中的
    所有线程都等待。
       java 1.5 升级新特性。
            java.util.concurrent.locks; 出现了新的包;
            出现了接口 Lock ;
     java 1.5 中提供了多线程升级解决方案:
   将同步 synchronized 替换成现实 Lock 操作;
   将 Object 中的 wait ,notify,notifyall ,替换成了 Condition 对象。
   该对象可以 Lock 锁进行获取;
          在示例中,实现了只唤醒对方操作。
   显式的锁机制。
 1.9.......................停止线程
   1. 定义循环结束标记------》 因为是线程运行代码一般都是循环,只要控制了循环即可。
   2. 使用 interrupt(中断)方法。--》该方法是结束线程的冻结状态,使线程回到运行状态中来。
   // stop 方法已经过时,不在使用。
   停止线程只有一种方法:让 run 方法结束。
   开启多线程,运行代码通常是循环结构,只要控制住循环,就可以让run方法结束,也就是线程结束。

   特殊情况: 当线程处于了冻结状态。就不会读到标记,那么线程即不会结束。
   //当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结状态进行清除,强制让线程恢复到运行
   //状态中来,这样就可以操作标记让线程结束。
   Thread 类提供该方法 interrupt();


 1.10..............................................守护线程   setDaemon(); 
   将该线程标记为守护线程或者用户线程。
       //即是将该线程标为后台线程,当前台线程结束,后台线程也跟着结束。---》就像守护者前台
       //线程一样。
   圣斗士星矢----------------------》守护雅典娜         。。。一样
    //当正在运行的线程都是守护线程时,java 虚拟机退出。
    //该方法必须在启动线程前使用。
 1.11.......................................join 方法
   等待该线程终止。在程序运行中,可以临时加入一个线程,让新加入的线程先运行完,在运行主线程。
   join:
     当 A 线程执行到了 B 线程的 join()方法时,A就会等待,等B线程都执行完,A 才会执行。
     join 可以用来临时加入线程。
 1.12..........................................优先级。
   用线程的 toString();方法可以看到线程的优先级。
   所有的线程,包括主线程,默认的优先级都是 5 。
   setPriority(); //方法可以设置线程的优先级。
        对象.setPriority(Thread.MAX_PRIORITY); 设置优先级要这样设置,不可写 数字。
   Thread.MAX_PRIORITY; ------> 10
   Thread.MIN_PRIORITY;-------->1
   Thread.NORM_PRIORITY;-------->5
   yield();//方法是暂停本线程,并执行另一个线程。 

 

-----------------------android培训、java培训、期待与您交流! --------------------

原创粉丝点击