线程方法,生命周期,线程状态,Synchronized,

  1. package com.hspedu.method;
    
    /**
     * @author 韩顺平
     * @version 1.0
     */
    public class ThreadMethod01 {
        public static void main(String[] args) throws InterruptedException {
            //测试相关的方法
            T t = new T();
            t.setName("老韩");
            t.setPriority(Thread.MIN_PRIORITY);//设置优先级1
            t.start();//启动子线程
    
    
            //主线程打印5 hi ,然后我就中断 子线程的休眠
            for(int i = 0; i < 5; i++) {
                Thread.sleep(1000);
                System.out.println("hi " + i);
            }
    
            System.out.println(t.getName() + " 线程的优先级 =" + t.getPriority());//1
    
            t.interrupt();//当执行到这里,就会中断 t线程的休眠.
    
    
    
        }
    }
    
    class T extends Thread { //自定义的线程类
        @Override
        public void run() {
            while (true) {
                for (int i = 0; i < 100; i++) {
                    //Thread.currentThread().getName() 获取当前线程的名称
                    System.out.println(Thread.currentThread().getName() + "  吃包子~~~~" + i);
                }
                try {
                    System.out.println(Thread.currentThread().getName() + " 休眠中~~~");
                    Thread.sleep(20000);//20秒
                } catch (InterruptedException e) {
                    //当该线程执行到一个interrupt 方法时,就会catch 一个 异常, 可以加入自己的业务代码
                    //InterruptedException 是捕获到一个中断异常.
                    System.out.println(Thread.currentThread().getName() + "被 interrupt了");
                }
            }
        }
    }
    
    

    中断只中断一次

  2.  

    package com.hspedu.method;
    
    /**
     * @author 韩顺平
     * @version 1.0
     */
    public class ThreadMethod02 {
        public static void main(String[] args) throws InterruptedException {
    
            T2 t2 = new T2();
            t2.start();
    
            for(int i = 1; i <= 20; i++) {
                Thread.sleep(1000);
                System.out.println("主线程(小弟) 吃了 " + i  + " 包子");
                if(i == 5) {
                    System.out.println("主线程(小弟) 让 子线程(老大) 先吃");
                    //join, 线程插队
                    //t2.join();// 这里相当于让t2 线程先执行完毕
                    Thread.yield();//礼让,不一定成功..
                    System.out.println("线程(老大) 吃完了 主线程(小弟) 接着吃..");
                }
    
            }
        }
    }
    
    class T2 extends Thread {
        @Override
        public void run() {
            for (int i = 1; i <= 20; i++) {
                try {
                    Thread.sleep(1000);//休眠1秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("子线程(老大) 吃了 " + i +  " 包子");
            }
        }
    }
    
    
  3. 用户线程和守护线程

    1. 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束

    2. 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结

    3. 常见的守护线程:垃圾回收机制

  4. package com.hspedu.method;
    
    /**
     * @author 韩顺平
     * @version 1.0
     */
    public class ThreadMethod03 {
        public static void main(String[] args) throws InterruptedException {
            MyDaemonThread myDaemonThread = new MyDaemonThread();
            //如果我们希望当main线程结束后,子线程自动结束
            //,只需将子线程设为守护线程即可
            myDaemonThread.setDaemon(true);
            myDaemonThread.start();
    
            for( int i = 1; i <= 10; i++) {//main线程
                System.out.println("宝强在辛苦的工作...");
                Thread.sleep(1000);
            }
        }
    }
    
    class MyDaemonThread extends Thread {
        public void run() {
            for (; ; ) {//无限循环
                try {
                    Thread.sleep(1000);//休眠1000毫秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("马蓉和宋喆快乐聊天,哈哈哈~~~");
            }
        }
    }
    
    
  5. 线程生命周期
  6. 线程状态。 线程可以处于以下状态之一:
    • NEW
      尚未启动的线程处于此状态。
    • RUNNABLE内核
      在Java虚拟机中执行的线程处于此状态。Ready【准备好了】和Running【真正的在执行】两个状态
    • BLOCKED
      被阻塞等待监视器锁定的线程处于此状态。
    • WAITING
      正在等待另一个线程执行特定动作的线程处于此状态。
    • TIMED_WAITING
      正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
    • TERMINATED
      已退出的线程处于此状态。
  7. 线程同步机制
    • 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性。
  8. 同步具体方法
    • 同步代码块
      synchronized(对象){//得到对象的锁,才能操作同步代码
          //需要被同步代码;
      }
      //synchronized还可以放在方法声明中,表示整个方法-为同步方法
      public synchronized void m1() {
      //需要被同步的代码
      
      }
  9. 互斥锁

    • java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性

    • 每个对象都对应一个可称为"互斥锁"的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。

    • 关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问

    • 同步的局限性:导致程序的执行效率要降低

    • 同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)

    • 同步方法(静态的)的锁为当前类本身

  10. 互斥锁注意事项

    • 同步方法如果没有使用static修饰:默认锁对象为this

    • 如果方法使用static修饰,默认锁对象:当前类.class

    • 实现的落地步骤:

      • 需要先分析上锁的代码

      • 选择同步代码块或同步方法

      • 要求多个线程的锁对象为同一个即可!

  11. 解决售票不同步问题Synchronized
    • package com.hspedu.syn;
      
      /**
       * @author 韩顺平
       * @version 1.0
       * 使用多线程,模拟三个窗口同时售票100张
       */
      public class SellTicket {
          public static void main(String[] args) {
      
              //测试
      //        SellTicket01 sellTicket01 = new SellTicket01();
      //        SellTicket01 sellTicket02 = new SellTicket01();
      //        SellTicket01 sellTicket03 = new SellTicket01();
      //
      //        //这里我们会出现超卖..
      //        sellTicket01.start();//启动售票线程
      //        sellTicket02.start();//启动售票线程
      //        sellTicket03.start();//启动售票线程
      
      
      //        System.out.println("===使用实现接口方式来售票=====");
      //        SellTicket02 sellTicket02 = new SellTicket02();
      //
      //        new Thread(sellTicket02).start();//第1个线程-窗口
      //        new Thread(sellTicket02).start();//第2个线程-窗口
      //        new Thread(sellTicket02).start();//第3个线程-窗口
      
              //测试一把
              SellTicket03 sellTicket03 = new SellTicket03();
              new Thread(sellTicket03).start();//第1个线程-窗口
              new Thread(sellTicket03).start();//第2个线程-窗口
              new Thread(sellTicket03).start();//第3个线程-窗口
      
          }
      }
      
      
      //实现接口方式, 使用synchronized实现线程同步
      class SellTicket03 implements Runnable {
          private int ticketNum = 100;//让多个线程共享 ticketNum
          private boolean loop = true;//控制run方法变量
          Object object = new Object();
      
      
          //同步方法(静态的)的锁为当前类本身
          //老韩解读
          //1. public synchronized static void m1() {} 锁是加在 SellTicket03.class
          //2. 如果在静态方法中,实现一个同步代码块.
          /*
              synchronized (SellTicket03.class) {
                  System.out.println("m2");
              }
           */
          public synchronized static void m1() {
      
          }
          public static  void m2() {
              synchronized (SellTicket03.class) {
                  System.out.println("m2");
              }
          }
      
          //老韩说明
          //1. public synchronized void sell() {} 就是一个同步方法
          //2. 这时锁在 this对象
          //3. 也可以在代码块上写 synchronize ,同步代码块, 互斥锁还是在this对象
          public /*synchronized*/ void sell() { //同步方法, 在同一时刻, 只能有一个线程来执行sell方法
      
              synchronized (/*this*/ object) {///*synchronized*/把这的去掉了,加到这了
                  if (ticketNum <= 0) {
                      System.out.println("售票结束...");
                      loop = false;
                      return;
                  }
      
                  //休眠50毫秒, 模拟
                  try {
                      Thread.sleep(50);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
      
                  System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票"
                          + " 剩余票数=" + (--ticketNum));//1 - 0 - -1  - -2
              }
          }
      
          @Override
          public void run() {
              while (loop) {
      
                  sell();//sell方法是一共同步方法
              }
          }
      }
      
      //使用Thread方式
      // new SellTicket01().start()
      // new SellTicket01().start();
      class SellTicket01 extends Thread {
      
          private static int ticketNum = 100;//让多个线程共享 ticketNum
      
      //    public void m1() {
      //        synchronized (this) {
      //            System.out.println("hello");
      //        }
      //    }
      
          @Override
          public void run() {
      
      
              while (true) {
      
                  if (ticketNum <= 0) {
                      System.out.println("售票结束...");
                      break;
                  }
      
                  //休眠50毫秒, 模拟
                  try {
                      Thread.sleep(50);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
      
                  System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票"
                          + " 剩余票数=" + (--ticketNum));
      
              }
          }
      }
      
      
      //实现接口方式
      class SellTicket02 implements Runnable {
          private int ticketNum = 100;//让多个线程共享 ticketNum
      
          @Override
          public void run() {
              while (true) {
      
                  if (ticketNum <= 0) {
                      System.out.println("售票结束...");
                      break;
                  }
      
                  //休眠50毫秒, 模拟
                  try {
                      Thread.sleep(50);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
      
                  System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票"
                          + " 剩余票数=" + (--ticketNum));//1 - 0 - -1  - -2
      
              }
          }
      }
      
      this是什么,SellTicket03.class又是什么
    • this是针对当前引用的对象,class对象就是原始对象本身
    • 非静态方法可以使用this或者class对象来同步,而静态方法必须使用class对象来同步并且它们互不影响
  12. 线程的死锁:多个线程都占用了对方的锁资源,但不肯让,导致死锁
  13. 释放锁

     

     

  14. 例题
    • package com.hspedu.homework;
      
      import java.util.Scanner;
      
      /**
       * @author 韩顺平
       * @version 1.0
       * 1.在main方法中启动两个线程
       * 2.第一个线程循环随机打印100以内的整数
       * 3.直到第2个线程从键盘读取了“Q”命令
       */
      public class Homework01 {
          public static void main(String[] args) {
              A a = new A();
              B b = new B(a);//一定要注意.
              a.start();
              b.start();
          }
      }
      
      //创建A线程类
      class A extends Thread {
          private boolean loop = true;
      
          @Override
          public void run() {
              //输出1-100数字
              while (loop) {
                  System.out.println((int)(Math.random() * 100 + 1));
                  //休眠
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
              System.out.println("a线程退出...");
      
          }
      
          public void setLoop(boolean loop) {//可以修改loop变量
              this.loop = loop;
          }
      }
      
      //直到第2个线程从键盘读取了“Q”命令
      class B extends Thread {
          private A a;//必须有这个属性
          private Scanner scanner = new Scanner(System.in);
      
          public B(A a) {//构造器中,直接传入A类对象
              this.a = a;
          }
      
          @Override
          public void run() {
              while (true) {
                  //接收到用户的输入
                  System.out.println("请输入你指令(Q)表示退出:");
                  char key = scanner.next().toUpperCase().charAt(0);
                  if(key == 'Q') {
                      //以通知的方式结束a线程
                      a.setLoop(false);
                      System.out.println("b线程退出.");
                      break;
                  }
              }
          }
      }
      

      创建线程,重写run方法,start(),获取字符,注意B类构造器的参数,属性

    • package com.hspedu.homework;
      
      /**
       * @author 韩顺平
       * @version 1.0
       */
      public class Homework02 {
          public static void main(String[] args) {
              T t = new T();
              Thread thread1 = new Thread(t);
              thread1.setName("t1");
              Thread thread2 = new Thread(t);
              thread2.setName("t2");
              thread1.start();
              thread2.start();
              //多线程创建对象是这样的
          }
      }
      
      //编程取款的线程
      //1.因为这里涉及到多个线程共享资源,所以我们使用实现Runnable方式
      //2. 每次取出 1000
      class T implements  Runnable {
          private int money = 10000;
      
          @Override
          public void run() {
              while (true) {
      
                  //解读
                  //1. 这里使用 synchronized 实现了线程同步
                  //2. 当多个线程执行到这里时,就会去争夺 this对象锁
                  //3. 哪个线程争夺到(获取)this对象锁,就执行 synchronized 代码块, 执行完后,会释放this对象锁
                  //4. 争夺不到this对象锁,就blocked ,准备继续争夺
                  //5. this对象锁是非公平锁.
      
                  synchronized (this) {//不是静态选this或者static修饰选类名.class。
                      //判断余额是否够
                      if (money < 1000) {
                          System.out.println("余额不足");
                          break;
                      }
      
                      money -= 1000;
                      System.out.println(Thread.currentThread().getName() + " 取出了1000 当前余额=" + money);
                  }
      
                  //休眠1s
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }
      }
      

你可能感兴趣的:(java,jvm,开发语言)