黑马程序员-多线程详解

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

 }

}

 

 

posted @ 2013-03-27 21:39  xinyuyuanm  阅读(146)  评论(0编辑  收藏  举报