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对象也不再打印了,也就是停止了!
原文地址:https://www.cnblogs.com/BigJunOba/p/8979164.html