• 黑马程序员多线程详解


    package com.itheima;

    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;

    /**
     * 进程: 是一个正在执行的程序。
     *     每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元
     * 线程:就是进程中的一个独立单元,线程控制着进程的执行。
     * 一个进程至少有一个线程
     *
     * Jvm 启动时会有一个进程java.exe
     * 该进程中至少有一个县城负责java程序的执行
     * 而且这个线程运行的代码在main方法中,该线程为主线程
     *
     * 创建线程第一种方式:继承Thread类
     * 1.定义类继承Thread
     * 2.复写Thread类中的run() 目的:将定义的代码存储在run方法,让线程运行
     * 3.调用线程的start方法 该方法有两个作用:1.启动线程  2.调用run()
     * @param args
     */
    class myThread1 extends Thread{
     private int i=1;
     public void run(){
      while(true){
       System.out.println(i++);
      }
     }
    }
    /**
     * 创建线程方式二:实现Runnable接口
     * 1.定义类实现Runnable接口
     * 2.覆盖Runnable接口中的run()
     * 3.通过Thread类创建线程对象
     * 4.将Runnable接口的子类做为参数传递给Thread类的构造函数
     * 5.调用Thread类的start方法开启线程
     * */
    class MyThread2 implements Runnable{
     int i=1;
     public void run(){
      while(true){
       System.out.println(i++);
      }
     }
    }
    /**
     * 实现方式和继承方式的区别
     * 实现方式的好处:避免了单继承的局限性 ,在定义线程是,建议使用实现的方式
     * 继承Thread:线程代码存放在Thread子类的run方法中
     * 实现Runnable:线程代码存放在实现接口的子类run方法中
     * */

    //需求:做一个像买票一样的窗口
    class myThread3 extends Thread{
     private static int ticket=1000;
     public void run(){
      if(ticket>0)
       System.out.println(ticket--);
     }
    }
    /**
     * 在创建多个myThread3的对象时,发现没一次运行结果多不同
     * 因为多个线程同时获取cpu执行权,cpu执行到谁,谁就运行
     * 明确一点,在某一个时刻,只能运行一个程序的运行(多核除外)
     * cpu在坐着快速的切换,所以看上去是同事运行的效果
     * 我们可以形象的把多线程的行为看作是互相抢夺cpu执行权
     *
     * 这就是多线程的一个特性:随机性,谁抢到谁执行,至于执行多长cpu说的算
     * */

    /**
     * 多线程的六种状态:
     * 1.被创建 2.运行 3.冻结(没有放弃运行资格,休息一会) 4.冻结(放弃运行资格)
     * 5.临时状态 即阻塞状态 6.消亡
     * */

    /*
     * 线程都有自己默认的名称 Thread-编号 从0开始
     * Thread(String name);  分配给Thread对象名称
     *
     * static void currentThread() 返回当前这在执行党的线程的线程
     * 设置线程的名称:
     * 1.Thread.setName(Stirng name);
     * 2.通过构造函数
     *
     * */

    /*线程安全问题的原因:
     * 1.多个线程在操作共享的数据;
     * 2.操作共享数据的线程代码有多条
     * 当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会
     * 导致线程安全问题的产生
     */
    //线程安全的事例
    class Ticket implements Runnable{
     private static int num=1000;
     boolean flag = true;
     public void run(){
      if(flag){
       while(true){
         if(num>0){
          try{
           Thread.sleep(1000);
          }
          catch(InterruptedException e){
           
          }
          System.out.println(Thread.currentThread().getName()+"::"+num--);
         }
       }
      }
     }
    }
    // 会打印多个错误的号码,有的num也会重复,也会出现负数的现象
     /*
     * 解决办法:对多余操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其它线程不参与执行
     *
     * java对于多线程的安全问题,提供了专业的解决方案,就是同步代码块
     * synchronized(对象){
     *   需要执行的代码
     * }
     * 这里的对象如同锁,持有锁的线程可以在同步中执行,没有锁的线程,即使获取了cpu执行权,也进不去因为没有锁的获取
     *
     * 同步的好处:解决了线程的安全问题;
     * 同步的弊端:先对降低了效率,因为同步外的线程都会判断同步锁,比较耗费资源
     * 同步的前提:1.必须有两个或者以上的线程
     *      2.必须是多个线程使用同一个锁
     * 如何找问题:
     * 1.明确哪些代码是多线程运行的代码
     * 2.明确共享资源
     * 3.明确多线程运行代码中哪些语句是操作共享数据的
     * */
     class Ticket1 implements Runnable{
      private int num=1000;
      Object obj = new Object();
      public void run(){
       while(true){
        synchronized(obj){//锁必须是一个对象,如果这里new Object()则使用的不是同一个锁
         if(num>0){
          System.out.println(Thread.currentThread().getName()+"-----"+(num--));
         }
        }
       }
      }
     }
    //锁也可以定义在方法上,同步函数的锁是this
     /*
      * 同步函数和同步代码块的区别
      * 同步函数的锁是固定的this
      * 同步代码块的锁是任意对象
      * 建议使用同步代码块
      * 静态的同步函数使用的锁是,该函数的字节码文件,可以通过getClass()获取
      * */
     //死锁:就是同步中嵌套着同步
     /*
      * 死锁的条件:
      * 1.两个或另个以上的线程
      * 2.某一个线程拿到锁后,还想去拿第二把锁,即锁的嵌套
      * 以下提供一个死锁的事例
      * */
     class Test1 implements Runnable{
     private boolean flag;
     private Object lock1 = new Object();
     private Object lock2 = new Object();
     Test1(boolean flag){
      this.flag=flag;
     }
     public void run(){
      if(flag){
       synchronized(lock1){
        System.out.println(Thread.currentThread().getName()+"---取得了lock1");
        synchronized(lock2){
         System.out.println(Thread.currentThread().getName()+"--是否取得lock2");
        }
       }
      }else{
       synchronized(lock2){
        System.out.println(Thread.currentThread().getName()+"---取得了lock1");
        synchronized(lock1){
         System.out.println(Thread.currentThread().getName()+"--是否取得lock2");
        }
       }
      }
     }
     }
     //这样,线程里取得lock1之后,还想去lock2, 但是另一个线程不释放锁,互掐这样就死锁了
     /*
      * 线程间通讯:
      * 其实就是多个线程在操作同一个资源
      * */
     
     /*
      * 重点:等待/唤醒机制
      * 涉及的方法:
      * 1.wait():让线程处于冻结状态,被wait的线程会存储到线程池中
      * 2.notify():唤醒线程池中的一个线程(随机的)
      * 3.notifyAll():唤醒线程池中的所有线程
      *
      * 这些方法必须定义在同步中,因为要对持有锁的线程操作,只有同步中才有锁
      * 必须明确到底操作的是那个锁上的线程
      *
      *
      * 为什么操作线程的方法定义在Object类中?
      * 因为这些方法是监视器的方法,监视器其实就是锁
      * 锁可以是任意对象,任意的对象调用的方法一定定义在Object中
      *
      * wait和sleep的区别
      * 1.wait() 可以指定时间,也可以不指定,sleep必须指定时间
      * 2.在同步中,wait()释放执行权,并释放锁,sleep释放执行权,不释放锁
      * */
     
     /*
      * 停止线程:
      * 1.stop方法(已过时)
      * 2.run方法结束
      *  如何控制线程的任务?
      *  任务中都会有循环结构,只要控制住循环,就可以结束任务
      *  控制循环:通常用定义标记来完成
      *
      * 特殊情况:当线程处于冻结状态,就不会读取到标记,那么多线程就不会结束
      * 当没有指定方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除
      * 强制让线程恢复到运行状态中,这样就可以操作标记,让线程结束
      * Thread类提供了该方法,interrupt()
      * 但是强制动作会发生InerruptedException,需要处理
      *
      * */
     //事例:
     class StopThread implements Runnable{
      private boolean flag = true;
      public synchronized void run(){
       while(flag){
        try {
        this.wait();
       } catch (Exception e) {
        // TODO: handle exception
        System.out.println(Thread.currentThread().getName()+"我要改标记"+e);
        flag = false; //在异常中改变标记
       }
       System.out.println(Thread.currentThread().getName()+"我结束了");
       }
      }
     }
     class StopThreadDemo{
      public static void main(String[] args){
       StopThread st = new StopThread();
       Thread t1 = new Thread(st);
       for(int i=0;i<50;i++){
        if(i==30){
          t1.interrupt();
          break;
        }
        System.out.println("main"+i);
       }
       System.out.println("over");
      }
     }
     /*
      * 线程的其他分类讲解
      * 1.守护线程,需要再开启线程之前设置
      * 守护线程可以简单理解为后台线程
      * 当前台线程都结束时,后台线程自动结束
      * 当线程都变成守护线程是,进程结束 
      * 可以理解为线程的嵌套 线程里的线程
      */
     class Test3 implements Runnable
     {
      int count = 1;
      public void run(){
       while(true)
        System.out.println(Thread.currentThread().getName()+"----"+(count++));
      } 
     }
     class ThreadDemo1
     {
      public static void main(String[] args){
       Test3 test = new Test3();
       Thread t = new Thread(test);
       t.setDaemon(true);//这样t就是main主线程的守护线程
       t.start();
       for(int i=0;i<50;i++){
        System.out.println(Thread.currentThread().getName()+"================="+i);
       }
       System.out.println("Game is over");
      }
     }
     /* 2.join 方法,当A线程执行到了B线程的join 方法时,A就会等待B线程执行完,A才会执行
      * join可以用来临时加入线程执行
      * */
     class Test2 implements Runnable
     {
      int count = 1;
      public void run(){
       for(int i=0;i<30;i++)
        System.out.println(Thread.currentThread().getName()+"----"+(count++));
      } 
     }
     class ThreadJoin
     {
      public static void main(String[] args)throws Exception{
       Test2 test = new Test2();
       Thread t = new Thread(test);
      
       t.start();
       t.join();//join 方法一行定义在线程启动之后
       for(int i=0;i<50;i++){
        System.out.println(Thread.currentThread().getName()+"================="+i);
       }
       System.out.println("Game is over");
      }
     }
    public class ThreadSummary {

     
     public static void main(String[] args) {
      // TODO Auto-generated method stub

     }

    }

  • 相关阅读:
    第01组 Alpha冲刺(4/6)
    第01组 Alpha冲刺(3/6)
    第01组 Alpha冲刺(2/6)
    第01组 Alpha冲刺(1/6)
    第01组 团队Git现场编程实战
    第01组 团队项目-需求分析报告
    团队项目-选题报告
    团队作业-选题报告
    第二次结对编程作业
    第09组 Alpha冲刺(5/6)
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/2985439.html
Copyright © 2020-2023  润新知