多线程的总结
版权声明:原创博客,乐于分享 (*^__^*) https://blog.****.net/qq455276333/article/details/51917326
继承Thread类:
在Java中,自带了对多线程技术的支持。继承Thread,还有实现Runnable接口。先看一下Thread类的结果,如下:
public class Thread implements Runnable
从上面的代码中发现,Thread类实现了Runnable接口,它们之间具有多态关系。
这种方法的局限是不支持多继承,因为Java是单继承的,所以为了多继承,完全可以实现Runnable接口的方法。
一边实现一边继承,但是这两种方法创建线程在工作时性质是一样的,没有本质区别。
- public class MyThread extends Thread{
- @Override
- public void run() {
- super.run();
- System.out.println("MyThread");
- }
- }
- public class Run {
- public static void main(String[] args) {
- MyThread myThread = new MyThread();
- myThread.start();
- System.out.println("运行结束!");
- }
- }
代码的运用结果:
从图结果看,MyThread类的run方法执行较晚,这也说明在使用多线程技术时,代码的执行结果与代码顺序或代码调用顺序无关的。
如果多次调用start()方法,则会出现java.lang.IllegalThreadStateException异常
上面介绍了线程的调用随机性,下面通过例子来演示线程的随机性:
创建自定义线程类MyThread.java,代码如下:
- public class MyThread extends Thread{
- @Override
- public void run() {
- super.run();
- for(int i=0;i<10;i++){
- int time = (int)(Math.random()*1000);
- try {
- Thread.sleep(time);
- System.out.println("run="+Thread.currentThread().getName());
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
再创建运行类:
- public class Run {
- public static void main(String[] args) {
- MyThread myThread = new MyThread();
- myThread.setName("myThread");
- myThread.start();
- for(int i=0;i<10;i++){
- int time = (int)(Math.random()*1000);
- try {
- Thread.sleep(time);
- System.out.println("main="+Thread.currentThread().getName());
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
在代码中,为了展现出线程具有随机性,所以使用随机数的形式来使用线程得到挂起效果,从而表现CPU执行哪个线程具有不确定性。
Thread类的start()方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象的run()方法。这个过程其实就是让系统安排一个时间来调用Thread中的run()方法,也就是使线程得到运行,启动线程具有异步执行的效果。如果调用代码thread.run()就不是异步执行,而是同步。那么此线程对象并不交给“线程规划器”来进行处理,而是由main主线程来调用run()方法,也就是必须等run()方法中的代码执行完后才执行后面的代码。
以异步方式运行效果如下:
另外还需注意,执行start()方法的顺序并不代表线程启动的顺序。
线程类代码如下:
- public class TheadT1 extends Thread{
- private int i;
- public TheadT1(int i){
- this.i = i;
- }
- public void run() {
- System.out.println(i);
- }
- }
执行类代码:
- public class TheadT1Test {
- public static void main(String[] args) {
- TheadT1 t1 = new TheadT1(1);
- TheadT1 t2 = new TheadT1(2);
- TheadT1 t3 = new TheadT1(3);
- TheadT1 t4 = new TheadT1(4);
- TheadT1 t5 = new TheadT1(5);
- t1.start();
- t2.start();
- t3.start();
- t4.start();
- t5.start();
- }
- }
程序运用结果如下图:
实现Runnable接口:
如果欲创建的线程类已经有一个父类的话,那只能通过实现Runnable接口实现多线程功能。
创建一个实现Runnable接口的类,代码如下:
- public class MyRunnable implements Runnable{
- @Override
- public void run() {
- System.out.println("MyRunnable");
- }
- }
- public class RunnableRun {
- public static void main(String[] args) {
- MyRunnable run = new MyRunnable();
- Thread t = new Thread(run);
- t.start();
- System.out.println("运用结束!");
- }
- }
运用结果:
使用Thread类的方式来开发多线程应用程序在设计上是有局限性的,因为Java是单继承,所以为了改变这种限制,可以使用Runnbale接口的方式来实现多线程技术。
实例变量与线程安全:
自定义线程类中的实例变量针对其他线程可以有共享与不共享之分,这在多个线程之间进行交互时是很重要的一个技术点。
(1) 不共享数据的情况:
通过下面一个示例查看数据不共享情况。
示例代码如下:
- public class SharedIsNoThread extends Thread{
- private int count = 5;
- public SharedIsNoThread(String name){
- this.setName(name);
- }
- @Override
- public void run() {
- super.run();
- while(count>0){
- count--;
- System.out.println("由"+Thread.currentThread().getName()+"计算,count="+count);
- }
- }
- }
- public class SharedIsNoThreadTest {
- public static void main(String[] args) {
- SharedIsNoThread t1 = new SharedIsNoThread("A");
- SharedIsNoThread t2 = new SharedIsNoThread("B");
- SharedIsNoThread t3 = new SharedIsNoThread("C");
- t1.start();
- t2.start();
- t3.start();
- }
- }
运行结果如下图:
通过程序表明,一共创建了3个线程,每个线程都有各自的count变量,自己减少自己的count变量的值。这样的情况就是变量不共享,此示例并不存在多个线程访问同一个实例变量的情况。
(2) 共享数据的情况
共享数据的情况如下图所示:
共享数据的情况就是多个线程可以访问同一个变量,比如在实现投票功能时,多个线程可以同时处理同一个人的票数。
下面通过一个示例看一下数据共享情况:
- public class SharedThread extends Thread{
- private int count = 5;
- @Override
- public void run() {
- super.run();
- while(count>0){
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- count--;
- System.out.println("由"+Thread.currentThread().getName()+"计算,count="+count);
- }
- }
- }
- public class SharedThreadTest {
- public static void main(String[] args) {
- SharedThread sharedThread = new SharedThread();
- Thread t1 = new Thread(sharedThread, "A");
- Thread t2 = new Thread(sharedThread, "B");
- Thread t3 = new Thread(sharedThread, "C");
- t1.start();
- t2.start();
- t3.start();
- }
- }
休眠一秒,是为了情景更容易出现。
运行结果如下图:
从输出结果来看,出现了负数,产生了“非线程安全”问题。而我们想要的打印结果却是不重复的,而是依次递减并且最小等于0的。
在某些jvm中,i—的操作要分成如下3步:
(1) 取得原有i值。
(2) 计算i-1。
(3) 对i进行赋值。
而且在我们程序中还有while判断这一步。如果出现多个线程访问,那么一定会出现非线程安全问题。
其实这个示例就是典型的销售场景:3个销售员,每个销售员卖出一个货品后不可以得出相同的剩余数量,必须在每个销售员卖完一个货品后,其他销售人员才可以在新的剩余物品数上继续减1操作。这时候就需要使多个线程间进行同步,也就是按顺序的方式进行减1操作。更改代码如下:
- public class SharedThread extends Thread{
- private int count = 5;
- @Override
- synchronized public void run() {
- super.run();
- while(count>0){
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- count--;
- System.out.println("有"+Thread.currentThread().getName()+"计算,count="+count);
- }
- }
- }
重新执行程序,就不会出现负数和值一样的情况。
通过run方法前加入synchronized关键字,使多个线程执行run()方法时,以排队的方式进行。当一个线程调用run方法前,先判断run方法有没有被上锁,如上锁,说明有其他线程正在调用run方法,必须等其他线程对run方法调用结束才可以调用run方法。这样也就实现了排队调用run方法的目的,也就达到了按顺序对count变量减1的效果。synchronized关键字可以在任意对象及方法上加锁,而加锁的这段代码称为“互斥区”或“临界区”。
当一个线程想要执行同步代码块中代码时,线程首先尝试获取锁,如果能拿到这把锁,那么这个线程就能执行同步代码块中的代码。如果不能拿到这把锁,那么这个线程会不断尝试拿这把锁,直到能够拿到为止,而且是有多个线程同时争抢这把锁。流程如下图:留意i--与System.out.println()的异常:
上面介绍解决非线程安全问题使用的是synchronized关键字,这里通过程序案例细化一下println()方法与i++联合使用时“有可能”出现的另外一种异常情况,并说明其中的原因。
示例代码如下:- public class MyThread extends Thread{
- private int i = 5;
- @Override
- public void run() {
- super.run();
- System.out.println("i="+(i--)+" threadName="+Thread.currentThread().getName());
- }
- }
- public class Run {
- public static void main(String[] args) {
- MyThread thread = new MyThread();
- Thread t1 = new Thread(thread);
- Thread t2 = new Thread(thread);
- Thread t3 = new Thread(thread);
- Thread t4 = new Thread(thread);
- Thread t5 = new Thread(thread);
- t1.start();
- t2.start();
- t3.start();
- t4.start();
- t5.start();
- }
- }
程序的执行结果:
本测试的用例是:虽然println()方法内部是同步的,但是i--的操作却是在进入该方法前执行的,所以有发生非线程安全的问题的概率。
所以,为了防止发生非线程安全的问题,还是需要继续使用同步方法。
currentThread()方法
currentThread()方法可返回代码段正在被哪个线程调用的信息。下面通过一个示例进行说明。具体如下:
- public class Run {
- public static void main(String[] args) {
- System.out.print(Thread.currentThread().getName());
- }
- }
运行结果:
结果说明,main方法被名为main的线程调用。
继续实验,创建如下代码:
- public class MyThread extends Thread{
- public MyThread(){
- System.out.println("构造方法的打印:"+Thread.currentThread().getName());
- }
- @Override
- public void run() {
- System.out.println("run方法的打印:"+Thread.currentThread().getName());
- }
- }
- public class Run {
- public static void main(String[] args) {
- Thread myThread = new MyThread();
- myThread.start();
- }
- }
执行结果如下:
通过结果可以看出,MyThread类的构造方法是被main线程调用的,而run方法是被名称为Thread-0的线程调用的,run方法是自动调用的。
代码改为如下:
- public class Run {
- public static void main(String[] args) {
- Thread myThread = new MyThread();
- //myThread.start();
- myThread.run();
- }
- }
在来测试一个比较复杂的,示例代码如下:
- public class CountOperate extends Thread{
- public CountOperate(){
- System.out.println("----CountOperate begin----");
- System.out.println("CountOperate Thread.currentThread().getName:"+Thread.currentThread().getName());
- System.out.println("CountOperate this.getName:"+this.getName());
- System.out.println("----CountOperate end----");
- }
- @Override
- public void run() {
- System.out.println("----run begin----");
- System.out.println("run Thread.currentThread().getName:"+Thread.currentThread().getName());
- System.out.println("run this.getName:"+this.getName());
- System.out.println("----run end----");
- }
- }
- public class Run {
- public static void main(String[] args) {
- CountOperate c = new CountOperate();
- Thread t1 = new Thread(c,"A");
- System.out.println(t1);
- t1.start();
- }
- }
getName()和Thread.currentThread().getName()返回的值不同,你可以修改代码如下:
- public class CountOperate extends Thread{
- public CountOperate(String name){
- super(name);
- System.out.println("----CountOperate begin----");
- System.out.println("CountOperate Thread.currentThread().getName:"+Thread.currentThread().getName());
- System.out.println("CountOperate this.getName:"+this.getName());
- System.out.println("----CountOperate end----");
- }
- public class RunCountOperate {
- public static void main(String[] args) {
- CountOperate countOperate = new CountOperate("M");
- countOperate.setName("N");
- Thread t1 = new Thread(countOperate);
- t1.setName("A");
- t1.start();
- }
- }
执行结果如下:
通过这个结果可以看出:this.getName()方法是指当前对象的线程名称,而Thread.currentThread.getName()方法表示正在运行的线程的名称。
isAlive()方法:
isAlive()方法的功能是判断当前线程是否处于活动状态。
创建示例代码,如下:
- public class MyThread extends Thread{
- @Override
- public void run() {
- System.out.println("---run:"+this.isAlive()+"---");
- }
- }
- public class Run {
- public static void main(String[] args) {
- MyThread m = new MyThread();
- System.out.println("begin="+m.isAlive());
- m.start();
- System.out.println("end="+m.isAlive());
- }
- }
程序运行结果如下:
方法isAlive()的作用是测试线程是否处于活动状态。什么是活动状态?活动状态就是线程已经启动且尚未终止。线程处于正在运行或准备开始的状态,就认为线程是存活的。
需要说明,如下代码:
- System.out.println("end="+m.isAlive());
虽然上面示例打印的结果是true,但此值是不确定的。打印true是因为MyThread线程还没有执行完毕,所以输出true。如果代码改为如下:
- public class Run {
- public static void main(String[] args) throws InterruptedException {
- MyThread m = new MyThread();
- System.out.println("begin="+m.isAlive());
- m.start();
- //线程休息一秒
- Thread.sleep(1000);
- System.out.println("end="+m.isAlive());
- }
- }
执行结果:
上述代码输出为false,因为MyThread线程在1秒钟内执行完毕了。
另外,在使用isAlive()方式时,如果将线程对象以构造参数的形式传递给Thread对象进行start()启动时,运行的结果和前面是有差异的。造成这样的差异原因是Thread.currentThread()和this的区别。下面通过代码测试一下:
- public class CountOperate extends Thread{
- public CountOperate(String name){
- super(name);
- System.out.println("----CountOperate begin----");
- System.out.println("CountOperate Thread.currentThread().getName:"+Thread.currentThread().getName());
- System.out.println("CountOperate Thread.currentThread().isAlive:"+Thread.currentThread().isAlive());
- System.out.println("CountOperate this.getName:"+this.getName());
- System.out.println("CountOperate this.getName:"+this.isAlive());
- System.out.println("----CountOperate end----");
- }
- @Override
- public void run() {
- System.out.println("----run begin----");
- System.out.println("run Thread.currentThread().getName:"+Thread.currentThread().getName());
- System.out.println("run Thread.currentThread().isAlive:"+Thread.currentThread().isAlive());
- System.out.println("run this.getName:"+this.getName());
- System.out.println("run this.isAlive:"+this.isAlive());
- System.out.println("----run end----");
- }
- }
- public class RunCountOperate {
- public static void main(String[] args) {
- CountOperate countOperate = new CountOperate("M");
- countOperate.setName("N");
- Thread t1 = new Thread(countOperate);
- System.out.println("main begin t1 isAlive="+t1.isAlive());
- t1.setName("A");
- t1.start();
- System.out.println("main end t1 isAlive="+t1.isAlive());
- }
- }
执行结果如下:
sleep()方法
方法sleep()的作用是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)。这个“正在执行的线程”是指this.currentThread()返回的线程。
通过一个示例说明,如下:
- public class MyThread1 extends Thread{
- @Override
- public void run() {
- System.out.println("run threadName="+this.currentThread().getName()+" begin");
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("run threadName="+this.currentThread().getName()+" end");
- }
- }
- public class Run1 {
- public static void main(String[] args) {
- MyThread1 t1 = new MyThread1();
- System.out.println("begin="+System.currentTimeMillis());
- t1.run();
- System.out.println("end="+System.currentTimeMillis());
- }
- }
运行代码结果如下:
继续修改代码,如下:
- public class MyThread2 extends Thread{
- @Override
- public void run() {
- System.out.println("run threadName="+this.currentThread().getName()+" begin="+System.currentTimeMillis());
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("run threadName="+this.currentThread().getName()+" end="+System.currentTimeMillis());
- }
- }
- public class Run2 {
- public static void main(String[] args) {
- MyThread2 t2 = new MyThread2();
- System.out.println("begin="+System.currentTimeMillis());
- t2.start();
- System.out.println("end="+System.currentTimeMillis());
- }
- }
运行结果如下:
由于main线程与Thread2线程是异步执行的,所以首先打印的信息begin和end。而MyThread2稍后执行,在最后两行打印run begin 和 run end相关的信息。
getId()方法
getId()方法的作用是取得线程的唯一标识。、
创建示例代码如下:
- public class Run1 {
- public static void main(String[] args) {
- Thread thread = Thread.currentThread();
- System.out.println(thread.getName()+" "+thread.getId());
- }
- }
程序运行结果如下:
继承Thread类:
在Java中,自带了对多线程技术的支持。继承Thread,还有实现Runnable接口。先看一下Thread类的结果,如下:
public class Thread implements Runnable
从上面的代码中发现,Thread类实现了Runnable接口,它们之间具有多态关系。
这种方法的局限是不支持多继承,因为Java是单继承的,所以为了多继承,完全可以实现Runnable接口的方法。
一边实现一边继承,但是这两种方法创建线程在工作时性质是一样的,没有本质区别。
- public class MyThread extends Thread{
- @Override
- public void run() {
- super.run();
- System.out.println("MyThread");
- }
- }
- public class Run {
- public static void main(String[] args) {
- MyThread myThread = new MyThread();
- myThread.start();
- System.out.println("运行结束!");
- }
- }
代码的运用结果:
从图结果看,MyThread类的run方法执行较晚,这也说明在使用多线程技术时,代码的执行结果与代码顺序或代码调用顺序无关的。
如果多次调用start()方法,则会出现java.lang.IllegalThreadStateException异常
上面介绍了线程的调用随机性,下面通过例子来演示线程的随机性:
创建自定义线程类MyThread.java,代码如下:
- public class MyThread extends Thread{
- @Override
- public void run() {
- super.run();
- for(int i=0;i<10;i++){
- int time = (int)(Math.random()*1000);
- try {
- Thread.sleep(time);
- System.out.println("run="+Thread.currentThread().getName());
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
再创建运行类:
- public class Run {
- public static void main(String[] args) {
- MyThread myThread = new MyThread();
- myThread.setName("myThread");
- myThread.start();
- for(int i=0;i<10;i++){
- int time = (int)(Math.random()*1000);
- try {
- Thread.sleep(time);
- System.out.println("main="+Thread.currentThread().getName());
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
在代码中,为了展现出线程具有随机性,所以使用随机数的形式来使用线程得到挂起效果,从而表现CPU执行哪个线程具有不确定性。
Thread类的start()方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象的run()方法。这个过程其实就是让系统安排一个时间来调用Thread中的run()方法,也就是使线程得到运行,启动线程具有异步执行的效果。如果调用代码thread.run()就不是异步执行,而是同步。那么此线程对象并不交给“线程规划器”来进行处理,而是由main主线程来调用run()方法,也就是必须等run()方法中的代码执行完后才执行后面的代码。
以异步方式运行效果如下:
另外还需注意,执行start()方法的顺序并不代表线程启动的顺序。
线程类代码如下:
- public class TheadT1 extends Thread{
- private int i;
- public TheadT1(int i){
- this.i = i;
- }
- public void run() {
- System.out.println(i);
- }
- }
执行类代码:
- public class TheadT1Test {
- public static void main(String[] args) {
- TheadT1 t1 = new TheadT1(1);
- TheadT1 t2 = new TheadT1(2);
- TheadT1 t3 = new TheadT1(3);
- TheadT1 t4 = new TheadT1(4);
- TheadT1 t5 = new TheadT1(5);
- t1.start();
- t2.start();
- t3.start();
- t4.start();
- t5.start();
- }
- }
程序运用结果如下图:
实现Runnable接口:
如果欲创建的线程类已经有一个父类的话,那只能通过实现Runnable接口实现多线程功能。
创建一个实现Runnable接口的类,代码如下:
- public class MyRunnable implements Runnable{
- @Override
- public void run() {
- System.out.println("MyRunnable");
- }
- }
- public class RunnableRun {
- public static void main(String[] args) {
- MyRunnable run = new MyRunnable();
- Thread t = new Thread(run);
- t.start();
- System.out.println("运用结束!");
- }
- }
运用结果:
使用Thread类的方式来开发多线程应用程序在设计上是有局限性的,因为Java是单继承,所以为了改变这种限制,可以使用Runnbale接口的方式来实现多线程技术。
实例变量与线程安全:
自定义线程类中的实例变量针对其他线程可以有共享与不共享之分,这在多个线程之间进行交互时是很重要的一个技术点。
(1) 不共享数据的情况:
通过下面一个示例查看数据不共享情况。
示例代码如下:
- public class SharedIsNoThread extends Thread{
- private int count = 5;
- public SharedIsNoThread(String name){
- this.setName(name);
- }
- @Override
- public void run() {
- super.run();
- while(count>0){
- count--;
- System.out.println("由"+Thread.currentThread().getName()+"计算,count="+count);
- }
- }
- }
- public class SharedIsNoThreadTest {
- public static void main(String[] args) {
- SharedIsNoThread t1 = new SharedIsNoThread("A");
- SharedIsNoThread t2 = new SharedIsNoThread("B");
- SharedIsNoThread t3 = new SharedIsNoThread("C");
- t1.start();
- t2.start();
- t3.start();
- }
- }
运行结果如下图:
通过程序表明,一共创建了3个线程,每个线程都有各自的count变量,自己减少自己的count变量的值。这样的情况就是变量不共享,此示例并不存在多个线程访问同一个实例变量的情况。
(2) 共享数据的情况
共享数据的情况如下图所示:
共享数据的情况就是多个线程可以访问同一个变量,比如在实现投票功能时,多个线程可以同时处理同一个人的票数。
下面通过一个示例看一下数据共享情况:
- public class SharedThread extends Thread{
- private int count = 5;
- @Override
- public void run() {
- super.run();
- while(count>0){
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- count--;
- System.out.println("由"+Thread.currentThread().getName()+"计算,count="+count);
- }
- }
- }
- public class SharedThreadTest {
- public static void main(String[] args) {
- SharedThread sharedThread = new SharedThread();
- Thread t1 = new Thread(sharedThread, "A");
- Thread t2 = new Thread(sharedThread, "B");
- Thread t3 = new Thread(sharedThread, "C");
- t1.start();
- t2.start();
- t3.start();
- }
- }
休眠一秒,是为了情景更容易出现。
运行结果如下图:
从输出结果来看,出现了负数,产生了“非线程安全”问题。而我们想要的打印结果却是不重复的,而是依次递减并且最小等于0的。
在某些jvm中,i—的操作要分成如下3步:
(1) 取得原有i值。
(2) 计算i-1。
(3) 对i进行赋值。
而且在我们程序中还有while判断这一步。如果出现多个线程访问,那么一定会出现非线程安全问题。
其实这个示例就是典型的销售场景:3个销售员,每个销售员卖出一个货品后不可以得出相同的剩余数量,必须在每个销售员卖完一个货品后,其他销售人员才可以在新的剩余物品数上继续减1操作。这时候就需要使多个线程间进行同步,也就是按顺序的方式进行减1操作。更改代码如下:
- public class SharedThread extends Thread{
- private int count = 5;
- @Override
- synchronized public void run() {
- super.run();
- while(count>0){
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- count--;
- System.out.println("有"+Thread.currentThread().getName()+"计算,count="+count);
- }
- }
- }
重新执行程序,就不会出现负数和值一样的情况。
通过run方法前加入synchronized关键字,使多个线程执行run()方法时,以排队的方式进行。当一个线程调用run方法前,先判断run方法有没有被上锁,如上锁,说明有其他线程正在调用run方法,必须等其他线程对run方法调用结束才可以调用run方法。这样也就实现了排队调用run方法的目的,也就达到了按顺序对count变量减1的效果。synchronized关键字可以在任意对象及方法上加锁,而加锁的这段代码称为“互斥区”或“临界区”。
当一个线程想要执行同步代码块中代码时,线程首先尝试获取锁,如果能拿到这把锁,那么这个线程就能执行同步代码块中的代码。如果不能拿到这把锁,那么这个线程会不断尝试拿这把锁,直到能够拿到为止,而且是有多个线程同时争抢这把锁。流程如下图:留意i--与System.out.println()的异常:
上面介绍解决非线程安全问题使用的是synchronized关键字,这里通过程序案例细化一下println()方法与i++联合使用时“有可能”出现的另外一种异常情况,并说明其中的原因。
示例代码如下:- public class MyThread extends Thread{
- private int i = 5;
- @Override
- public void run() {
- super.run();
- System.out.println("i="+(i--)+" threadName="+Thread.currentThread().getName());
- }
- }
- public class Run {
- public static void main(String[] args) {
- MyThread thread = new MyThread();
- Thread t1 = new Thread(thread);
- Thread t2 = new Thread(thread);
- Thread t3 = new Thread(thread);
- Thread t4 = new Thread(thread);
- Thread t5 = new Thread(thread);
- t1.start();
- t2.start();
- t3.start();
- t4.start();
- t5.start();
- }
- }
程序的执行结果:
本测试的用例是:虽然println()方法内部是同步的,但是i--的操作却是在进入该方法前执行的,所以有发生非线程安全的问题的概率。
所以,为了防止发生非线程安全的问题,还是需要继续使用同步方法。
currentThread()方法
currentThread()方法可返回代码段正在被哪个线程调用的信息。下面通过一个示例进行说明。具体如下:
- public class Run {
- public static void main(String[] args) {
- System.out.print(Thread.currentThread().getName());
- }
- }
运行结果:
结果说明,main方法被名为main的线程调用。
继续实验,创建如下代码:
- public class MyThread extends Thread{
- public MyThread(){
- System.out.println("构造方法的打印:"+Thread.currentThread().getName());
- }
- @Override
- public void run() {
- System.out.println("run方法的打印:"+Thread.currentThread().getName());
- }
- }
- public class Run {
- public static void main(String[] args) {
- Thread myThread = new MyThread();
- myThread.start();
- }
- }
执行结果如下:
通过结果可以看出,MyThread类的构造方法是被main线程调用的,而run方法是被名称为Thread-0的线程调用的,run方法是自动调用的。
代码改为如下:
- public class Run {
- public static void main(String[] args) {
- Thread myThread = new MyThread();
- //myThread.start();
- myThread.run();
- }
- }
在来测试一个比较复杂的,示例代码如下:
- public class CountOperate extends Thread{
- public CountOperate(){
- System.out.println("----CountOperate begin----");
- System.out.println("CountOperate Thread.currentThread().getName:"+Thread.currentThread().getName());
- System.out.println("CountOperate this.getName:"+this.getName());
- System.out.println("----CountOperate end----");
- }
- @Override
- public void run() {
- System.out.println("----run begin----");
- System.out.println("run Thread.currentThread().getName:"+Thread.currentThread().getName());
- System.out.println("run this.getName:"+this.getName());
- System.out.println("----run end----");
- }
- }
- public class Run {
- public static void main(String[] args) {
- CountOperate c = new CountOperate();
- Thread t1 = new Thread(c,"A");
- System.out.println(t1);
- t1.start();
- }
- }
getName()和Thread.currentThread().getName()返回的值不同,你可以修改代码如下:
- public class CountOperate extends Thread{
- public CountOperate(String name){
- super(name);
- System.out.println("----CountOperate begin----");
- System.out.println("CountOperate Thread.currentThread().getName:"+Thread.currentThread().getName());
- System.out.println("CountOperate this.getName:"+this.getName());
- System.out.println("----CountOperate end----");
- }
- public class RunCountOperate {
- public static void main(String[] args) {
- CountOperate countOperate = new CountOperate("M");
- countOperate.setName("N");
- Thread t1 = new Thread(countOperate);
- t1.setName("A");
- t1.start();
- }
- }
执行结果如下:
通过这个结果可以看出:this.getName()方法是指当前对象的线程名称,而Thread.currentThread.getName()方法表示正在运行的线程的名称。
isAlive()方法:
isAlive()方法的功能是判断当前线程是否处于活动状态。
创建示例代码,如下:
- public class MyThread extends Thread{
- @Override
- public void run() {
- System.out.println("---run:"+this.isAlive()+"---");
- }
- }
- public class Run {
- public static void main(String[] args) {
- MyThread m = new MyThread();
- System.out.println("begin="+m.isAlive());
- m.start();
- System.out.println("end="+m.isAlive());
- }
- }
程序运行结果如下:
方法isAlive()的作用是测试线程是否处于活动状态。什么是活动状态?活动状态就是线程已经启动且尚未终止。线程处于正在运行或准备开始的状态,就认为线程是存活的。
需要说明,如下代码:
- System.out.println("end="+m.isAlive());
虽然上面示例打印的结果是true,但此值是不确定的。打印true是因为MyThread线程还没有执行完毕,所以输出true。如果代码改为如下:
- public class Run {
- public static void main(String[] args) throws InterruptedException {
- MyThread m = new MyThread();
- System.out.println("begin="+m.isAlive());
- m.start();
- //线程休息一秒
- Thread.sleep(1000);
- System.out.println("end="+m.isAlive());
- }
- }
执行结果:
上述代码输出为false,因为MyThread线程在1秒钟内执行完毕了。
另外,在使用isAlive()方式时,如果将线程对象以构造参数的形式传递给Thread对象进行start()启动时,运行的结果和前面是有差异的。造成这样的差异原因是Thread.currentThread()和this的区别。下面通过代码测试一下:
- public class CountOperate extends Thread{
- public CountOperate(String name){
- super(name);
- System.out.println("----CountOperate begin----");
- System.out.println("CountOperate Thread.currentThread().getName:"+Thread.currentThread().getName());
- System.out.println("CountOperate Thread.currentThread().isAlive:"+Thread.currentThread().isAlive());
- System.out.println("CountOperate this.getName:"+this.getName());
- System.out.println("CountOperate this.getName:"+this.isAlive());
- System.out.println("----CountOperate end----");
- }
- @Override
- public void run() {
- System.out.println("----run begin----");
- System.out.println("run Thread.currentThread().getName:"+Thread.currentThread().getName());
- System.out.println("run Thread.currentThread().isAlive:"+Thread.currentThread().isAlive());
- System.out.println("run this.getName:"+this.getName());
- System.out.println("run this.isAlive:"+this.isAlive());
- System.out.println("----run end----");
- }
- }
- public class RunCountOperate {
- public static void main(String[] args) {
- CountOperate countOperate = new CountOperate("M");
- countOperate.setName("N");
- Thread t1 = new Thread(countOperate);
- System.out.println("main begin t1 isAlive="+t1.isAlive());
- t1.setName("A");
- t1.start();
- System.out.println("main end t1 isAlive="+t1.isAlive());
- }
- }
执行结果如下:
sleep()方法
方法sleep()的作用是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)。这个“正在执行的线程”是指this.currentThread()返回的线程。
通过一个示例说明,如下:
- public class MyThread1 extends Thread{
- @Override
- public void run() {
- System.out.println("run threadName="+this.currentThread().getName()+" begin");
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("run threadName="+this.currentThread().getName()+" end");
- }
- }
- public class Run1 {
- public static void main(String[] args) {
- MyThread1 t1 = new MyThread1();
- System.out.println("begin="+System.currentTimeMillis());
- t1.run();
- System.out.println("end="+System.currentTimeMillis());
- }
- }
运行代码结果如下:
继续修改代码,如下:
- public class MyThread2 extends Thread{
- @Override
- public void run() {
- System.out.println("run threadName="+this.currentThread().getName()+" begin="+System.currentTimeMillis());
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("run threadName="+this.currentThread().getName()+" end="+System.currentTimeMillis());
- }
- }
- public class Run2 {
- public static void main(String[] args) {
- MyThread2 t2 = new MyThread2();
- System.out.println("begin="+System.currentTimeMillis());
- t2.start();
- System.out.println("end="+System.currentTimeMillis());
- }
- }
运行结果如下:
由于main线程与Thread2线程是异步执行的,所以首先打印的信息begin和end。而MyThread2稍后执行,在最后两行打印run begin 和 run end相关的信息。
getId()方法
getId()方法的作用是取得线程的唯一标识。、
创建示例代码如下:
- public class Run1 {
- public static void main(String[] args) {
- Thread thread = Thread.currentThread();
- System.out.println(thread.getName()+" "+thread.getId());
- }
- }
程序运行结果如下: