• 使用多线程


            一个进程正在运行时至少会有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、共享数据的情况

  • 相关阅读:
    火柴排队sol
    国王游戏sol
    子串sol
    跳石头
    解方程sol
    花匠sol
    字符串整理
    计算系数
    矩阵取数游戏sol
    8.2_java_28
  • 原文地址:https://www.cnblogs.com/hanw1991/p/7487055.html
Copyright © 2020-2023  润新知