多线程学习笔记(一)

来源:互联网 发布:快3遗漏号码数据查询 编辑:程序博客网 时间:2024/09/21 06:36
1、在java中要实现多线程,有两种手段,一种是继承Thread类,另外一种是实现Runable接口
1)继承了Thread类
public class HelloThread extends Thread{ private String name; public HelloThread(String name) {  this.name = name; }  @Override public void run() {  for(int i = 0; i < 5; i++)  System.out.println( name + "运行第 " + i + " 次"); }  public static void main(String[] arags) {  HelloThread h1 = new HelloThread("线程A");  HelloThread h2 = new HelloThread("线程B");  h1.start();  h2.start(); }}

2)实现了Runnable接口
public class HelloRunnable implements Runnable{ private String name;     public HelloRunnable(String name) {        this.name = name;    } @Override public void run() {  for(int i = 0; i < 5; i++)   System.out.println( name + "运行第 " + i + " 次"); }  public static void main(String[] args) {  HelloRunnable h1 = new HelloRunnable("线程A");  Thread t1 = new Thread(h1);  HelloRunnable h2 = new HelloRunnable("线程B");  Thread t2 = new Thread(h2);  t1.start();  t2.start(); }}

Thread其实也是实现了Runnable接口
class Thread implements Runnable {    //…public void run() {        if (target != null) {             target.run();        }        }}

Thread和Runnable的区别:
如果一个类继承Thread,则不适合资源共享,但用Runnable接口的类就可以实现资源共享。同时避免了单继承,所以推荐使用Runnable作为多线程的开启方法
public class MyThread implements Runnable{ //多个线程处理一个对象可实现资源共享 private int ticket = 5; @Override public void run() {  for(int i = 0; i <= 20; i++) {   if(ticket > 0) {    System.out.println(Thread.currentThread().getName() + "正在售票" +ticket--);   }  } }  public static void main(String[] args) {  MyThread my = new MyThread();  new Thread(my, "一号窗口").start();  new Thread(my, "二号窗口").start();  new Thread(my, "三号窗口").start(); }}

2、线程使用的方法有:
1)isAlive 判断线程是否还在运行
public static void main(String[] args) {  Thread t1 = new Thread();  System.out.println("线程启动前: " + t1.isAlive());  t1.start();  System.out.println("线程启动后: " + t1.isAlive()); }

2)join 在当前线程中调用另外一个线程的join方法,则会阻塞当前运行的线程直到调用join方法的线程执行完毕再继续执行,换句话说,如果调用join方法的线程没有执行完,则当前线程会一直等待,但其它无关线程不受阻碍。
    一般是用于使用子线程执行耗时操作,当要使用子线程中的结果时调用该方法把结果执行完毕再往下执行。
public static void main(String[] argss) {  Thread t1 = new Thread(new Runnable() {   @Override   public void run() {    try {     for(int i = 0; i < 3; i++) {      System.out.println("子线程: " +Thread.currentThread().getName());      Thread.sleep(1000); //sleep 1 秒     }         } catch (InterruptedException e) {     // TODO Auto-generated catch block     e.printStackTrace();    }   }  });  //开启线程  t1.start();  for(int i = 0; i < 50; i++) {   if(i > 8) {    try {     t1.join();  //当大于8时把t1线程执行完再往下执行    } catch (InterruptedException e) {     // TODO Auto-generated catch block     e.printStackTrace();    }   }   System.out.println("主线程执行到第 " + i + "次");  } }

结果:


可以看到当线程达到第8次时会先把子线程执行完在往下执行,你自己可以尝试一下。

3)wait 释放锁对象并等待,如果等待对象没有被锁住,则抛出异常IllegalMonitorStateException,换句话说,需要使用对象的wait方法,则需要在synchronized 语句块或方法体内使用。wait方法不是线程的方法,而是每一个对象的方法。所以针对的是访问该对象的当前线程的阻塞。

①该对象没有被当前线程锁住,则调用wait方法报错:
public static void main(String[] argss) {  Object object =new Object();  try {   object.wait();  } catch (InterruptedException e) {   e.printStackTrace();  } }


②使用synchronized语句块
 public static void main(String[] argss) {  Object object =new Object();  synchronized (object) {   try {    object.wait(); //阻塞访问当前对象的线程,即主线程,所以下面这句话永远不会打印出来    System.out.println("当前线程为: " + Thread.currentThread().getName());   } catch (InterruptedException e) {    e.printStackTrace();   }  } }

4)notify 唤醒在等待该对象锁的线程,每次只唤醒一个线程,同时不能控制唤醒的是哪一个线程。一般与wait互用
    我们还是使用上一个例子,但是因为当前线程已被阻塞,我想你不会在当前线程中使用objecty.notify来调用吧。我们需要另外一个线程中唤醒被阻塞的线程。

 //将对象变成静态类变量实现两个线程之间共享 static Object object =new Object(); public static void main(String[] argss) {  //使用t1对主线程进行唤醒操作  Thread t1 = new Thread(new Runnable() {   @Override   public void run() {    while(true) {     //因为不知道几时进行阻塞状态,所以需要不断循环     System.out.println("子线程运行中...");          synchronized (object) {      object.notify(); //唤醒主线程操作      System.out.println("唤醒后我会不会继续操作。。"); //调用notify之后不会阻塞当前线程,其它线程会等待当前线程执行完再争夺对象锁     }          try {      Thread.sleep(1000);     } catch (InterruptedException e) {      e.printStackTrace();     }         }      }  });  t1.start(); //开启子线程  synchronized (object) {   try {    System.out.println("主线程进行wait等待中");    object.wait(); //阻塞访问当前对象的线程,即主线程       System.out.println("当前线程为: " + Thread.currentThread().getName());   } catch (InterruptedException e) {    e.printStackTrace();   }  } }

结果:


如果操作的对象不是同一个,wait和notify也就没什么意义了。

5)synchronized关键字 用于保证同一时刻最多只有一个线程执行该代码段,用于锁定方法或者代码块

①synchronized方法
    如:public synchronized void accessVal(int newVal); 即创建该方法的实例并调用改方法时,同时刻只会有一个线程进行操作,锁对象是该方法的实例。如果是静态类方法,则获得的是类锁,类锁不是把所有该类对象实例都加锁,而是单单只是类信息,即Object.class对象。 如: public synchronized static void accessVal(int newVal);

②synchronized快
    上面代码中也有用到,即synchronized(要加锁的对象),可以使用synchronized(this)锁住当前对象实例,或者锁住你需要的共享对象。用法比较简单。

6)interrupt 通过抛出一个中断信号,让线程获得InterruptException,从而退出阻塞状态,但不会中断一个正在运行的线程 。如用Object.wait Thread.join 和 Thread.sleep阻塞,都可以使用interrupt方法提前退出阻塞。(个人觉得比较少用)

①.sleep() & interrupt()
 public static void main(String[] args) {  //这里有两个线程threadA和threadB,将threadA调用sleep方法,并在threadB中调用threadA.interrupt()方法触发异常。  final Thread threadA = new Thread(new Runnable() {   public void run() {    try {     System.out.println("进入sleep状态");     Thread.sleep(100000);    } catch (InterruptedException e) {     e.printStackTrace();    }    System.out.println("捕获Exception后运行的一行。。");   }  });   Thread threadB = new Thread(new Runnable() {   public void run() {    threadA.interrupt();   }  });   threadA.start();  //避免那么快执行完,先sleep一下  try {   Thread.sleep(100);  } catch (InterruptedException e) {   e.printStackTrace();  }  threadB.start(); }

结果:


②join() & interrupt()
interrupt的调用者一定是当前被阻塞的线程,同时这个被阻塞的线程由其他线程调用。这样才能发挥功效
public static void main(String[] args) {  //这里有三个线程。  //线程A会在i = 8时调用线程C的join方法,即自己被阻塞,让线程C方法运行完再继续运行  //线程B会调用线程A的interrupt方法提前退出join方法  final Thread threadC = new Thread(new Runnable() {   public void run() {    for(int i = 0; i < 10; i++) {     System.out.println("ThreadC 线程执行第: " + i + "次" );     try {      Thread.sleep(100);     } catch (InterruptedException e) {      e.printStackTrace();     }    }   }  });   final Thread threadA = new Thread(new Runnable() {   public void run() {    for(int i = 0; i < 30; i++) {     if(i == 8) {      try {       threadC.join();      } catch (InterruptedException e) {       e.printStackTrace();      }     }     System.out.println("ThreadA 线程执行第: " + i + "次" );    }   }  });   Thread threadB = new Thread(new Runnable() {   public void run() {    while(true) {     try {      Thread.sleep(500);     } catch (InterruptedException e) {      e.printStackTrace();     }     threadA.interrupt();    }        }  });  threadA.start();  threadB.start();  threadC.start();  }

结果:


③wait() & interrupt() 
 public static void main(String[] argss) {  final Object object = new Object();  final Thread threadA = new Thread(new Runnable() {   @Override   public void run() {        System.out.println("线程A开始执行...");          synchronized (object) {      try {       object.wait();      } catch (InterruptedException e) {       e.printStackTrace();      }      System.out.println("线程A继续执行。。。");     }         }  });  threadA.start(); //开启子线程   Thread threadB = new Thread(new Runnable() {   @Override   public void run() {    System.out.println("线程B开始执行");    //如果太快是会先打印出线程A继续执行。。而不是线程B开始执行    try {     Thread.sleep(100);    } catch (InterruptedException e) {     e.printStackTrace();    }    threadA.interrupt();   }  });  threadB.start(); }}

7) Thread.yield() 暂停线程的执行,给其它具有相同优先权的线程执行的集合,若没有其它线程执行,则线程继续执行。创建一个线程默认优先级为5,可以使用setPriority进行设置优先级。

    补充:线程有4个状态
    新建状态New 线程处于新建状态,还没调用start方法
    就绪状态Runnable:线程对象创建后,其它线程调用了该对象的start方法,该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
    运行状态Running:就绪状态的线程获得了CPU,执行程序代码。
    阻塞状态 Blocked:阻塞状态时线程因为某种原因放弃CPU使用权,暂时停止运行。需要重新进入就绪状态,等待获得CPU使用时间片
        (一)等待阻塞:运行的线程执行wait方法,JVM会把线程放入等待池
        (二)同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入池中
        (三)其它阻塞:运行的线程执行sleep或join方法,或者发出了I/0请求时,JVM会把线程设为阻塞状态,当之前方法完毕之后,线程重新转入就绪状态。
    死亡状态Dead:线程执行完或者异常退出,则该线程结束生命周期

8)suspend和resume。因为suspend容易发生死锁,调用suspend方法时,目标线程会停下来,但仍会拥有之前获得的锁。所以不建议使用。


















0 0
原创粉丝点击