黑马程序员Java__多线程

------- android培训java培训、期待与您交流! --------



7 java多线程
     进程:是一个正在执行中的程序,每一个进程都有一个执行顺序,该顺序是一个执行路径,
           或者是一个控制单元,一个进程中至少有一个线程。
     线程:是进程中的一个独立的控制单元,线程是控制着线程的执行。


   主线程:java vm 启动的时候会有一个进程java.exe,该进程中至少一个线程负责java程序的运行,还有一个线程是负责垃圾回收机制
    而且这个线程运行的代码是在main方法中,所以该线程称为主线程。
     
   7.1.创建线程


1) 继承Thread。
      1.定义类继承Thread。
      2.复写Thread类中的run方法。
      3.调用线程的start方法,该方法有两个作用:启动线程,调用run方法。  
       1)扩展thread类
             语法格式:


                public class YourThread extends Thread{


                     public void run(){
                               //希望执行的代码
                           }


                     } 


              调用格式:


                public static void main(String[] args){


                        YourThread t=new YourThread();
                        t.start();


                         }


  2) 实现runnable接口
       1.定义类实现runnable接口
       2.覆盖runnable接口中的方法,将要运行的代码放在run方法中。
       3.通过Thread类创建一个线程实例。
       4.将runnable接口的子类对象作为参数传给Thread类的构造函数,
           因为自定义的run方法所属对象是runnable接口的子类对象,所以必须让线程去指定对象的run方法,就必有明确该run方法的所属对象。
       5.调用Thread类的Start方法启动线程,并调用runnable接口的run方法。 
             语法格式:
                 
                public class YourThread implements Runnable{
           
                     public void run(){
                               //希望执行的代码
                           }         




                     }
              


               
              调用格式:


                public static void main(String[] args){


                        YourThread t=new YourThread();
                        Thead tt=new Thread(t);
                        tt.start();


                         }
        
      实现方式和继承方式有什么区别?
      1.实现方式:将要运行的代码放在runnable接口子类run方法当中。
      2.继承方式:将要运行的代码放在Thread类的run方法当中。
      3.实现方式的好处:避免了单继承的局限性;因为java只支持单继承。
      4.定义线程时,建议使用实现方式。
      
 
  7.2 线程的同步
      多线程产生安全问题的原因?
      因为当多条语句共同操作同一线程的共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一线程
      参与进来执行,导致共享数据的错误。
       解决方法:
       对多条操作共享数据的语句,只能让一个线程执行完,在执行过程中,其他线程不可以参以执行。
  
     1.方法同步
        通过在方法声明加入Synchronized关键字来声明methodName方法:
          
               public synchronized void methodName(parameterList){
                        ..........
                      }


      
     2.对象同步
        语法格式:
             synchronized (object){
                    //允许访问的控制代码         
                   }


    例如:


  /*
 * 使用二个线程来买票
 * 一个线程在两步代码块中
 * 一个在两步函数中
 * 都是在执行买票
 */


public class Tick implements Runnable {
   //假设有1000张票
 private int tick=100;


 @Override
 public void run() {
  while(true){ 
   //(2)采用synchronized来给当前this(tick)对象加锁
//   synchronized (this){
   this.show();
//   }
  }
 }
 //(1)采用synchronized来声明show方法
 public synchronized void show(){
  //判断如果票数大于0
  if(tick>0){
   //让当前线程睡眠
   try {Thread.sleep(10); } catch (Exception e) {}
   System.out.println(Thread.currentThread().getName()+".....sail:"+tick--);
  } 
 }
 public static void main(String[] args) {
  Tick t = new Tick();
  
   //创建二个线程
  Thread t1=new Thread(t);
  Thread t2=new Thread(t);
  //同时启动二个线程
  t1.start();
  t2.start(); 
 }
}


     3.对象如同锁,持有锁的线程可以在同步中执行,没有锁的线程即没有获取CPU的执行权,也进不去因为没有获取锁。
       经典案例-火车上的卫生间。


     4.同步的前提:
          1.必须两个或者两个以上的线程。
          2.必须是多个线程使用同一个锁。
     5.同步的优点:解决了多线程的安全问题,弊端多个线程需要判断较为消耗资源。
     6.同步函数使用是哪个锁?
       函数需要被对象调用,那么函数只有一个所属对象引用,就是this。
     7.如果同步函数被静态修饰后,使用的锁是什么?
       通过验证,发现不在是this,因为静态方法中也不可以定义this
       静态进内存是,内存中没有本类对象;但一定有该类的字节码文件对象,类名.class,该对象的类型是Class。
     8.静态的同步方法,使用的锁是该方法所在类的字节码文件对象,类名.class。


     如下 :


 /*
 * 使用二个线程来买票
 * 一个线程在两步代码块中
 * 一个在两步函数中
 * 都是在执行买票
 */


public class Tick implements Runnable {
   //假设有1000张票
 private static int tick=100;
    boolean flag=true;
 @Override
 public void run() {
  if(flag){
  while(true){ 
 //该方法所在的类的字节码对象Tick.class
   synchronized (Tick.class){
    if(tick>0){
     //让当前线程睡眠
     try {Thread.sleep(10); } catch (Exception e) {}
     System.out.println(Thread.currentThread().getName()+".....code:"+tick--);
    } 
   }
  }
 }else
  while(true)
   show();
}
 //同步方法被静态修饰
 public static synchronized void show(){
  //判断如果票数大于0
  if(tick>0){
   //让当前线程睡眠
   try {Thread.sleep(10); } catch (Exception e) {}
   System.out.println(Thread.currentThread().getName()+".....sail:"+tick--);
  } 
 }
 public static void main(String[] args) {
  Tick t = new Tick();
  
   //创建二个线程
  Thread t1=new Thread(t);
  Thread t2=new Thread(t);
  //同时启动二个线程
  t1.start();
  try {Thread.sleep(10); } catch (Exception e) {}
  t.flag=false;
  t2.start(); 
 }
}




     9.wait(),notify(),notifyAll()用来操作线程为什么定义在object类中。
      1.这些方法存在与同步中。
      2.使用这些方法时必须要标识所属的同步的锁。
      3.锁是任意对象,所以任意对象调用的方法一定定义在Object类中。
    10.wait(),sleep()的区别
       wait()是object类是的方法,释放资源,释放锁,
       只有通过对此对象发出notify(),notifyAll(),本线程才进入对象锁定池,准备获取对象锁进入运行状态。
       sleep()是Thread类的方法,释放资源,不释放锁。
    11.线程的通讯:多个线程共同访问一个资源,但所做的操作不一样;必须用到判断标记while(flag)、唤醒notifyAll()。


        实例:


      /*
 * 资源类:
 */
class Resoure {
 private String name;
 private int count=1;
 private boolean flag=false;
 public synchronized void set(String name){
  while(flag)
   //当前线程等待   t1,t2
   try {wait();} catch (Exception e) {}
  this.name=name+"----"+count++;
  System.out.println(Thread.currentThread().getName()+"......生产者....."+this.name);
     flag=true;
     //唤醒在此对象监视器上等待的所有线程
     this.notifyAll();
 }
 
 public synchronized void out(){
  while(!flag)
   //当前线程等待  t3,t4
   try {wait();} catch (Exception e) {}
  System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name);
  flag=false;
  //唤醒在此对象监视器上等待的所有线程
  this.notifyAll();
 }
}
    12.如何让线程停止?
       只有一种,让run方法结束,只要控制住循环就能让其结束。
       特殊情况:当线程处于了冻结状态,就不会读取该标记,线程就不会结束。当没有指定的方式让冻结的线程恢复到运行状态时,
       这时候要对冻结进行清除,这样就可以操作标记让线程结束。


      Thread类提供interrupt();


      如果线程在调用 de>Objectde> 类的 de>wait()de>、de>wait(long)de> 或 de>wait(long, int)de> 方法,或者该类的 de>join()de>、de>join(long)de>、de>join(long, int)de>、de>sleep(long)de> 或 de>sleep(long, int)de> 方法过程中受阻,则其中断状态将被清除,它还将收到一个 de>InterruptedExceptionde>。


  例如:


        public class StopThread implements Runnable {


 private boolean flag = true;


 public synchronized void run() {
  while (flag) {
   try {
    //线程处于冻结状态下,不会读取标记,线程不会结束。
    wait();
   } catch (InterruptedException e) {
    System.out.println(Thread.currentThread().getName()
      + "...........exception");
    flag = false;
   }
   System.out.println(Thread.currentThread().getName() + ".....run");
  }


 }


 public void chagerFlag() {
  flag = false;
 }
 public static void main(String[] arg){
    StopThread s = new StopThread();
    Thread t1= new Thread(s);
    Thread t2 = new Thread(s);
    t1.start();
    t2.start();
    int num=0;
    while(true){
     if(num++==60){
      s.chagerFlag();
      
      t1.interrupt();
    //中断该线程,将其中断状态清除。
      t2.interrupt();
      break;
     }
     System.out.println(Thread.currentThread().getName()+":"+num);
    }
    System.out.println("over");
   }
}


思路如下:


开始t1执行后,生产一个商品,flag为true,1放弃资格,t2判断flag为true,t2放弃资格。t3开始执行,判断flag为true,消费一个商品,flag为false, t4判断flag为false, t4放弃资格
当t1获得执行权后,执行完后flag为true,生产一个商品,唤醒了t2 。t1放弃资格,t2就直接往下执行了,然后又输出一个“生产者”,所以出现了生产两个商品的情况。
解决的方法就是将if改为while结构后,t2就不会再生产了,直接放弃资格,最后导致全部等待,所以要把this.notify()改为this.notifyAll()就可以解决了。
    13.join():当线程执行到了B线程的join方法时,A就会等待;等B执行完了,A才执行;可以用来临时加入线程执行!


--------- android培训java培训、期待与您交流! ----------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值