黑马程序员-多线程详解
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
}
}
 
 
                     
                    
                 
                    
                
 
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号