使用多线程

        一个进程正在运行时至少会有1个线程在运行,这种情况在Java中也是存在的。这些线程在后台默默地执行,比如调用public static void main(String[] args)方法的线程就是这样的,而且它是由JVM创建的

        在Java的JDK开发包中,已经自带了对多线程的支持,可以很方便地进行多线程编程。实现多线程编程的方式主要有两种,一种是继承Thread类,另一种是实现Runnable接口,Thread类的结构如下:

      

       从上面的源代码中可以发现,Thread类实现了Runnable接口,他们之间具有多态关系。

       其实,使用继承Thread类的方式创建新线程时,最大的局限就是不支持多继承,因为Java语言的特点就是单根继承,所以为了支持多继承,完全可以实现Runnable接口的方式,一边实现一边继承。但用这两种方式创建的线程在工作时的性质是一样的,没有本质的区别。

       创建一个自定义的线程类MyThread.java,此类继承自Thread,并且重写run方法。  

 1 package com.mythread.www;
 2 
 3 public class MyThread extends Thread {
 4     
 5     @Override
 6     public void run() {
 7         super.run();
 8         System.out.println("MyThread");
 9     }
10     
11 }

       运行类代码如下:

 1 package test;
 2 
 3 import com.mythread.www.MyThread;
 4 
 5 public class Run {
 6     
 7     public static void main(String[] args) {
 8         MyThread myThread = new MyThread();
 9         myThread.start();
10         System.out.println("运行结束!");
11     }
12 
13 }

        运行结果如下:

       

        这行代码执行后,线程已处于就绪状态,但CPU还未分配时间片段给此线程,而是继续执行主线程main。

       从运行结果来看,MyThread.java类中的run方法执行的时间比较晚,这也说明在使用多线程技术时,代码的运行结果与代码执行顺序或调用顺序是无关的。

       线程是一个子任务,CPU以不确定的方式,或者说是以随机的时间来调用线程中的run方法,所以就会出现先打印 “运行结束!” 后输出 “MyThread” 这样的结果了。

       如果多次调用start()方法,则会出现如下异常:

 

 1 package test;
 2 
 3 import com.mythread.www.MyThread;
 4 
 5 public class Run {
 6     
 7     public static void main(String[] args) {
 8         MyThread myThread = new MyThread();
 9         myThread.start();
10         myThread.start();
11         System.out.println("运行结束!");
12     }
13 
14 }

 

    

       看下Thread类的源码就清楚了

      

        当第一次调用start()方法时,threadStatus初始值为0,表示线程还未开始执行,执行完start0()方法后,threadStatus的值将改变,不再为0,表示线程已经处于执行状态,当再次调用start()方法时,就会抛出上述异常。

--------------------------------------------------------------------------------------------------------------------------------

 1 package test;
 2 
 3 public class MyThread extends Thread {
 4     
 5     @Override
 6     public void run() {
 7         for (int i = 0; i < 5; i++) {
 8             int time = (int)(Math.random() * 1000);
 9 //          System.out.println("myThreadTime:" + time);
10             try {
11                 Thread.sleep(time);
12             } catch (InterruptedException e) {
13                 // TODO Auto-generated catch block
14                 e.printStackTrace();
15             }
16             System.out.println("myThread=" + Thread.currentThread().getName());
17         }
18     }
19 
20 }
 1 package test;
 2 
 3 public class Test {
 4     
 5     public static void main(String[] args) {
 6         MyThread myThread = new MyThread();
 7         myThread.setName("myThread");
 8         myThread.start();
 9 //      myThread.run();
10         for (int i = 0; i < 5; i++) {
11             int time = (int)(Math.random() * 1000);
12 //          System.out.println("mainTime:" + time);
13             try {
14                 Thread.sleep(time);
15             } catch (InterruptedException e) {
16                 // TODO Auto-generated catch block
17                 e.printStackTrace();
18             }
19             System.out.println("main=" + Thread.currentThread().getName());
20         }
21     }
22     
23 }

上述代码执行后的结果如下:

myThread.run()这段代码的注释去掉,再把myThread.start()注释掉后,运行的结果如下:

      Thread.java类中的start()方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象的run()方法。这个过程其实就是让系统安排一个时间来调用Thread中的run()方法,也就是使线程得到运行,启动线程,具有异步执行的效果。如果调用代码myThread.run()就不是异步执行了,而是同步,那么此线程对象并不给“线程规划器”来进行处理,而是由main主线程来调用run()方法,也就是必须等run()方法中的代码执行完后才可以执行后面的代码。

       另外需要注意的是,执行start()方法的顺序不代表线程启动的顺序。

 1 package test;
 2 
 3 public class Test {
 4     
 5     public static void main(String[] args) {
 6         MyThread t0 = new MyThread(0);
 7         MyThread t1 = new MyThread(1);
 8         MyThread t2 = new MyThread(2);
 9         MyThread t3 = new MyThread(3);
10         MyThread t4 = new MyThread(4);
11         MyThread t5 = new MyThread(5);
12         MyThread t6 = new MyThread(6);
13         MyThread t7 = new MyThread(7);
14         MyThread t8 = new MyThread(8);
15         MyThread t9 = new MyThread(9);
16         t0.start();
17         t1.start();
18         t2.start();
19         t3.start();
20         t4.start();
21         t5.start();
22         t6.start();
23         t7.start();
24         t8.start();
25         t9.start();    
26     }
27     
28 }

执行上述代码后,运行结果如下:

--------------------------------------------------------------------------------------------------------------------------------

       如果欲创建的线程类已经有一个父类了,这时就不能再继承自Thread类了,因为Java不支持多继承,所以就需要实现Runnable接口来应对这种情况。

 1 package test;
 2 
 3 public class MyRunnable implements Runnable {
 4     
 5     @Override
 6     public void run() {
 7         System.out.println("运行中!");
 8     }
 9 
10 }

       如何使用这个MyRunnable.java类呢?这就要看下Thread.java的构造函数了,如下图所示,其中三角符号标识的是default构造函数,其它的均为public构造函数

 1 package test;
 2 
 3 public class Run {
 4     
 5     public static void main(String[] args) {
 6         MyRunnable myRunnable = new MyRunnable();
 7         Thread thread = new Thread(myRunnable);
 8         thread.start();
 9         System.out.println("运行结束!");
10     }
11 
12 }

运行上述代码后,结果如下:

 

另外需要说明的是,Thread.java类也实现了Runnable接口,那就意味着构造函数Thread(Runnable target)不光可以传入Runnable接口的对象,还可以传入一个Thread类的对象,这样做完全可以将一个Thread对象中的run()方法交由其他的线程进行调用。

--------------------------------------------------------------------------------------------------------------------------------

实例变量与线程安全

1、不共享数据的情况

 1 package thread;
 2 
 3 public class MyThread extends Thread {
 4     
 5     private int count = 5;
 6     
 7     public MyThread(String name) {
 8         super();
 9         this.setName(name);
10     }
11     
12     @Override
13     public void run() {
14         while (count > 0) {
15             count--;
16             System.out.println(this.currentThread().getName() +
17                     ":count = " + count);
18         }    
19     }
20     
21     public static void main(String[] args) {
22         MyThread a = new MyThread("A");
23         MyThread b = new MyThread("B");
24         MyThread c = new MyThread("C");
25         a.start();
26         b.start();
27         c.start();
28     }
29 
30 }

 

执行上述代码运行结果如下:

从运行结果可以看出,每个线程都有各自的count变量,自己减少自己的count变量的值。这样的情况就是变量不共享,此示例并不存在多个线程访问同一个实例变量的情况。

2、共享数据的情况

 

posted @ 2017-09-06 21:27  #hanw  阅读(200)  评论(0编辑  收藏  举报