zoukankan      html  css  js  c++  java
  • 线程(java课堂笔记)

    1.两种方式的差异
    2.线程的生命周期
    3.线程控制(线程的方法)
    4.线程同步
    5.线程同步锁
    一、 两种方式的差异
    A extends Thread :简单
    不能再继承其他类了(Java单继承)同份资源不共享
    B implements Runnable:( 推荐) )多个线程共享一个目标资源,适合多线程处理同一份资源。
    该类还可以继承其他类,也可以实现其他接口。
    二、 线程的生命周期
    新建:当程序使用new创建一个线程后,该线程处于新建状态,此时他和其他java对象一样,仅仅由Java虚拟机为其分配内存并初始化成员变量值。【 Thread r = new Thread()】
    就绪:当线程对象调用start()方法后,该线程处于就绪状态,线程计入线程队列排队,此时该状态线程并未开始执行,它仅表示可以运行了。至于该线程何时运行,取决于JVM线程调度器的调度。【 r.start() 】
    运行:若处于就绪状态的线程获得了CPU,开始执行run()线程执行体,该线程处于执行状态。
    阻塞:线程运行过程中需要被中断,目的是是其他的线程获得执行的机会。该状态就会进入阻塞状态。
    注意:阻塞状态不能直接转成运行状态,阻塞状态只能重新进入就绪状态。
    死亡:run()执行完成,线程正常结束;
    线程抛出未捕获的Exception或Error;
    调用线程的stop()。(易导致死锁,不推荐)
    注意:
    主线程结束后,其他线程不受其影响,不会随之结束;一旦子线程启动起来后,就拥有和主线程相等地位,不受主线程影响。
    例子:
    class MyThread implements Runnable {
      public void run() {
        for (int i = 0; i < 200;i++) {
          System.out.println(i);
        }
      }
    }
    public class MyThreadDemo {
      public static void main(String[] args) {
        for (int i = 0; i < 10;i++) {
          if(i == 5){
            MyThread t = new MyThread();
            new Thread(t).start();
          }
          System.out.println("main" + i);
        }
      }
    }
    测试线程是否活着,可用线程对象的isAlive()方法。当线程处于就绪,运行,阻塞状态返回true。
    当线程处于新建和死亡状态,返回false。
    已死亡的线程是不可能通过start()方法唤醒线程的。
    否则引发IllegalThreadStateException异常;
    public class DieThreadDemo {
      public static void main(String[]args) {
        DeadThread dt = new DeadThread();
        for (int i = 0; i < 100; i++) {
          Thread mainThread =Thread.currentThread();//获得当前线程(Main线程)
          System.out.println(mainThread.getName()+ "--" + i);
          System.out.println("------>"+mainThread.isAlive());
          if (i == 10) {
            dt.start();
          }
        }
        if (!dt.isAlive()) {
          dt.start();
        }
      }
    }
    class DeadThread extends Thread {
      public void run() {
        for (int i = 0; i < 50; i++) {
          System.out.println(getName() +"--" + i);
        }
      }
    }
    三、线程控制
    Join()方法:等待该线程终止。
    join 方法:调用join方法的线程对象强制运行,该线程强制运行期间,其他线程无法运行,必须等到该线程结束后其他线程才可以运行。
    有人也把这种方式成为联合线程
    join方法的重载方法:
    join(long millis):
    join(long millis,int nanos):
    通常很少使用第三个方法:
    程序无须精确到一纳秒;
    计算机硬件和操作系统也无法精确到一纳秒;
    public class JoinDemo {
      public static void main(String[]args) throws Exception {
        Thread join = new Thread(newJoin(),"Join线程");
        join.start();
        int i = 0;
        while(i < 500){
          if(i == 100){
            join.join();
          }
          System.out.println("Main -- > "+ i ++);
        }
      }
    }
    class Join implements Runnable {
      public void run() {
        int i = 0;
        while(i< 200){
          System.out.println(Thread.currentThread().getName() + "--" + i ++ );
        }
      }
    }
    setDaemon()方法
    后台线程:处于后台运行,任务是为其他线程提供服务。也称为“守护线程”或“精灵线程”。
    JVM的垃圾回收就是典型的后台线程。
    特点:若所有的前台线程都死亡,后台线程自动死亡。
    设置后台线程:Thread对象setDaemon(true);
    setDaemon(true)必须在start()调用前。
    否则出现IllegalThreadStateException异常;
    前台线程创建的线程默认是前台线程;
    判断是否是后台线程:使用Thread对象的isDaemon()方法;
    并且当且仅当创建线程是后台线程时,新线程才是后台线程。
    class Daemon extends Thread {
      public void run() {
        for (int i = 0; i < 10000; i++) {
          System.out.println(getName() +"-" + i);
        }
      }
    }
    public class DaemonDemo {
      public static void main(String[]args) {
        Daemon d = new Daemon();
        d.setDaemon(true);//把 d 线程设置成后台线程
        d.start();
        for (int i = 0; i < 5; i++) {
          System.out.println(Thread.currentThread().getName()+"--" + i);
        }
      }
    }
    Sleep()
    线程休眠:
    让执行的线程暂停一段时间,进入阻塞状态。
    sleep(long milllis) throws InterruptedException:毫秒
    sleep(long millis,int nanos) throws InterruptedException:毫秒,纳秒
    调用sleep()后,在指定时间段之内,该线程不会获得执行的机会。
    public class SleepDemo {
      public static void main(String[]args) {
        for (int i = 10; i > 0; i--) {
          System.out.println("还剩" + i);
          try {
            Thread.sleep(1000);
          } catch (InterruptedException e){
            e.printStackTrace();
          }
        }
      }
    }
    四、线程优先级
    每个线程都有优先级,优先级的高低只和线程获得执行机会的次数多少有关。
    并非线程优先级越高的就一定先执行,哪个线程的先运行取决于CPU的调度;
    默认情况下main线程具有普通的优先级,而它创建的线程也具有普通优先级。
    Thread对象的setPriority(int x)和getPriority()来设置和获得优先级。
    MAX_PRIORITY : 值是10
    MIN_PRIORITY : 值是1
    NORM_PRIORITY : 值是5(主方法默认优先级)
    class Priority extends Thread{
      public void run() {
        for (int i = 0; i < 100; i++) {
          System.out.println(getName()+",优先级=" + getPriority() +"--"+ i);
        }
      }
    }
    public class PriorityDemo {
      public static void main(String[]args) {
        Thread.currentThread().setPriority(7);
        for (int i = 0; i < 100; i++) {
          if(i == 10){
            Priority p1 = new Priority();
            p1.setPriority(Thread.MAX_PRIORITY);
            p1.start();
          }
          if(i == 15){
            Priority p2 = new Priority();
            p2.setPriority(Thread.MIN_PRIORITY);
            p2.start();
          }
          System.out.println("main" + i);
        }
      }
    }
    Yield()方法
    线程礼让:
    暂停当前正在执行的线程对象,并执行其他线程;
    Thread的静态方法,可以是当前线程暂停,但是不会阻塞该线程,而是进入就绪状态。
    所以完全有可能:某个线程调用了yield()之后,线程调度器又把他调度出来重新执行。
    class Yield implements Runnable {
      public void run() {
        for (int i = 0; i < 100; i++) {
          System.out.println(Thread.currentThread().getName() + "--> " + i);
          if (i % 2 == 0) {
            Thread.yield();// 礼让
          }
        }
      }
    }
    public class YieldDemo {
      public static void main(String[]args) {
        Yield y = new Yield();
        new Thread(y, "A").start();
        new Thread(y, "C").start();
      }
    }
    API 过时方法 -- 易死锁, , 不推荐使用
    stop:终止线程
    马上让线程停止运行,并释放该线程所持有的锁,该操作无法保证对象的内部状态正确;
    suspend:挂起线程
    使线程进入“阻塞”状态,该状态下CPU不会分给线程时间片,进入这个状态可以用来暂停一个线程的运行,在被resume方法调用前,不可用.
    如果要suspend的目标线程对一个重要的系统资源持有锁,那么没任何线程可以使用这个资源直到要suspend的目标线程被resumed。
    resume:恢复线程
    恢复被suspend方法挂起的线程Java2开始已经废弃了suspend()和resume()方法,因为使用这两个方法可能会产生死锁,所以应该使用同步对象调用wait()和notify()的机制来代替suspend()和resume()进行线程控制。
    五、线程的安全性问题
    导致安全问题的出现的原因:
    多个线程访问出现延迟。
    线程随机性。
    注:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。
    我们可以通过Thread.sleep(long time)方法来简单模拟延迟情况。
    六、线程同步
    当多个线程访问同一份数据的时候,很容易出现线程安全的问题。
    让多个操作在同一个时间段内只能有一个线程进行,其它线程要等到该线程完成后才可以继续执行。
    经典的事例: 银行取钱
    用户输入取款金额;
    系统判断余额是否大于取款金额
    若余额大于取款金额,取款成功;
    若余额小于取款金额,取款失败。
    例子分析:
    账户类:Account
    字段balance表示余额
    取钱类:DrawMoney 线程类
    字段a表示账户对象,money表示需要取的金额;
    把取钱行为放在线程体里面;
    提示:
    public void run(){
      if(c.getBlance() >= money){
        System.out.println(getName()+",取走" + money);
        Thread.sleep(100);
        c.setBlance(c.getBlance()- money);
        System.out.println("余额= " +c.getBlance());
      }else{
        System.out.println(getName()+ "余额不足,余额"+ c.getBlance() +",需要取走" +money );
      }
    }
    解决问题:
    方法一:
    当发生以上情况时,java给我们提供了同步代码块
    synchronized(obj){
    //obj表示同步监视器,是同一个同步对象
    /**.....
    TODO SOMETHING
    */
    }
    方法二:
    synchronized 返回值类型 方法名(参数列表){
    /**.....
    TODO SOMETHING
    */
    }
    同步方法的同步监听器其实的是 this
    /**
    * 账号
    *
    */
    public class Account {
    /**
    * 余额
    */
      private double blance;
      public Account(double blance) {
        this.blance = blance;
      }
      public double getBlance() {
        return blance;
      }
      public void setBlance(double blance){
        this.blance = blance;
      }
      public synchronized void draw(doubledrawMoney){
        if(getBlance() >= drawMoney) {
          System.out.println(Thread.currentThread().getName()+",取出" + drawMoney);
          try {
            Thread.sleep(1);
          } catch (InterruptedException e){
            e.printStackTrace();
          }
    //修改余额
          setBlance(getBlance() -drawMoney);
          System.out.println("余额= " +getBlance());
        }else{
          System.out.println("对不起余额不足");
        }
      }
    }
    /**
    * 取钱操作
    */
    public class DrawThread extendsThread{
    /**
    * 账号
    */
      private Account a;
    /**
    * 取的钱
    */
      public double drawMoney;
      public DrawThread(String name,Account a,double drawMoney){
        super(name);
        this.a = a;
        this.drawMoney = drawMoney;
      }
    /**
    * 当多条线程修改同一个共享资源数据的时候,将会导致数据安全问题
    */
      public void run(){
    /*   synchronized (a) {
          if(a.getBlance() >= drawMoney) {
            System.out.println(getName()+",取出" +drawMoney);
            try {
              Thread.sleep(1);
            } catch (InterruptedExceptione) {
              e.printStackTrace();
            }
    //修改余额
            a.setBlance(a.getBlance() -drawMoney);
            System.out.println("余额= " +a.getBlance());
          }else{
            System.out.println("对不起余额不足");
          }
        }*/
        a.draw(drawMoney);
      }
    }
    以上内容是课堂笔记之后是今天作业:做一个售票的程序
    要求三个窗口,一共50张票,随机售票,售完后求小明在每个窗口购票的几率:
    public class Dome {                         //主类
      public static void main(String[] args) {             //主方法
        Chuangkou c1 = new Chuangkou("窗口一");
        Chuangkou c2 = new Chuangkou("窗口二");
        Chuangkou c3 = new Chuangkou("窗口三");
        Thread t1 = new Thread(c1);               //新建三个线程
        Thread t2 = new Thread(c2);
        Thread t3 = new Thread(c3);
        t1.start();                         //三个线程同时运行
        t2.start();
        t3.start();
      }
    }
    
    public class Piao{                        //用来统计票数  
      public static String name = "目前只有这一种票";
      public static int piao = 1;
    }
    
    public class Chuangkou implements Runnable{           //窗口类
      public String name;
      static int a = 0;                        //定义三个静态变量用来统计每个窗口一共卖的票
      static int b = 0;
      static int c = 0;
      public Chuangkou(String name) {               //有参构造器
        this.name = name;
      }
      public void run(){                        //run方法
        while(Piao.piao<49){                    //如果票数等于49就停止
          try {
            Thread.sleep(1/10);                //经测试睡眠1/10的时间最有利于每个窗口的售票率
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          synchronized(Piao.name){                //同步,防止多个线程同时运行
            System.out.println(name+"卖出了第 "+Piao.piao+++"票");
          }
          switch(name){                      //统计窗口票数
            case "窗口一":a+=1;break;
            case "窗口二":b+=1;break;
            case "窗口三":c+=1;break;
          }
        }
        System.out.println(name+"售罄");
        if(Piao.piao == 50){                      //计算出小明购票几率
          System.out.println("小明买窗口一的票的概率为"+(a/50.0));
          System.out.println("小明买窗口二的票的概率为"+(b/50.0));
          System.out.println("小明买窗口三的票的概率为"+(c/50.0));
        }
    
      }
    }

    目前这个作业还有问题,不知为什么小明的效率有时会出现两次,有时不出现,希望大神指导一下。

  • 相关阅读:
    hiveserver2 with kerberos authentication
    python Basic usage
    python Quicksort demo
    Python HeapSort
    mrunit for wordcount demo
    CCDH证书
    Hadoop question list
    Hadoop Yarn core concepts
    Hadoop Resource
    Hadoop could not find or load main class
  • 原文地址:https://www.cnblogs.com/qihongbao/p/6780071.html
Copyright © 2011-2022 走看看