多线程学习

来源:互联网 发布:富士施乐驱动for mac 编辑:程序博客网 时间:2024/06/15 14:47

创建多线程

方法1:继承Thread类然后重写Thread类中的run()方法,run方法中的代码就是线程要执行的代码,然后调用start方法开启一个线程。
class Thread_Test extends Thread{
public void run(){
}
}
使用方法:Thread_Test t1 = new Thread_Test();
t1.start();
方法2:实现Runnable接口,覆盖Runnable接口中的run方法,将线程任务代码封装到run方法中,通过Thread类创建对线,并将Runnable接口中的子类对象作为参数传递给Thread类的构造函数,调用线程的start方法开启线程。
class Thread_Test2 implements Runnable {
public void run(){
}
}
使用方法:Thread_Test2 t2 = new Thread_Test2 ();
Thread t3 = new Thread(t2);
t3.start();

Thread和Runnable的区别

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
总结:
实现Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类

提醒一下大家:main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。

线程状态:
这里写图片描述

1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

 PS:同步函数使用的同步锁就是 this;静态函数属于类而不是具体对象,所以静态函数中是不存在this的,所以如果同步函数是静态的,锁显然就不是this!!!;静态的同步函数使用的锁是 该函数所述的字节码文件对象,该对象可以用 getClass方法获取,也可以用当前 类名.class 表示,但是此时由于getClass()是非静态方法,所以只能用类名.class。

线程调度

线程的调度

1、调整线程优先级:Java线程有优先级,优先级高的线程会获得较多的运行机会

Java线程的优先级用整数表示,取值范围是1~10,Thread类有以下三个静态常量:
static int MAX_PRIORITY
线程可以具有的最高优先级,取值为10。
static int MIN_PRIORITY
线程可以具有的最低优先级,取值为1。
static int NORM_PRIORITY
分配给线程的默认优先级,取值为5。

Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级。

每个线程都有默认的优先级。主线程的默认优先级为Thread.NORM_PRIORITY。
线程的优先级有继承关系,比如A线程中创建了B线程,那么B将和A具有相同的优先级。
JVM提供了10个线程优先级,但与常见的操作系统都不能很好的映射。如果希望程序能移植到各个操作系统中,应该仅仅使用Thread类有以下三个静态常量作为优先级,这样能保证同样的优先级采用了同样的调度方式。

2、线程睡眠:Thread.sleep(long millis)方法,使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。sleep()平台移植性好。

3、线程等待:Object类中的wait()方法,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是Object类中的方法,行为等价于调用 wait(0) 一样。

4、线程让步:Thread.yield() 方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。

5、线程加入:join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。

6、线程唤醒:Object类中的notify()方法,唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。 直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。类似的方法还有一个notifyAll(),唤醒在此对象监视器上等待的所有线程。
注意:Thread中suspend()和resume()两个方法在JDK1.5中已经废除,不再介绍。因为有死锁倾向。

常用方法

start
使线程开始执行,实际上这个方法会调用下面的run这个方法,如果这个线程已经开始执行,则会扔出IllegalThreadStateException

sleep
是当前已经运行的线程休眠一段时间。如果当前线程已经被别的线程中断的话,将会扔出InterruptedException,而且interrupted标志也会被清空。这个方法有两个版本,具体参看JDK文档。

run
线程执行的业务逻辑应该在这里实现。

join
等待另外一个线程死亡。如果当前线程已经被别的线程中断的话,将会扔出InterruptedException,而且interrupted标志也会被清空。

yield
使当前线程临时的中断执行,来允许其他线程可以执行,因为Java的线程模型实际上映射到操作系统的线程模型,所以对于不同的操作系统,这个方法的就有不同的意义。对于非抢占式Operating System,这个方法使得其他线程得到运行的机会,但是对于抢占式的OS,这个方法没有太多的意义。关于这个方法,后边还有更多的介绍。

wait
Wait方法和后边的两个方法都来自Object。看过Java源码的可以知道,这三个方法都是Native方法,使比较直接的和操作系统打交道的方法。
这个方法的作用是让当前线程等待,直到被唤醒或者等待的时间结束。当前线程进入等待队列的时候,会放弃当前所有的资源,所以当前线程必须获得这些对象的Monitor,否则会扔出IllegalMonitorStateException 关于wait方法的更多,后边会有介绍到。

notify
通知其他线程可以使用资源了。这个方法的使用要求很多,总之需要当前线程获得被调用的notify方法的对象的monitor。比如:

Synchronized (person) {                                                     person.notify();}

其实,获得monitor的方法还有别的,这里简单介绍一下:
1. 执行这个对象的一个同步的方法
2. 执行这个对象的同步块
3. 执行一个同步的静态方法

notifyAll
除了通知所有的线程可以准备执行之外,跟上面的方法要求一样。但是只有一个线程会被选择然后执行,这个就跟优先级和其他状态有关系了。

interrupt
中断线程。

sleep()和yield()的区别
sleep()和yield()的区别):sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会被执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
sleep 方法使当前运行中的线程睡眼一段时间,进入不可运行状态,这段时间的长短是由程序设定的,yield 方法使当前线程让出 CPU
占有权,但让出的时间是不可设定的。实际上,yield()方法对应了如下操作:先检测当前是否有相同优先级的线程处于同可运行状态,如有,则把
CPU 的占有权交给此线程,否则,继续运行原来的线程。所以yield()方法称为“退让”,它把运行机会让给了同等优先级的其他线程
另外,sleep 方法允许较低优先级的线程获得运行机会,但 yield() 方法执行时,当前线程仍处在可运行状态,所以,不可能让出较低优先级的线程些时获得 CPU
占有权。在一个运行系统中,如果较高优先级的线程没有调用 sleep 方法,又没有受到 I\O
阻塞,那么,较低优先级线程只能等待所有较高优先级的线程运行结束,才有机会运行。

线程同步

volatile
这个关键字告诉编译器不要对这个属性或者值进行优化,也就是为了保证这个变量的同步性,如果这个值被更新,其他线程应该可以马上访问到最新的值,而不是“脏值”。其实这个关键字是同步互斥的一个模型,但是现在没有实现。

synchronized
给对象或者Class加锁,分为对象锁和Class锁。
对象锁只是加在当前对象上,对别的对象没有影响,这种加锁一般都是把这个关键字用在方法和同步块上面。
Class锁就是加在这个Class上面,所有的其他对象想访问这个Class的对象的任何同步方法,必须获得这个锁。这种锁一般把这个关键字用在静态方法中,或者显示的这样实现:

synchronized (AClass.class) {      while (isSuspended) {              wait();    }}

一般我们很少用Class锁。
这里简单提一下monitor,个人感觉这里把monitor认为是一把锁也可以。网络上有朋友解释的比较好:在java中,每个对象只有一个相应的monitor,一个mutex,而每一个monitor都可以有多个“doors”可以进入,即,同一个monitor中被守护的代码可以在不同的地方,因为同一个对象可以出现在不同的代码段,只要mutex锁定的对象是同一个,每个入口都用Synchronized关键字表明,当一个线程通过了Synchronized关键字,它就所住了该monitor所有的doors.

例子

单单在概念上理解清楚了还不够,需要在实际的例子中进行测试才能更好的理解。对Object.wait(),Object.notify()的应用最经典的例子,应该是三线程打印ABC的问题了吧,这是一道比较经典的面试题,题目要求如下:

   建立三个线程,A线程打印10次A,B线程打印10次B,C线程打印10次C,要求线程同时运行,交替打印10次ABC。这个问题用Object的wait(),notify()就可以很方便的解决。代码如下:
public class MyThread implements Runnable {         private String name;         private Object prev;         private Object self;         private MyThread(String name, Object prev, Object self) {             this.name = name;             this.prev = prev;             this.self = self;         }         @Override        public void run() {             int count = 10;             while (count > 0) {                 synchronized (prev) {                     synchronized (self) {                         System.out.print(name);                         count--;                         self.notify();                     }                     try {                         prev.wait();                     } catch (InterruptedException e) {                         e.printStackTrace();                     }                 }             }         }         public static void main(String[] args) throws Exception {             Object a = new Object();             Object b = new Object();             Object c = new Object();             MyThread pa = new MyThread("A", c, a);             MyThread pb = new MyThread("B", a, b);             MyThread pc = new MyThread("C", b, c);             new Thread(pa).start();          Thread.sleep(100);  //确保按顺序A、B、C执行          new Thread(pb).start();          Thread.sleep(100);            new Thread(pc).start();             Thread.sleep(100);            }     }    

生产者浪费者模式

 //公共资源类 public class PublicResource {          private int number = 0;          /**          * 增加公共资源          */          public synchronized void increace() {              while (number != 0) {                  try {                      wait();                  } catch (InterruptedException e) {                      e.printStackTrace();                  }              }              number++;              System.out.println(number);              notify();          }          /**          * 减少公共资源          */          public synchronized void decreace() {              while (number == 0) {                  try {                      wait();                  } catch (InterruptedException e) {                      e.printStackTrace();                  }              }              number--;              System.out.println(number);              notify();          }      }  
/**  * 生产者线程,负责生产公共资源  */  public class ProducerThread implements Runnable {      private PublicResource resource;      public ProducerThread(PublicResource resource) {          this.resource = resource;      }      @Override      public void run() {          for (int i = 0; i < 10; i++) {              try {                  Thread.sleep((long) (Math.random() * 1000));              } catch (InterruptedException e) {                  e.printStackTrace();              }              resource.increace();          }      }  }  /**  * 消费者线程,负责消费公共资源  */  public class ConsumerThread implements Runnable {      private PublicResource resource;      public ConsumerThread(PublicResource resource) {          this.resource = resource;      }      @Override      public void run() {          for (int i = 0; i < 10; i++) {              try {                  Thread.sleep((long) (Math.random() * 1000));              } catch (InterruptedException e) {                  e.printStackTrace();              }              resource.decreace();          }      }  }  
/* 测试类  */ public class ProducerConsumerTest {          public static void main(String[] args) {              PublicResource resource = new PublicResource();              new Thread(new ProducerThread(resource)).start();              new Thread(new ConsumerThread(resource)).start();              new Thread(new ProducerThread(resource)).start();              new Thread(new ConsumerThread(resource)).start();              new Thread(new ProducerThread(resource)).start();              new Thread(new ConsumerThread(resource)).start();          }      }