zoukankan      html  css  js  c++  java
  • Java多线程编程(一)Java多线程技能

      一、进程和多线程的概念以及线程的优点

      打开Windo任务管理器可以看到很多正在运行着的exe程序,完全可以将运行在内存中的exe文件理解成进程,进程是受操作系统管理的基本运行单元。

      线程可以理解成在进程中独立运行的子任务。比如,QQ.exe运行时就有很多的子任务在同时运行。

      使用线程,可以最大限度地利用CPU的空闲时间来处理其他的任务,CPU在人物之间不停地切换,由于切换速度非常快,所以人们感觉这些任务似乎在同时运行。也就是说看,可以在同一时间内运行更多不同种类的任务,可以大幅增加CPU的利用率。

      二、使用多线程

      一个进程正在运行时至少会有一个线程在运行。

    1 package test;
    2 
    3 public class Test {
    4 
    5     public static void main(String[] args) {
    6         System.out.println(Thread.currentThread().getName());
    7     }
    8 
    9 }

      输出为:main。这里输出的main其实就是一个名称叫做main的线程在执行main()方法中的代码。

      1.继承Thread类

      实现多线程编程的方式主要有两种:一种是继承Thread类,另一种是实现Runnable接口。从Thread类的结构来看,Thread类实现了Runnable接口,它们之间具有多台关系。为了支持多继承,完全可以实现Runnable接口的形式,一边实现一边继承。

      1.1线程的调用的随机性

    1 public class MyThread extends Thread {
    2     @Override
    3     public void run() {
    4         super.run();
    5         System.out.println("MyThread");
    6     }
    7 }
     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 }
    运行结束
    MyThread
    

       从输出结果可以看出,代码的运行结果与代码执行顺序或调用顺序是无关的,线程是一个子任务,CPU以不确定的方式,或者说是以随机的时间来调用线程中的run方法。

      1.2 演示线程的随机性

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

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

      1.3 start()方法的顺序并不代表线程启动的顺序

     1 package extthread;
     2 
     3 public class MyThread extends Thread {
     4 
     5     private int i;
     6 
     7     public MyThread(int i) {
     8         super();
     9         this.i = i;
    10     }
    11 
    12     @Override
    13     public void run() {
    14         System.out.println(i);
    15     }
    16 
    17 }
     1 package test;
     2 
     3 import extthread.MyThread;
     4 
     5 public class Test {
     6 
     7     public static void main(String[] args) {
     8         MyThread t11 = new MyThread(1);
     9         MyThread t12 = new MyThread(2);
    10         MyThread t13 = new MyThread(3);
    11         MyThread t14 = new MyThread(4);
    12         MyThread t15 = new MyThread(5);
    13         MyThread t16 = new MyThread(6);
    14         MyThread t17 = new MyThread(7);
    15         MyThread t18 = new MyThread(8);
    16         MyThread t19 = new MyThread(9);
    17         MyThread t110 = new MyThread(10);
    18         MyThread t111 = new MyThread(11);
    19         MyThread t112 = new MyThread(12);
    20         MyThread t113 = new MyThread(13);
    21 
    22         t11.start();
    23         t12.start();
    24         t13.start();
    25         t14.start();
    26         t15.start();
    27         t16.start();
    28         t17.start();
    29         t18.start();
    30         t19.start();
    31         t110.start();
    32         t111.start();
    33         t112.start();
    34         t113.start();
    35 
    36     }
    37 
    38 }
    2
    3
    1
    4
    5
    7
    6
    8
    10
    9
    11
    12
    13
    

       从输出结果可以看出,start()方法的顺序并不代表线程启动的顺序。

      2.实现Runnable接口

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

    1 package myrunnable;
    2 
    3 public class MyRunnable implements Runnable {
    4     @Override
    5     public void run() {
    6         System.out.println("myrunnable类中的run!");
    7     }
    8 }
     1 package test;
     2 
     3 import myrunnable.MyRunnable;
     4 
     5 public class Run {
     6 
     7     public static void main(String[] args) {
     8         Runnable runnable=new MyRunnable();
     9         Thread thread=new Thread(runnable);
    10         thread.start();
    11         System.out.println("run中的main!");
    12     }
    13 
    14 }
    run中的main!
    myrunnable类中的run!
    

       Thread.java类的8个构造函数中,有两个可以传递Runnable接口的对象,并且由于Thread类也实现了Runnable接口,所以也就意味着构造函数Thread(Runnable target)不光可以传入Runnable接口的对象, 还可以传入一个Thread类的对象,这样做完全可以将一个Thread对象中的run()方法交由其他的线程进行调用。

      3.实例变量与线程安全

      自定义线程类中的实例变量针对其他线程可以有共享与不共享之分。

      3.1 不共享数据

     1 package mythread;
     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         super.run();
    15         while (count > 0) {
    16             count--;
    17             System.out.println("由" + this.currentThread().getName() + " 计算, count=" + count);
    18         }
    19     }
    20 }
     1 package test;
     2 
     3 import mythread.MyThread;
     4 
     5 public class Run {
     6     public static void main(String[] args) {
     7         MyThread a = new MyThread("A");
     8         MyThread b = new MyThread("B");
     9         MyThread c = new MyThread("C");
    10         a.start();
    11         b.start();
    12         c.start();
    13     }
    14 }
    由B 计算, count=4
    由C 计算, count=4
    由A 计算, count=4
    由A 计算, count=3
    由C 计算, count=3
    由B 计算, count=3
    由C 计算, count=2
    由A 计算, count=2
    由C 计算, count=1
    由C 计算, count=0
    由B 计算, count=2
    由B 计算, count=1
    由B 计算, count=0
    由A 计算, count=1
    由A 计算, count=0
    

       一共创建了3个线程,每个线程都有各自的count变量,自己减少自己的count变量的值。这样的情况就是变量不共享。

      3.2 共享数据

      共享数据的情况就是多个线程可以访问同一个变量,比如在实现投票功能的软件时,多个线程可以同时处理同一个人的票数。

      3.2.1“非线程安全”的问题

     1 package mythread;
     2 
     3 public class MyThread extends Thread {
     4 
     5     private int count=5;
     6     
     7     @Override
     8     public void run() {
     9         super.run();
    10             count--;
    11             System.out.println("由"+this.currentThread().getName()+"计算,count="+count);
    12     }
    13 }
     1 package test;
     2 
     3 import mythread.MyThread;
     4 
     5 public class Run {
     6     public static void main(String[] args) {
     7         MyThread mythread=new MyThread();
     8 
     9         Thread a=new Thread(mythread,"A");
    10         Thread b=new Thread(mythread,"B");
    11         Thread c=new Thread(mythread,"C");
    12         Thread d=new Thread(mythread,"D");
    13         Thread e=new Thread(mythread,"E");
    14         a.start();
    15         b.start();
    16         c.start();
    17         d.start();
    18         e.start();
    19     }
    20 }
    由B计算,count=3
    由A计算,count=3
    由C计算,count=2
    由D计算,count=1
    由E计算,count=0
    

       从输出结果可以看出,线程B和Acount的值都是3,说明A和B同时对count进行处理,产生了“非线程安全”问题。

      3.2.2解决“非线程安全”的问题

      将MyThread类修改一下即可。

     1 package mythread;
     2 
     3 public class MyThread extends Thread {
     4 
     5     private int count=5;
     6     
     7     @Override
     8     synchronized public void run() {
     9         super.run();
    10             count--;
    11             System.out.println("由"+this.currentThread().getName()+"计算,count="+count);
    12     }
    13 }
    由A计算,count=4
    由B计算,count=3
    由E计算,count=2
    由D计算,count=1
    由C计算,count=0
    

       通过在run方法前加入synchronized关键字,使多个线程在执行run方法时,以排队的方式进行处理。当一个线程调用run前,先判断run方法有没有被上锁,如果上锁,说明有其他线程正在调用run方法,必须等其他线程对run方法调用结束后才可以执行run方法。这样也就实现了排队调用run方法的目的,也就达到了按顺序对count变量减1的效果。

      synchronized可以在任意对象及方法上加锁,而加锁的这段代码称为“互斥区”或“临界区”。当一个线程想要执行同步方法里面的代码时,线程首先尝试去拿这把锁,如果能够拿到这把锁,那么这个线程就可以执行synchronized里面的代码。如果不能拿到这把锁,那么这个线程就会不断地尝试拿这把锁,直到能拿到为止,而且是多个线程同时去争抢这把锁。

      3.2.3另一个解决“非线程安全”的示例

     1 package controller;
     2 
     3 //生成一个servlet组件
     4 public class LoginServlet {
     5 
     6     private static String usernameRef;
     7     private static String passwordRef;
     8 
     9     public static void doPost(String username, String password) {
    10         try {
    11             usernameRef = username;
    12             if (username.equals("a")) {
    13                 Thread.sleep(5000);
    14             }
    15             passwordRef = password;
    16 
    17             System.out.println("username=" + usernameRef + " password="+ passwordRef);
    18         } catch (InterruptedException e) {
    19             e.printStackTrace();
    20         }
    21     }
    22 
    23 }
     1 package extthread;
     2 
     3 import controller.LoginServlet;
     4 
     5 public class ALogin extends Thread {
     6     @Override
     7     public void run() {
     8         LoginServlet.doPost("a", "aa");
     9     }
    10 }
     1 package extthread;
     2 
     3 import controller.LoginServlet;
     4 
     5 public class BLogin extends Thread {
     6     @Override
     7     public void run() {
     8         LoginServlet.doPost("b", "bb");
     9     }
    10 }
     1 package test;
     2 
     3 import extthread.ALogin;
     4 import extthread.BLogin;
     5 
     6 public class Run {
     7 
     8     public static void main(String[] args) {
     9         ALogin a = new ALogin();
    10         a.start();
    11         BLogin b = new BLogin();
    12         b.start();
    13     }
    14 
    15 }
    username=b password=bb
    username=b password=aa
    

       由于没有使用synchronized关键字,从而产生了“非线程安全”的问题。即在另一个线程没有等待sleep(5000)这个过程就执行了。修改代码使用synchronized关键字就可以解决这个问题。

     1 package controller;
     2 
     3 //生成一个servlet组件
     4 public class LoginServlet {
     5 
     6     private static String usernameRef;
     7     private static String passwordRef;
     8 
     9     synchronized public static void doPost(String username, String password) {
    10         try {
    11             usernameRef = username;
    12             if (username.equals("a")) {
    13                 Thread.sleep(5000);
    14             }
    15             passwordRef = password;
    16 
    17             System.out.println("username=" + usernameRef + " password="+ passwordRef);
    18         } catch (InterruptedException e) {
    19             e.printStackTrace();
    20         }
    21     }
    22 
    23 }
    username=a password=aa
    username=b password=bb
    

      4.留意i--与System.out.println()的异常

      println()方法与i++联合使用时“有可能”出现的另外一种异常情况。

     1 package extthread;
     2 
     3 public class MyThread extends Thread {
     4 
     5     private int i = 5;
     6 
     7     @Override
     8     public void run() {
     9         System.out.println("i=" + (i--) + " threadName="+ Thread.currentThread().getName());
    10     }
    11 
    12 }
     1 package test;
     2 
     3 import extthread.MyThread;
     4 
     5 public class Run {
     6 
     7     public static void main(String[] args) {
     8 
     9         MyThread run = new MyThread();
    10 
    11         Thread t1 = new Thread(run);
    12         Thread t2 = new Thread(run);
    13         Thread t3 = new Thread(run);
    14         Thread t4 = new Thread(run);
    15         Thread t5 = new Thread(run);
    16 
    17         t1.start();
    18         t2.start();
    19         t3.start();
    20         t4.start();
    21         t5.start();
    22 
    23     }
    24 
    25 }
    i=5 threadName=Thread-4
    i=5 threadName=Thread-1
    i=4 threadName=Thread-3
    i=3 threadName=Thread-5
    i=2 threadName=Thread-2
    

       需要说明的是,虽然println()方法在内部是同步的(有synchronized关键字),但是i--的操作却是在进入println()之间发生的,所以有发生“非线程安全”问题的概率。所以还是应该使用synchronized关键字。

     1 package extthread;
     2 
     3 public class MyThread extends Thread {
     4 
     5     private int i = 5;
     6 
     7     @Override
     8     synchronized public void run() {
     9         System.out.println("i=" + (i--) + " threadName="+ Thread.currentThread().getName());
    10     }
    11 
    12 }
    i=5 threadName=Thread-1
    i=4 threadName=Thread-2
    i=3 threadName=Thread-3
    i=2 threadName=Thread-5
    i=1 threadName=Thread-4
    

      三、currentThread()方法

      currentThread()方法可返回代码段正在被哪个线程调用的信息。

      示例1:结果说明main方法被名为main的线程调用。

    1 package run;
    2 
    3 public class Run1 {
    4     public static void main(String[] args) {
    5         System.out.println(Thread.currentThread().getName());
    6     }
    7 }
    main
    

       示例2:MyThread.java类的构造函数是被main线程调用的,而run方法时被名称为Thread-0的线程调用的,run方法是自动调用的方法。

     1 package mythread;
     2 
     3 public class MyThread extends Thread {
     4 
     5     public MyThread() {
     6         System.out.println("构造方法的打印:" + Thread.currentThread().getName());
     7     }
     8 
     9     @Override
    10     public void run() {
    11         System.out.println("run方法的打印:" + Thread.currentThread().getName());
    12     }
    13 
    14 }
     1 package run;
     2 
     3 import mythread.MyThread;
     4 
     5 public class Run2 {
     6     public static void main(String[] args) {
     7         MyThread mythread = new MyThread();
     8         mythread.start();
     9         //mythread.run();
    10     }
    11 }
    构造方法的打印:main
    run方法的打印:Thread-0
    

       示例3:修改示例2中的Run2.java。从结果中我们可以看出MyThread.java类的构造函数和run方法都是被main线程调用的,这里也就是说明了同步和异步的区别。

    package run;
    
    import mythread.MyThread;
    
    public class Run2 {
        public static void main(String[] args) {
            MyThread mythread = new MyThread();
            //mythread.start();
            mythread.run();
        }
    }
    构造方法的打印:main
    run方法的打印:main
    

      示例4:输出显示了this.getname()方法和currentThread().getname()的区别

     1 package mythread;
     2 
     3 public class CountOperate extends Thread {
     4 
     5     public CountOperate() {
     6         System.out.println("CountOperate---begin");
     7         System.out.println("Thread.currentThread().getName()="+ Thread.currentThread().getName());
     8         System.out.println("this.getName()=" + this.getName());
     9         System.out.println(Thread.currentThread() == this);
    10         System.out.println("CountOperate---end");
    11     }
    12 
    13     @Override
    14     public void run() {
    15         System.out.println("run---begin");
    16         System.out.println("Thread.currentThread().getName()="+ Thread.currentThread().getName());
    17         System.out.println("this.getName()=" + this.getName());
    18         System.out.println(Thread.currentThread() == this);
    19         System.out.println("run---end");
    20     }
    21 
    22 }
    package test;
    
    import mythread.CountOperate;
    
    public class Run {
    
        public static void main(String[] args) {
            CountOperate c = new CountOperate();
            Thread t1 = new Thread(c);
            t1.setName("test");
            t1.start();
        }
    
    }
    CountOperate---begin
    Thread.currentThread().getName()=main
    this.getName()=Thread-0
    false
    CountOperate---end
    run---begin
    Thread.currentThread().getName()=test
    this.getName()=Thread-0
    false
    run---end
    

      首先分析构造函数,根据输出可以看出,调用构造函数的是main线程,而此时还没有启动CountOperate子线程,所以打印出this.getName()=Thread-0,并且此时this代表的是CountOperate对象的实例,因此会输出false。

      然后分析run方法,从输出上来看,很让人疑惑的是run中的this.getName()=Thread-0这条输出。

      通过查看Thread类的源码,可以发现

      public Thread(Runnable target) {
            init(null, target, "Thread-" + nextThreadNum(), 0);
        }

      在Thread类的构造方法中,默认地会通过this.name=name给name赋值。也就是说,在Thread源码中实际上new Thread(c)会将c的应用对象绑定到一个private变量target上,在t1被执行的时候,即t1.run被调用的时候,它会调用target.run方法,也就是说它是直接调用c对象的run方法,也就是说,在run方法被执行的时候,this.getName()实际上返回的是target.getName()方法,而Thread.currentThread().getName()实际上是t1.getName()。

      示例5:修改示例4中的main方法

    package test;
    
    import mythread.CountOperate;
    
    public class Run {
    
        public static void main(String[] args) {
            CountOperate c = new CountOperate();
            c.setName("test");
            c.start();
        }
    
    }
    CountOperate---begin
    Thread.currentThread().getName()=main
    this.getName()=Thread-0
    false
    CountOperate---end
    run---begin
    Thread.currentThread().getName()=test
    this.getName()=test
    true
    run---end
    

       从输出结果中也可以看出,此时CountOperate构造函数和之前的输出保持一致,因为执行构造方法时线程还没有执行,所以this.getName()=Thread-0。而run方法中CountOperate线程已经执行并且可以获取到线程的名称,所以输出this.getName()=test。这也就是自动调用和被动调用的区别。

      四、isAlive()方法

      isAlive()方法用于判断当前的线程是否处于活动状态。

      示例1:end ==true说明mythread线程还没有执行完毕

    package mythread;
    
    public class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("run=" + this.isAlive());
        }
    }
    package run;
    
    import mythread.MyThread;
    
    public class Run {
        public static void main(String[] args) {
            MyThread mythread = new MyThread();
            System.out.println("begin ==" + mythread.isAlive());
            mythread.start();
            System.out.println("end ==" + mythread.isAlive());
        }
    }
    begin ==false
    end ==true
    run=true
    

       示例2:修改示例1程序。end ==false说明mythread对象已经在1秒之内执行完毕。

    package run;
    
    import mythread.MyThread;
    
    public class Run {
        public static void main(String[] args) throws InterruptedException{
            MyThread mythread = new MyThread();
            System.out.println("begin ==" + mythread.isAlive());
            mythread.start();
            Thread.sleep(1000);
            System.out.println("end ==" + mythread.isAlive());
        }
    }
    begin ==false
    run=true
    end ==false
    

      示例3:将线程对象以构造参数的方式传递给Thread对象进行start()启动时,运行的结果会有差异。造成这种结果的原因和之前遇到的情况一样,也就是Thread.currentThread()和this的区别。

    package mythread;
    
    public class CountOperate extends Thread {
    
        public CountOperate() {
            System.out.println("CountOperate---begin");
    
            System.out.println("Thread.currentThread().getName()="+ Thread.currentThread().getName());
            System.out.println("Thread.currentThread().isAlive()="+ Thread.currentThread().isAlive());
            System.out.println("this.getName()=" + this.getName());
            System.out.println("this.isAlive()=" + this.isAlive());
            System.out.println("CountOperate---end");
        }
    
        @Override
        public void run() {
            System.out.println("run---begin");
            System.out.println("Thread.currentThread().getName()="+ Thread.currentThread().getName());
            System.out.println("Thread.currentThread().isAlive()="+ Thread.currentThread().isAlive());
            System.out.println("this.getName()=" + this.getName());
            System.out.println("this.isAlive()=" + this.isAlive());
            System.out.println("run---end");
        }
    
    }
    package test;
    
    import mythread.CountOperate;
    
    public class Run {
    
        public static void main(String[] args) {
            CountOperate c = new CountOperate();
            Thread t1 = new Thread(c);
            System.out.println("main begin t1 isAlive=" + t1.isAlive());
            t1.setName("test");
            t1.start();
            System.out.println("main end t1 isAlive=" + t1.isAlive());
        }
    
    }
    CountOperate---begin
    Thread.currentThread().getName()=main
    Thread.currentThread().isAlive()=true
    this.getName()=Thread-0
    this.isAlive()=false
    CountOperate---end
    main begin t1 isAlive=false
    main end t1 isAlive=true
    run---begin
    Thread.currentThread().getName()=test
    Thread.currentThread().isAlive()=true
    this.getName()=Thread-0
    this.isAlive()=false
    run---end
    

      五、sleep()方法

      sleep()方法的作用是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)。这个“正在执行的线程”是只this.currentThread()返回的线程。

      示例1:直接使用mythread.run()方法同步启动线程,这就使得通过main线程来执行MyThread1的线程,通过输出可以发现线程休眠了2秒。

    package mythread;
    
    public class MyThread1 extends Thread {
        @Override
        public void run() {
            try {
                System.out.println("run threadName="+ this.currentThread().getName() + " begin");
                Thread.sleep(2000);
                System.out.println("run threadName="+ this.currentThread().getName() + " end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    package run;
    
    import mythread.MyThread1;
    
    public class Run1 {
        public static void main(String[] args) {
            MyThread1 mythread = new MyThread1();
            System.out.println("begin =" + System.currentTimeMillis());
            mythread.run();
            System.out.println("end   =" + System.currentTimeMillis());
        }
    }
    begin =1525254700337
    run threadName=main begin
    run threadName=main end
    end   =1525254702337
    

       示例2:在main方法中使用mythread.start()方法后,main线程和MyThread2线程异步执行,所以首先打印信息为begin和end。而MyThread2线程是随后运行的,在最后两行打印run begin和run end。

    package mythread;
    
    public class MyThread2 extends Thread {
        @Override
        public void run() {
            try {
                System.out.println("run threadName="+ this.currentThread().getName() + " begin ="+ System.currentTimeMillis());
                Thread.sleep(2000);
                System.out.println("run threadName="+ this.currentThread().getName() + " end   ="+ System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    package run;
    
    import mythread.MyThread2;
    
    public class Run2 {
        public static void main(String[] args) {
            MyThread2 mythread = new MyThread2();
            System.out.println("begin =" + System.currentTimeMillis());
            mythread.start();
            System.out.println("end   =" + System.currentTimeMillis());
        }
    }
    begin =1525254898564
    end   =1525254898564
    run threadName=Thread-0 begin =1525254898564
    run threadName=Thread-0 end   =1525254900564
    

      六、getId()方法

      getId()方法的所用是取得线程的唯一标识。从打印结果看,当前执行代码的线程名称为main,线程id为1。

    public class Run1 {
        public static void main(String[] args) {
            System.out.println(Thread.currentThread().getName());
            System.out.println(Thread.currentThread().getId());
        }
    }
    main
    1
    

      七、停止线程

      在Java中有一下3中方法可以终止正在运行得线程:

      ①使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。

      ②使用stop方法强行终止线程,但是不推荐使用这个方法,因为stop和suspend即resume一样,都是作废过期的方法,使用它们可能产生不可预料的结果。

      ③使用interrupt方法中断线程。

      1.停止不了的线程

      示例:通过调用interrupt()方法来停止线程,但interrupt()方法的使用效果并不像for+break语句那样,马上就停止循环。调用interrupt()方法仅仅是在当前线程打了一个停止的标记,并不是真的停止线程。从运行的结果也可以看出,调用interrupt方法并没有停止线程。

    package exthread;
    
    public class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 500000; i++) {
                System.out.println("i=" + (i + 1));
            }
        }
    }
    package test;
    
    import exthread.MyThread;
    
    import exthread.MyThread;
    
    public class Run {
    
        public static void main(String[] args) {
            try {
                MyThread thread = new MyThread();
                thread.start();
                Thread.sleep(2000);
                thread.interrupt();
            } catch (InterruptedException e) {
                System.out.println("main catch");
                e.printStackTrace();
            }
        }
    
    }
    ...
    i=499991
    i=499992
    i=499993
    i=499994
    i=499995
    i=499996
    i=499997
    i=499998
    i=499999
    i=500000
    

      2.判断线程是否是停止状态

      判断线程的状态是不是停止的,Thread.java类里提供了两种方法:①this.interrupted()测试当前线程是否已经中断,当前线程是指运行this.interrupted()方法的线程②this.isInterrupted()测试线程是否已经中断

      示例1:this.interrupted()使用。从打印情况来看,线程并未停止,这也就证明了interrupted()方法是测试当前线程是否已经中断,这个“当前线程”是main,它从未中断过,所以打印的结果是两个false。

    package exthread;
    
    public class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 500000; i++) {
                System.out.println("i=" + (i + 1));
            }
        }
    }
    package test;
    
    import exthread.MyThread;
    
    import exthread.MyThread;
    
    public class Run {
        public static void main(String[] args) {
            try {
                MyThread thread = new MyThread();
                thread.start();
                Thread.sleep(1000);
                thread.interrupt();
                //Thread.currentThread().interrupt();
                System.out.println("是否停止1?="+thread.interrupted());
                System.out.println("是否停止2?="+thread.interrupted());
            } catch (InterruptedException e) {
                System.out.println("main catch");
                e.printStackTrace();
            }
            System.out.println("end!");
        }
    }
    i=125615
    i=125616
    i=125617
    是否停止1?=false
    i=125618
    i=125619
    i=125620
    i=125621
    i=125622
    是否停止2?=false
    end!
    i=125623
    i=125624
    i=125625
    

       示例2:修改示例1中的run方法。Thread.currentThread().interrupt();确实使main线程产生了中断效果。但是令人困惑的是,是否停止2?=false这是因为,如果连续两次调用interrupt()方法,则第一次调用已经清楚了其中断状态后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外,其他情况下都会返回false。

    package test;
    
    import exthread.MyThread;
    
    import exthread.MyThread;
    
    public class Run2 {
        public static void main(String[] args) {
            Thread.currentThread().interrupt();
            System.out.println("是否停止1?=" + Thread.interrupted());
            System.out.println("是否停止2?=" + Thread.interrupted());
            System.out.println("end!");
        }
    }
    是否停止1?=true
    是否停止2?=false
    end!
    

       示例3:isInterrupted()方法并未清除状态标志,所以打印了两个true,和前面interrupted()方法的区别就是是否清除标志。

    package test;
    
    import exthread.MyThread;
    
    import exthread.MyThread;
    
    public class Run3 {
        public static void main(String[] args) {
            try {
                MyThread thread = new MyThread();
                thread.start();
                Thread.sleep(1000);
                thread.interrupt();
                System.out.println("是否停止1?="+thread.isInterrupted());
                System.out.println("是否停止2?="+thread.isInterrupted());
            } catch (InterruptedException e) {
                System.out.println("main catch");
                e.printStackTrace();
            }
            System.out.println("end!");
        }
    }
    i=143424
    i=143425
    是否停止1?=true
    i=143426
    i=143427
    i=143428
    i=143429
    i=143430
    i=143431
    i=143432
    i=143433
    i=143434
    i=143435
    i=143436
    i=143437
    i=143438
    i=143439
    i=143440
    i=143441
    i=143442
    i=143443
    i=143444
    i=143445
    i=143446
    i=143447
    i=143448
    i=143449
    i=143450
    i=143451
    i=143452
    是否停止2?=true
    i=143453
    

      3.能停止的线程--异常法

      可以在for语句来判断一下线程是否是停止状态,如果是停止状态,则后面的代码不再运行即可。

      示例1:虽然停止了线程,但是如果for语句下面还有语句,还是会继续运行的。

    package exthread;
    
    public class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 500000; i++) {
                if (this.interrupted()) {
                    System.out.println("已经是停止状态了!");
                    break;
                }
                System.out.println("i=" + (i + 1));
            }
        }
    }
    package test;
    
    import exthread.MyThread;
    
    import exthread.MyThread;
    
    public class Run {
    
        public static void main(String[] args) {
            try {
                MyThread thread = new MyThread();
                thread.start();
                Thread.sleep(2000);
                thread.interrupt();
            } catch (InterruptedException e) {
                System.out.println("main catch");
                e.printStackTrace();
            }
            System.out.println("end!");
        }
    
    }
    i=252895
    i=252896
    i=252897
    i=252898
    end!
    已经是停止状态了!
    

       示例2:验证如果for语句下面还有语句时,还是会继续运行的。

    package exthread;
    
    public class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 500000; i++) {
                if (this.interrupted()) {
                    System.out.println("已经是停止状态了!");
                    break;
                }
                System.out.println("i=" + (i + 1));
            }
            System.out.println("如果此代码是for又继续运行,线程并未停止!");
        }
    }
    package test;
    
    import exthread.MyThread;
    import exthread.MyThread;
    
    public class Run {
    
        public static void main(String[] args) {
            try {
                MyThread thread = new MyThread();
                thread.start();
                Thread.sleep(2000);
                thread.interrupt();
            } catch (InterruptedException e) {
                System.out.println("main catch");
                e.printStackTrace();
            }
            System.out.println("end!");
        }
    
    }
    i=251852
    i=251853
    i=251854
    已经是停止状态了!
    如果此代码是for又继续运行,线程并未停止!
    end!
    

       示例3:解决语句继续运行的问题,通过异常语句来停止线程。

    package exthread;
    
    public class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            try {
                for (int i = 0; i < 500000; i++) {
                    if (this.interrupted()) {
                        System.out.println("已经是停止状态了!");
                        throw new InterruptedException();
                    }
                    System.out.println("i=" + (i + 1));
                }
                System.out.println("我在for语句下面,你猜我会不会输出");
            } catch (InterruptedException e) {
                System.out.println("进run方法中的catch了");
                e.printStackTrace();
            }
        }
    }
    i=238006
    i=238007
    end!
    已经是停止状态了!
    进run方法中的catch了
    java.lang.InterruptedException
    	at exthread.MyThread.run(MyThread.java:11)
    

      4.在沉睡中停止

      示例1:在sleep()状态下停止线程(先sleep然后再interrupt()),会进入catch语句,并且清除停止状态值,使之变成false。

    package exthread;
    
    public class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            try {
                System.out.println("run begin");
                Thread.sleep(200000);
                System.out.println("run end");
            } catch (InterruptedException e) {
                System.out.println("在沉睡中被停止!进入了catch!"+this.isInterrupted());
                e.printStackTrace();
            }
        }
    }
    package test;
    
    import exthread.MyThread;
    
    import exthread.MyThread;
    
    public class Run {
    
        public static void main(String[] args) {
            try {
                MyThread thread = new MyThread();
                thread.start();
                Thread.sleep(200);
                thread.interrupt();
            } catch (InterruptedException e) {
                System.out.println("main catch");
                e.printStackTrace();
            }
            System.out.println("end!");
        }
    
    }
    run begin
    end!
    在沉睡中被停止!进入了catch!false
    java.lang.InterruptedException: sleep interrupted
    	at java.lang.Thread.sleep(Native Method)
    	at exthread.MyThread.run(MyThread.java:9)
    

       示例2:先执行interrupt停止线程,因此输出end!然后for循环执行完成,然后进入sleep,产生异常,进入catch。

    package exthread;
    
    public class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            try {
                for(int i=0;i<100000;i++){
                    System.out.println("i="+(i+1));
                }
                System.out.println("run begin");
                Thread.sleep(200000);
                System.out.println("run end");
            } catch (InterruptedException e) {
                System.out.println("先执行interrupt停止线程,再遇到sleep!进入catch!");
                e.printStackTrace();
            }
        }
    }
    package test;
    
    import exthread.MyThread;
    
    import exthread.MyThread;
    
    public class Run {
        public static void main(String[] args) {
            MyThread thread = new MyThread();
            thread.start();
            thread.interrupt();
            System.out.println("end!");
        }
    }
    end!
    i=1
    i=2
    i=3
    i=4
    i=5
    i=6
    i=7
    ...
    i=99998
    i=99999
    i=100000
    run begin
    先执行interrupt停止线程,再遇到sleep!进入catch!
    java.lang.InterruptedException: sleep interrupted
    	at java.lang.Thread.sleep(Native Method)
    	at exthread.MyThread.run(MyThread.java:12)
    

      5.能停止的线程--暴力停止

      示例:使用stop()方法停止线程则是非常暴力的。从输出可以看出,main线程停止了8秒然后被stop掉了,然后子线程每隔一秒执行输出一个数字。

    package testpackage;
    
    public class MyThread extends Thread {
        private int i = 0;
    
        @Override
        public void run() {
            try {
                while (true) {
                    i++;
                    System.out.println("i=" + i);
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    }
    package test.run;
    
    import testpackage.MyThread;
    
    public class Run {
    
        public static void main(String[] args) {
            try {
                MyThread thread = new MyThread();
                thread.start();
                Thread.sleep(8000);
                thread.stop();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    }
    i=1
    i=2
    i=3
    i=4
    i=5
    i=6
    i=7
    i=8
    

      6.方法stop()与java.lang.ThreadDeath异常

      示例:调用stop()方法会抛出java.lang.ThreadDeath异常,但在通常情况下,次异常不需要显示地捕捉。不过需要注意的是,stop()方法已经被作废,因为如果强制让线程停止则有可能使一些请理性的工作得不到完成。另外一个情况就是对锁定的对象进行了“解锁”,导致数据得不到同步的处理,出现数据不一致的问题。

    package testpackage;
    
    public class MyThread extends Thread {
        @Override
        public void run() {
            try {
                this.stop();
            } catch (ThreadDeath e) {
                System.out.println("进入run方法中的catch了");
                e.printStackTrace();
            }
        }
    }
    package test.run;
    
    import testpackage.MyThread;
    
    public class Run {
        public static void main(String[] args) {
            MyThread thread = new MyThread();
            thread.start();
        }
    }
    进入run方法中的catch了
    java.lang.ThreadDeath
    	at java.lang.Thread.stop(Unknown Source)
    	at testpackage.MyThread.run(MyThread.java:7)
    

      7.释放锁的不良后果

      示例:使用stop()释放锁将会给数据造成不一致的结果。如果出现这样的情况,程序处理的数据就有可能遭到破坏,最终导致程序执行的流程错误。这里的意思就是还没来得及给password赋值就把线程stop掉了。由于stop()方法已经作废了,所以不建议使用。

    package testpackage;
    
    public class SynchronizedObject {
    
        private String username = "a";
        private String password = "aa";
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        synchronized public void printString(String username, String password) {
            try {
                this.username = username;
                Thread.sleep(100000);
                this.password = password;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    }
    package testpackage;
    
    public class MyThread extends Thread {
    
        private SynchronizedObject object;
    
        public MyThread(SynchronizedObject object) {
            super();
            this.object = object;
        }
    
        @Override
        public void run() {
            object.printString("b", "bb");
        }
    }
    package test.run;
    
    import testpackage.MyThread;
    import testpackage.SynchronizedObject;
    
    public class Run {
        public static void main(String[] args) {
            try {
                SynchronizedObject object = new SynchronizedObject();
                MyThread thread = new MyThread(object);
                thread.start();
                Thread.sleep(500);
                thread.stop();
                System.out.println(object.getUsername() + " "+ object.getPassword());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    b aa
    

      8.使用return停止线程

      示例:将方法interrupt()与return结合使用也能实现停止线程的效果。线程运行了2秒才停止。不过还是建议用“抛异常”的方法来实现线程的停止,因为在catch块中还可以将异常向上抛,使线程停止的事件得以传播。

    package extthread;
    
    public class MyThread extends Thread {
    
        @Override
        public void run() {
                while (true) {
                    if (this.isInterrupted()) {
                        System.out.println("ֹͣ停止了!");
                        return;
                    }
                    System.out.println("timer=" + System.currentTimeMillis());
                }
        }
    
    }
    package test.run;
    
    import extthread.MyThread;
    
    public class Run {
    
        public static void main(String[] args) throws InterruptedException {
            MyThread t=new MyThread();
            t.start();
            Thread.sleep(2000);
            t.interrupt();
        }
    
    }
    timer=1525267712005
    timer=1525267712006
    timer=1525267712006
    timer=1525267712006
    timer=1525267712006
    ...
    timer=1525267714005
    timer=1525267714005
    timer=1525267714005
    timer=1525267714005
    timer=1525267714005
    ֹͣ停止了!

      八、暂停线程

      暂停线程意味着次线程还可以恢复运行。在Java中,使用suspend()方法暂停线程,使用resume()方法恢复线程的执行。

      1.suspend与resume方法的使用

      示例:通过输出我们可以看出,四行输出的时间间隔都是5秒,并且暂停和恢复控制线程中的run方法中的getI()方法的暂停和执行。

    package mythread;
    
    public class MyThread extends Thread {
    
        private long i = 0;
    
        public long getI() {
            return i;
        }
    
        public void setI(long i) {
            this.i = i;
        }
    
        @Override
        public void run() {
            while (true) {
                i++;
            }
        }
    
    }
    package test.run;
    
    import mythread.MyThread;
    
    public class Run {
    
        public static void main(String[] args) {
    
            try {
                MyThread thread = new MyThread();
                thread.start();
                Thread.sleep(5000);
                // A
                thread.suspend();
                System.out.println("A= " + System.currentTimeMillis() + " i="+ thread.getI());
                Thread.sleep(5000);
                System.out.println("A= " + System.currentTimeMillis() + " i="+ thread.getI());
                // B
                thread.resume();
                Thread.sleep(5000);
                // C
                thread.suspend();
                System.out.println("B= " + System.currentTimeMillis() + " i="+ thread.getI());
                Thread.sleep(5000);
                System.out.println("B= " + System.currentTimeMillis() + " i="+ thread.getI());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    }
    A= 1525268875079 i=2770802031
    A= 1525268880079 i=2770802031
    B= 1525268885079 i=5533965062
    B= 1525268890079 i=5533965062
    

      2.suspend与resume方法的缺点--独占

      示例1:在使用suspend与resume方法时,如果使用不当,极易造成公共的同步对象的独占,使得其他线程无法访问公共同步对象。因为线程a被锁定,所以线程的方法也被锁定,其他线程就无法访问这个方法。即独占并锁死了printString方法。

    package testpackage;
    
    public class SynchronizedObject {
    
        synchronized public void printString() {
            System.out.println("begin");
            if (Thread.currentThread().getName().equals("a")) {
                System.out.println("a线程永远suspend了");
                Thread.currentThread().suspend();
            }
            System.out.println("end");
        }
    
    }
    package test.run;
    
    import testpackage.SynchronizedObject;
    
    public class Run {
    
        public static void main(String[] args) {
            try {
                final SynchronizedObject object = new SynchronizedObject();
    
                Thread thread1 = new Thread() {
                    @Override
                    public void run() {
                        object.printString();
                    }
                };
    
                thread1.setName("a");
                thread1.start();
    
                Thread.sleep(1000);
    
                Thread thread2 = new Thread() {
                    @Override
                    public void run() {
                        System.out.println("thread2启动了,但进入不了printString方法");
                        System.out.println("因为printString方法被a线程锁定并且永远被suspend了");
                        object.printString();
                        System.out.println("你猜我会不会执行");
                    }
                };
                thread2.start();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    }
    begin
    a线程永远suspend了
    thread2启动了,但进入不了printString方法
    因为printString方法被a线程锁定并且永远被suspend了
    

       示例2:注意这里的main end!可以正常打印出来。将和之后的示例3作对比。

    package mythread;
    
    public class MyThread extends Thread {
        private long i = 0;
    
        @Override
        public void run() {
            while (true) {
                i++;
                //System.out.println(i);
            }
        }
    }
    package test.run;
    
    import mythread.MyThread;
    
    public class Run {
    
        public static void main(String[] args) {
    
            try {
                MyThread thread = new MyThread();
                thread.start();
                Thread.sleep(1000);
                thread.suspend();
                System.out.println("main end!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    }
    main end!
    

       示例3:修改示例2中的MyThread。控制台将不再打印main end!,这是因为当程序运行到println()方法内部停止时,同步锁未被释放。下面看一下println()方法的源码

        public void println(long x) {
            synchronized (this) {
                print(x);
                newLine();
            }
        }

      这导致当前PringStream的println()方法一直呈“暂停”状态,并且“锁未释放”,而main()方法当中的代码 System.out.println("main end!"); 迟迟不能执行打印!也就是线程中使用了println()方法然后被暂停,由于println()方法需要同步,即两个println()方法不能分别执行,所以就造成了第一个println()方法再暂停的情况下第二个不能执行的情况。

    package mythread;
    
    public class MyThread extends Thread {
        private long i = 0;
    
        @Override
        public void run() {
            while (true) {
                i++;
                System.out.println(i);
            }
        }
    }
    ...121036
    121037
    121038
    121039
    121040
    121041
    

      3.suspend与resume方法的缺点--不同步

      示例:和前面的stop()方法使得数据不同步的情况一样,使用suspend与resume方法也会造成数据不同步的情况。线程a被暂停了导致password字段的值不能被更新,所以线程2在执行的时候保留默认值。

    package myobject;
    
    public class MyObject {
    
        private String username = "1";
        private String password = "11";
    
        public void setValue(String u, String p) {
            this.username = u;
            if (Thread.currentThread().getName().equals("a")) {
                System.out.println("ֹͣ停止a线程");
                Thread.currentThread().suspend();
            }
            this.password = p;
        }
    
        public void printUsernamePassword() {
            System.out.println(username + " " + password);
        }
    }
    package test;
    
    import myobject.MyObject;
    
    public class Run {
    
        public static void main(String[] args) throws InterruptedException {
    
            final MyObject myobject = new MyObject();
    
            Thread thread1 = new Thread() {
                public void run() {
                    myobject.setValue("a", "aa");
                };
            };
            thread1.setName("a");
            thread1.start();
    
            Thread.sleep(500);
    
            Thread thread2 = new Thread() {
                public void run() {
                    myobject.printUsernamePassword();
                };
            };
            thread2.start();
    
        }
    
    }
    ֹͣ停止a线程
    a 11
    

      九、yield()方法

      yield()方法的作用是放弃当前的CPU资源,将它让给其他的任务去占用CPU执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。

      示例1:不添加yield()方法的情况。即线程独占CPU时间片来执行线程。用时21毫秒!

    package test;
    
    import extthread.MyThread;
    
    import extthread.MyThread;
    
    public class Run {
        public static void main(String[] args) {
            MyThread thread = new MyThread();
            thread.start();
        }
    
    }
    package extthread;
    
    public class MyThread extends Thread {
    
        @Override
        public void run() {
            long beginTime = System.currentTimeMillis();
            int count = 0;
            for (int i = 0; i < 50000000; i++) {
                //Thread.yield();
                count = count + (i + 1);
            }
            long endTime = System.currentTimeMillis();
            System.out.println("用时" + (endTime - beginTime) + "毫秒!");
        }
    
    }
    用时21毫秒!
    

      示例2:添加yield()方法的情况。将CPU时间片让给其他资源导致速度变慢很多。用时13017毫秒!

    package extthread;
    
    public class MyThread extends Thread {
    
        @Override
        public void run() {
            long beginTime = System.currentTimeMillis();
            int count = 0;
            for (int i = 0; i < 50000000; i++) {
                //Thread.yield();
                count = count + (i + 1);
            }
            long endTime = System.currentTimeMillis();
            System.out.println("用时" + (endTime - beginTime) + "毫秒!");
        }
    
    }
    用时13017毫秒!
    

      十、线程的优先级

      在操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU资源较多,也就是CPU优先执行优先级高的线程对象中的任务。设置线程优先级有助于帮“线程规划器”确定在下一次选择哪一个线程来优先执行。

      Java中的线程优先级分为1~10这10个等级,使用MIN_PRIORITY/NORM_PRIORITY/MAX_PRIORITY三个常量来预置优先级的值。设置线程的优先级使用setPriority()方法,源码如下

        public final void setPriority(int newPriority) {
            ThreadGroup g;
            checkAccess();
            if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
                throw new IllegalArgumentException();
            }
            if((g = getThreadGroup()) != null) {
                if (newPriority > g.getMaxPriority()) {
                    newPriority = g.getMaxPriority();
                }
                setPriority0(priority = newPriority);
            }
        }

      1.线程优先级的继承特性

      在Java中,线程的优先级具有继承性,比如A线程启动B线程,则B线程的优先级与A是一样的。

      示例1:优先级被继承,线程1启动线程2,两个线程的优先级是一样的,同时和mian线程也是一样的优先级。

    package extthread;
    
    public class MyThread1 extends Thread {
        @Override
        public void run() {
            System.out.println("MyThread1 run priority=" + this.getPriority());
            MyThread2 thread2 = new MyThread2();
            thread2.start();
        }
    }
    package extthread;
    
    public class MyThread2 extends Thread {
        @Override
        public void run() {
            System.out.println("MyThread2 run priority=" + this.getPriority());
        }
    }
    package test;
    
    import extthread.MyThread1;
    
    public class Run {
        public static void main(String[] args) {
            System.out.println("main thread begin priority="+ Thread.currentThread().getPriority());
            //Thread.currentThread().setPriority(6);
            System.out.println("main thread end   priority="+ Thread.currentThread().getPriority());
            MyThread1 thread1 = new MyThread1();
            thread1.start();
        }
    }
    main thread begin priority=5
    main thread end   priority=5
    MyThread1 run priority=5
    MyThread2 run priority=5
    

       示例2:修改main方法。修改后main线程的优先级比原始的5高,通过main线程启动1线程,然后通过1线程启动2线程,优先级都是一样的。

    package test;
    
    import extthread.MyThread1;
    
    public class Run {
        public static void main(String[] args) {
            System.out.println("main thread begin priority="+ Thread.currentThread().getPriority());
            Thread.currentThread().setPriority(6);
            System.out.println("main thread end   priority="+ Thread.currentThread().getPriority());
            MyThread1 thread1 = new MyThread1();
            thread1.start();
        }
    }
    main thread begin priority=5
    main thread end   priority=6
    MyThread1 run priority=6
    MyThread2 run priority=6
    

      2.优先级具有规则性

      示例:高优先级的线程总是大部分先执行完,但并不代表高优先级的线程全部先执行完。另外,并不是MyThread先被main线程调用就会先执行完,当优先级等级差距很大时,谁先执行完和代码的调用顺序无关。即线程的优先级与代码执行顺序无关。

    package extthread;
    
    import java.util.Random;
    
    public class MyThread1 extends Thread {
        @Override
        public void run() {
            long beginTime = System.currentTimeMillis();
            long addResult = 0;
            for (int j = 0; j < 10; j++) {
                for (int i = 0; i < 50000; i++) {
                    Random random = new Random();
                    random.nextInt();
                    addResult = addResult + i;
                }
            }
            long endTime = System.currentTimeMillis();
            System.out.println("******线程1 use time=" + (endTime - beginTime));
        }
    }
    package extthread;
    
    import java.util.Random;
    
    public class MyThread2 extends Thread {
        @Override
        public void run() {
            long beginTime = System.currentTimeMillis();
            long addResult = 0;
            for (int j = 0; j < 10; j++) {
                for (int i = 0; i < 50000; i++) {
                    Random random = new Random();
                    random.nextInt();
                    addResult = addResult + i;
                }
            }
            long endTime = System.currentTimeMillis();
            System.out.println("%%%%%%线程2 use time=" + (endTime - beginTime));
        }
    }
    package test;
    
    import extthread.MyThread1;
    import extthread.MyThread2;
    
    public class Run {
        public static void main(String[] args) {
            for (int i = 0; i < 5; i++) {
                MyThread1 thread1 = new MyThread1();
                thread1.setPriority(1);
                thread1.start();
    
                MyThread2 thread2 = new MyThread2();
                thread2.setPriority(10);
                thread2.start();
            }
        }
    }
    %%%%%%线程2 use time=239
    %%%%%%线程2 use time=256
    %%%%%%线程2 use time=311
    %%%%%%线程2 use time=312
    %%%%%%线程2 use time=319
    ******线程1 use time=173
    ******线程1 use time=211
    ******线程1 use time=212
    ******线程1 use time=180
    ******线程1 use time=477
    

      3.优先级具有随机性

      优先级较高的线程不一定每一次都先执行完。

      示例:修改2中示例中的main方法,修改优先级为5和6,从结果可以看出,不要把线程的优先级与运行结果的顺序作为衡量的标准,优先级较高的线程不一定每一次都先执行完run()方法中的任务,也就是说,线程优先级与打印顺序无关,不要讲这两者的关系相关联,它们的关系具有不确定性和随机性。

    package extthread;
    
    import java.util.Random;
    
    public class MyThread1 extends Thread {
        @Override
        public void run() {
            long beginTime = System.currentTimeMillis();
            for (int i = 0; i < 1000; i++) {
                Random random = new Random();
                random.nextInt();
            }
            long endTime = System.currentTimeMillis();
            System.out.println("**********1 use time=" + (endTime - beginTime));
        }
    }
    %%%%%%%%%%2 use time=2
    **********1 use time=6
    **********1 use time=5
    %%%%%%%%%%2 use time=0
    **********1 use time=0
    **********1 use time=1
    %%%%%%%%%%2 use time=1
    %%%%%%%%%%2 use time=0
    **********1 use time=0
    %%%%%%%%%%2 use time=1

      4.看谁运行得快

      示例:从输出结果可以看出,优先级高的线程运行得快,b的优先级为8,a的优先级为2,b运行得快得多,这里要注意的是,int类型的数一直加超过范围后会变成负数!!!

    package extthread;
    
    public class ThreadA extends Thread {
    
        private int count = 0;
    
        public int getCount() {
            return count;
        }
    
        @Override
        public void run() {
            while (true) {
                count++;
            }
        }
    
    }
    package extthread;
    
    public class ThreadB extends Thread {
    
        private int count = 0;
    
        public int getCount() {
            return count;
        }
    
        @Override
        public void run() {
            while (true) {
                count++;
            }
        }
    
    }
    package test;
    
    import extthread.ThreadA;
    import extthread.ThreadB;
    
    public class Run {
    
        public static void main(String[] args) {
    
            try {
                ThreadA a = new ThreadA();
                a.setPriority(Thread.NORM_PRIORITY - 3);
                a.start();
    
                ThreadB b = new ThreadB();
                b.setPriority(Thread.NORM_PRIORITY + 3);
                b.start();
    
                Thread.sleep(20000);
                a.stop();
                b.stop();
    
                System.out.println("a=" + a.getCount());
                System.out.println("b=" + b.getCount());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    a=1197537357
    b=-1459844607
    

      十一、守护线程

      在Java线程中有两种线程,一种是用户线程,另一种是守护线程。守护线程就是当进程中不存在非守护线程了,则守护线程自动销毁。Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是GC(垃圾回收器),它就是一个很称职的守护者。

      示例:main线程休眠5秒后,最后一个非守护线程结束,守护线程也一同结束。

    package testpackage;
    
    public class MyThread extends Thread {
        private int i = 0;
    
        @Override
        public void run() {
            try {
                while (true) {
                    i++;
                    System.out.println("i=" + (i));
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    package test.run;
    
    import testpackage.MyThread;
    
    public class Run {
        public static void main(String[] args) {
            try {
                MyThread thread = new MyThread();
                thread.setDaemon(true);
                thread.start();
                Thread.sleep(5000);
                System.out.println("我离开thread对象也不再打印了,也就是停止了!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    i=1
    i=2
    i=3
    i=4
    i=5
    我离开thread对象也不再打印了,也就是停止了!
    
  • 相关阅读:
    深入理解Node.js垃圾回收与内存管理
    【File System】Node.js中文件操作模块File System
    【事件流】事件冒泡和事件捕获
    undefined 和null的区别?
    localStorage实现登录注册功能
    解刨for循环
    react中嵌入高德地图并Marker标点
    react页面中嵌入地图,标识出某个地点,使用插件react-amap
    react中使用antd的List组件,以及下载文件,List隔行变色
    公众号页面数据处理
  • 原文地址:https://www.cnblogs.com/BigJunOba/p/8979164.html
Copyright © 2011-2022 走看看