多线程

1.1  线程概述

       在计算机中,所有应用都是有CPU执行的,一个CPU在某个时间点只能运行一个程序——即一个进程。

       操作系统的每一个进程都只是存在一个线程,即当一个Java程序启东市,线程运行main()方法中的代码。要在程序中实现多段代码交替运行,这需要创建多个线程——即多线程。多线程进程一样,也是有CPU轮流执行的,是不给CPU运行的速度很块。

1.2 线程的创建

       Java提供两种多线程实现方法:

  1)    继承java.lang包下的Tread类,覆盖Thread的run()方法,在run()中实现运行在线程上的代码;

  2)    实现java.lang.Runnable接口,在run()中实现运行在线程上的代码。

1.2.1 继承Thread类的创建多线程

       JDK提供一个线程类Thread,继承该类,重写run方法,可实现多线程,并且提供一个start()方法用于启动线程,程序启动后,线程会自动调用run()方法。

 1 public class Example01 {
 2     
 3     public static void main(String[] args) {
 4         MyThread myThread=new MyThread();
 5         myThread.start();
 6         while(true){
 7             System.out.println("main()方法在运行");
 8         }
 9         
10     }
11     public  static class MyThread extends Thread{
12         public void run() {
13             while(true){
14                 System.out.println("MyThread类的run()方法在运行");
15             }
16         }
17     }
18 
19 
20 }

  结果分析:两个while循环中的打印语句轮流执行。即main()方法和MyTread()类的中的Run()语句可以同时运行。

1.2.2 实现Runable接口创建多线程

       继承Thread类的创建多线程有一定局限性,一个类一旦继承某个父类就无法继承其他类(Java只支持单继承)。

 1 public class Example02 {
 2     public static void main(String[] args) {
 3         MyThread myThread=new MyThread();//创建MyThread的实例对象
 4         Thread thread=new Thread(myThread);//创建线程对象
 5         thread.start();
 6         while(true){
 7             System.out.println("main()方法在运行");
 8         }     
 9     }
10     public  static class MyThread implements Runnable{
11         public void run() {//线程代码块,当调用Start()方法时,线程从此处开始执行
12             while(true){
13                 System.out.println("MyThread类的run()方法在运行");
14             }
15         }
16     }
17 }

  结果分析:两个while循环中的打印语句轮流执行。即main()方法和MyTread()类的中的Run()语句可以同时运行。

1.2.3 两种多线程比较

       以售票大厅为类:售票大厅有四个窗口,共计售卖100张票。

A)   继承Thread类的创建多线程

 1 public class Example03 {
 2     public static class TicketWindow extends Thread{
 3         private int tickets=100;
 4         public void run() {
 5             while(true) {        //通过死循环打印语句
 6                 if(tickets>0) {
 7                     Thread th=Thread.currentThread();//获取当前线程
 8                     String th_name=th.getName();  //获取当前线程的名字
 9                     System.out.println(th_name+"正在发售第"+tickets--+"张票");
10                 }
11             }
12         }
13     }
14     public static void main(String[] args) {
15         new TicketWindow().start();//创建一个线程对象TicketWindow()并开启
16         new TicketWindow().start();//创建一个线程对象TicketWindow()并开启
17         new TicketWindow().start();//创建一个线程对象TicketWindow()并开启
18         new TicketWindow().start();//创建一个线程对象TicketWindow()并开启
19     }
20 }

  运行结果:Thread-0正在发售第100张票

Thread-3正在发售第100张票

Thread-2正在发售第100张票

Thread-1正在发售第100张票

Thread-2正在发售第99张票

Thread-3正在发售第99张票

Thread-0正在发售第99张票

Thread-3正在发售第98张票

Thread-2正在发售第98张票

Thread-1正在发售第99张票

Thread-2正在发售第97张票

Thread-3正在发售第97张票

Thread-0正在发售第98张票

…….

          分析可知:线程会自动编号,,用户创建的第一个线程为“Thread-0”,从0开始编号。但是四个线程都是从100章票开始往下减,独立出来自己的资源,且四个线程每次执行的次序都是随机的。(但是这种运行结果是不合理的)。

B)    实现Runable接口创建多线程

 1 public class Example04 {
 2     public static class TicketWindow implements Runnable{
 3         private int tickets=100;
 4         public void run() {
 5             while(true) {        //通过死循环打印语句
 6                 if(tickets>0) {
 7                     Thread th=Thread.currentThread();//获取当前线程
 8                     String th_name=th.getName();  //获取当前线程的名字
 9                     System.out.println(th_name+"正在发售第"+tickets--+"张票");
10                 }
11             }
12         }
13     }
14     public static void main(String[] args) {
15         TicketWindow tw =new TicketWindow();//创建TicketWindow实例对象tw
16         new Thread(tw,"窗口1").start();//创建线程对象并命名为窗口1,开启线程
17         new Thread(tw,"窗口2").start();
18     }
19 }

  分析:代码只创建了一个ThickWindow对象,然后创建了四个线程,四个线程都去调用这个TicketWindow对象中的run()方嘎,确保四个线程访问的是同一个tickets变量,共享100章车票。

  综上;Runnable接口相对于Threadgen有显著好处

       1)适合多个相同程序去处理同个资源的情况,把线程同程序代码、数据有效的分离;

       2)可以避免由于Java的单继承性带来的局限性;

1.3 线程的调度

       Java虚拟机会按照特定的机制为程序中的每个线程分配CPU的使用权,这种机制被称作线程的调度。

       线程的电镀有两种模型:分时调度模型和抢占式调度模型

  A)   分时调度模型:所有线程轮流获得CPU的使用权,并且平均分配每个下次占用的CPU的时间片

  B)    抢占式调度模型:可让运行迟中优先级高的线程先占用CPU,对于优先级相同的线程。随机选取一个线程使其占用CPU

1.3.1 线程的优先级

       对线程进行调度,可以设置线程的优先级。线程优先级用1-10的整数表示,数字越大优先级越高。

表5-1 Thread类的优先级常量

Threadl类的静态常量

功能描述

Static int MAX_PRIORITY

表示线程的最高优先级,10

Static int MIN_PRIORITY

表示线程的最低优先级,0

Static int NORM_PRIORITY

表示线程的普通优先级,5

       处于就绪状态的每个线程独有自己的优先级,main线程具有普通优先级。可以通过Thread类的setPriority(int newPriority)方法对其进行设置,该方法中的参数newPriority为1-10的整数或者Thread类的三个静态常量。

 1 public class Example5 {
 2     public static void main(String[] args) {
 3         Thread maxPriority=new Thread(new MaxPriority(),"优先级最高的线程");//创建线程对象
 4         Thread minPriority=new Thread(new MinPriority(),"优先级最低的线程");
 5         //设置优先级
 6         maxPriority.setPriority(10);
 7         minPriority.setPriority(Thread.MIN_PRIORITY);
 8         //开启两个线程
 9         minPriority.start();
10         maxPriority.start();    
11     }
12     //定义类MaxPriority实现Runnable接口
13     public  static class MaxPriority implements Runnable{
14         public void run() {//线程代码块,当调用Start()方法时,线程从此处开始执行
15             for(int i=0;i<10;i++) {
16                 System.out.println(Thread.currentThread().getName()+"正在输出"+i);
17             }
18         }    
19     }
20     //定义类MinPriority实现Runnable接口
21     public  static class MinPriority implements Runnable{
22         public void run() {//线程代码块,当调用Start()方法时,线程从此处开始执行
23             for(int i=0;i<10;i++) {
24                 System.out.println(Thread.currentThread().getName()+"正在输出"+i);
25             }
26         }    
27     }
28 }

1.3.2 线程休眠、让步、插队

  A)线程休眠:Java提供一个静态方法sleep(long millis),该方法可以让当前的正在执行的线程暂停一段时间,进入休眠等待状态。

       Sleep(long millis)方法声明抛出InterruptedException异常,英雌在调用该方法时应该不活异常,或者声明抛出异常。

B)线程让步:Java提供yield()方法来实现,该方法和sleep()方法相似,都可让当前正在运行的线程暂停。它的作用是是将线程转换成就绪状态,让系统调度器可以重新调度一次。

  C)线程插队:在线程中也提供一个方法join()使得线程可以“插队”。当在某个线程调用其它线程的join()方法时,调用的线程将被堵塞,直到被join()方法加入的线程执行完成后它才可以继续执行。

  join()方法声明抛出InterruptedException异常,英雌在调用该方法时应该不活异常,或者声明抛出异常。

 1 public class Example06 {
 2     public static void main(String[] args) throws Exception {
 3         Thread t=new Thread(new MaxPriority(),"线程一");//创建线程对象
 4         t.start();
 5         for(int i=0;i<10;i++) {
 6             System.out.println(Thread.currentThread().getName()+"正在输出"+i);
 7             if(i==2) {
 8                 t.join();
 9             }
10             Thread.sleep(500);
11         }    
12     }
13     //定义类MaxPriority实现Runnable接口
14         public  static class MaxPriority implements Runnable{
15             public void run() {//线程代码块,当调用Start()方法时,线程从此处开始执行
16                 for(int i=0;i<10;i++) {
17                     System.out.println(Thread.currentThread().getName()+"正在输出"+i);
18                     try {
19                         Thread.sleep(500);
20                     } catch (InterruptedException e) {
21                         // TODO Auto-generated catch block
22                         e.printStackTrace();
23                     }
24                 }
25             }    
26         }
27 }

 

分析运行结果:发现当main中的循环变量变为2是,调用t线程的join()方法,直到t线程执行完毕以后再执行main线程。

posted @ 2018-12-04 18:43  yj乐之  阅读(111)  评论(0编辑  收藏  举报