多线程编程
Posted 余磊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程编程相关的知识,希望对你有一定的参考价值。
一、线程
线程是操作系统的概念,线程也称为轻量级进程,是CPU的基本使用单元,他的轻量级名称适合进程相关的。线程由线程ID、程序计数器、寄存器和堆栈组成,多个线程可以共享代码段和诸如打开的文件等系统资源。而传统的进程其实就是单线程控制程序,每个进程都有自己的代码段、数据段和其他系统资源。这无疑是的每个进程管理更多的内容,从而成为重量级进程。“轻量级”是指线程没有独自的存储空间,和同一个进程的多个线程共享存储空间。
线程是程序中一个单一的顺序控制流程发。在单个程序中同时运行多个程序万成都通的工作,称为多线程。
1、引入线程的好处如下:
(1)创建一个新线程花费的时间少。
(2)两个线程(在同一个进程中的)的切换时间少
(3)由于同一个进程内的线程共享内存和文件,所以线程之间相互通信不必调用内核。
多线程和传统的单线程在程序设计上的最大区别是每个线程独自运行,是彼此独立的指令流,造成线程之间的执行时乱序的,所以线程的控制需要谨慎对待。
2、创建线程
java的多线程机制提供了两种方式实现多线程,一种是通过继承java.long.Thread类实现,一种是通过实现Runnable接口实现
(1)通过Thread类实现(通过继承Thread类实现多线程的类,既继承了run()函数,也继承了start()函数)
1 public class ParamterE1 extends Thread { 2 public void run(){ 3 4 for (int j = 0; j < 100; j++) { 5 System.out.print(j+" "); 6 } 7 } 8 } 9 10 11 public class javaTest { 12 13 public static void main(String[] args) { 14 Thread t1=new ParamterE1(); 15 ParamterE1 t2=new ParamterE1(); 16 17 t1.start(); 18 t2.start(); 19 20 } 21 }
运行结果:
1 0 0 1 1 2 3 4 5 6 7 8 9 2 10 3 11 4 12 5 13 6 7 8 9 10 11 14 12 15 13 16 17 14 15 16 17 18 19 20 21 18 19 20 21 22 23 24 25 26 27 28 29 30 22 23 24 25 26 27 28 29 30 31 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 32 33 34 35 36 48 37 49 38 50 39 51 40 52 41 53 42 54 43 55 44 56 45 57 46 58 47 59 48 60 49 50 51 52 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
1 public class javaTest { 2 3 public static void main(String[] args) { 4 5 //获取当前线程的引用 6 Thread t=Thread.currentThread(); 7 System.out.println("主线程:"+t); 8 //改变线程的新信息 9 t.setName("My Thread"); 10 System.out.println("改变名称之后:"+t); 11 12 Thread t1=new Thread("小帅哥"); 13 Thread t2=new Thread(); 14 System.out.println(t1+" "+t2); 15 16 } 17 }
运行结果:
1 主线程:Thread[main,5,main] 2 改变名称之后:Thread[My Thread,5,main] 3 Thread[小帅哥,5,main] Thread[Thread-0,5,main]
注:在通过继承Thread类继承写多线程程序时,需要用到两个函数run()和start()。
1)程序必须覆盖run()方法,把需要多线程执行的代码放在函数体内,如果没有覆盖该方法,程序可以顺利通过编译,但是调用的即使调用了start()函数,因为没有覆盖run()函数,程序无法找到多线程执行的代码,所以实际上相当于单线程程序。
2)程序覆盖了run()函数,函数体中的代码可以通过多线程执行了,但是需要调用start()方法。该方法会初始化一些和多线程有关的资源,而后会自动调用run()方法。
(2)实现Runnable接口创建线程(实现Runnable接口的类继承了其run()函数,而没有继承start()函数(Runnable接口中只有唯一一个run()函数))
java提供了另一个有用的接口实现多线程编程。因为java不支持多继承,所以如果用户的类已经继承了一个类,而又需要多线程机制的支持,此时继承Tread类就不现实了。所以Runnable接口在这种情况下就很实用。Runnable接口有唯一的方法run(),所以事先该接口时必须自己定义该方法,提供多线程需要执行的代码。如果运行通过实现Runnable接口的多线程程序,则需要借助Thread类,因为Runnable接口没有提供任何东西支持多线程,必须借助Tread类的框架实现多线程,即通过类Thread的构造函数public Thread(Runnable target)来实现。
1 public class ParamterE1 implements Runnable { 2 //重写run函数 3 public void run(){ 4 5 for (int j = 0; j < 100; j++) { 6 System.out.print(j+" "); 7 } 8 } 9 } 10 11 12 public class javaTest { 13 14 public static void main(String[] args) { 15 16 ParamterE1 p=new ParamterE1(); 17 //通过Thread实现多线程操作 18 Thread t=new Thread(p); 19 20 ParamterE1 p1=new ParamterE1(); 21 //通过Thread实现多线程操作 22 Thread t1=new Thread(p1); 23 24 t.start(); 25 t1.start(); 26 27 } 28 }
运行结果:
1 0 0 1 1 2 3 4 5 6 7 8 9 10 11 2 12 3 13 4 14 5 15 6 7 8 9 10 11 12 13 14 15 16 16 17 17 18 18 19 19 20 20 21 21 22 22 23 23 24 24 25 25 26 26 27 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 28 42 29 43 30 44 31 45 32 46 33 47 34 48 35 49 36 50 37 51 38 52 39 53 40 54 41 55 42 56 43 57 44 58 45 59 46 60 47 61 48 62 49 63 50 64 51 65 52 66 53 67 54 68 55 69 56 70 57 71 58 72 59 73 60 74 61 75 62 76 63 77 64 78 65 79 66 80 67 81 68 82 69 83 70 84 71 85 72 86 73 87 74 88 75 89 76 90 77 91 78 92 79 93 80 94 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 95 96 97 98 99
3、线程的状态
java中线程执行过程比较复杂,但是线程对象创建后并不是立即执行,需要做些准备工作才有执行的权利,而一旦抢占到CPU周期,则线程可以运行,但CPU周期结束则线程必须暂时停止,后线程执行过程中的某个条件无法满足时会暂时停止,只有等待条件满足时才会继续执行,最后从run()方法返回后,线程退出。可以看出线程的执行过程涉及一些状态,线程就在这些状态之间迁移。
线程的状态包括新线程、可运行态、非运行态和死亡态。
(1)新线程态(new Thread)
产生一个Thread对象就生成一个新线程。当线程处于新线程态时,仅仅是一个空线程对象,它没有分配到系统资源。因此只能启动或终止它。任何其他操作都会引发异常。
(2)可运行态(Runnable)
start()方法产生运行线程所必需的资源,调度线程执行,并且调用线程的run()方法。
(3)非运行态(Not Runnable)
当suspend()方法、sleep()方法被调用,线程使用wait()来等待条件变量和线程处于I/O等待时间发生时,线程进入非运行态。
(4)死亡态(Dead)
当run()方法返回,或别的线程调用stop()方法,线程进入死亡态,线程进入死亡态。通常Applet使用它的stop()方法来终止它产生的所有线程。
在多线程程序中,由于对CPU周期的抢占性,由于各种原因导致线程处于阻塞状态。而导致线程阻塞的原因是多样的,如线程调用sleep(milliseconds)方法使得线程进入休眠状态时会导致阻塞发生,调用wait()方法挂起线程和等待程序的某个输出/输入时会导致阻塞发生。而导致线程死亡的唯一因素就是线程执行完run()方法返回。
4、线程的优先级
理论上,优先级高的线程比游戏那几低的线程获得更多的CPU 时间。实际上,获得CPU时间长短与喝多因素有关,不能仅仅靠优先级来判断。
可以通过Thread类的setPriority()方法来设置线程的优先级,该方法的参数为int型,qishijava提供三个优先级别,都为Thread类的常量,从高到低依次为Thread.MAX-PRIOPITY、Thread.NORM_PRIOPRITY、Thread.MIN_PRIORITY。优先级低的不意味着线程得不到执行,而是线程被优先执行的概率小。这也说明设置程序优先级不会造成死锁的产生。
1 public class ParamterE1 implements Runnable { 2 public void run(){ 3 4 for (int j = 0; j < 100; j++) { 5 System.out.print(j+" "); 6 } 7 } 8 9 10 public class javaTest { 11 12 public static void main(String[] args) { 13 14 ParamterE1 p=new ParamterE1(); 15 Thread t=new Thread(p); 16 t.setPriority(1); 17 18 ParamterE1 p1=new ParamterE1(); 19 Thread t1=new Thread(p1); 20 t1.setPriority(2); 21 22 t.start(); 23 t1.start(); 24 25 } 26 }
运行结果:
1 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 16 17 18 19 20 21 52 53 54 55 56 57 58 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 59 60 61 62 63 63 64 65 66 67 68 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
分析结果易知道:优先级低的不意味着线程得不到执行,而是线程被优先执行的概率小。这也说明设置程序优先级不会造成死锁的产生。
5、线程的同步
所谓同步,就是在发出一个功能调用时,在没得到结果之前,该调用就不返回,同时其他线程也不能调用这个方法。
而在多线程编程中经常遇到访问共享资源的问题,这些资源可以使数据、文件、一块内存区域或是外围设备的访问等。所以必须解决多线程编程中如何实现资源的共享问题,在java中称为线程的同步问题,在多数的编程语言中解决共享资源冲突的方法是采用顺序机制(Serialize),通过为共享资源枷锁的方法实现资源的顺序访问。
java语言提供两种基本的同步用法:同步方法和同步语句。
(1)同步方法(把修改数据的方法用关键字synchronized来修饰)一个方法使用关键字synchronized修饰后,当一个线程A使用这个方法时,其他线程想使用这个方法就必须等待,知道A使用完改方法。
1 public class ParamterE1 implements Runnable { 2 public void run(){ 3 4 fun(); 5 } 6 7 public synchronized void fun(){ 8 for (int i = 0; i < 50; i++) { 9 System.out.print(i+" "); 10 } 11 12 } 13 } 14 15 public class javaTest { 16 17 public static void main(String[] args) { 18 19 ParamterE1 p=new ParamterE1(); 20 Thread t=new Thread(p); 21 22 Thread t1=new Thread(p); 23 24 25 t.start(); 26 t1.start(); 27 } 28 }
运行结果:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
上述代码已经说明了同步锁的存在,下面一段代码之比上面的代码多了句话,执行结果,完全体现不了同步效果,认真思考后~
1 public class ParamterE1 implements Runnable { 2 public void run(){ 3 4 fun(); 5 } 6 7 public synchronized void fun(){ 8 for (int i = 0; i < 50; i++) { 9 System.out.print(i+" "); 10 } 11 12 } 13 } 14 15 public class javaTest { 16 17 public static void main(String[] args) { 18 19 ParamterE1 p=new ParamterE1(); 20 Thread t=new Thread(p); 21 22 ParamterE1 p1=new ParamterE1(); 23 Thread t1=new Thread(p1); 24 25 26 t.start(); 27 t1.start(); 28 } 29 }
运行结果:
1 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
这段代码中实例化两个ParamterE1对象,p1与p是两个的对象,由p与p1对象创建的线程,其实已经不属于一个进程了。所以synchronized锁没用。
(2)同步语句(与同步方法不同,同步语句必须指定提供固定锁的对象)。
1 public class ParamterE1 implements Runnable { 2 public void run(){ 3 4 fun(); 5 } 6 7 public void fun(){ 8 synchronized(this){ 9 for (int i = 0; i < 50; i++) { 10 System.out.print(i+" "); 11 } 12 } 13 } 14 } 15 16 public class javaTest { 17 18 public static void main(String[] args) { 19 20 ParamterE1 p=new ParamterE1(); 21 Thread t=new Thread(p); 22 23 Thread t1=new Thread(p); 24 25 t.start(); 26 t1.start(); 27 } 28 }
运行结果:
1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
自己定义一个锁对象:
1 public class ParamterE1 implements Runnable { 2 Object mutex = new Object(); 3 public void run(){ 4 5 fun(); 6 } 7 8 public void fun(){ 9 synchronized (mutex) { 10 for (int i = 100; i < 150; i++) { 11 System.out.print(i+" "); 12 } 13 System.out.println(); 14 } 15 16 } 17 } 18 19 public class javaTest { 20 21 public static void main(String[] args) { 22 23 ParamterE1 p=new ParamterE1(); 24 Thread t=new Thread(p); 25 26 Thread t1=new Thread(p); 27 28 t.start(); 29 t1.start(); 30 } 31 }
运行结果:
1 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 2 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
下面代码和上面的代码有细微的差别,确无法做到同步~
1 public class ParamterE1 implements Runnable { 2 public void run(){ 3 4 fun(); 5 } 6 7 public void fun(){ 8 Object mutex = new Object(); 9 synchronized (mutex) { 10 for (int i = 100; i < 150; i++) { 11 System.out.print(i+" "); 12 } 13 System.out.println(); 14 } 15 16 } 17 } 18 19 20 public class javaTest { 21 22 public static void main(String[] args) { 23 24 ParamterE1 p=new ParamterE1(); 25 Thread t=new Thread(p); 26 27 Thread t1=new Thread(p); 28 29 t.start(); 30 t1.start(); 31 } 32 }
运行结果:
1 100 100 101 101 102 102 103 103 104 104 105 105 106 106 107 107 108 108 109 109 110 110 111 112 113 114 115 116 117 118 119 120 121 122 123 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 2 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
分析:两个自定义的同步锁对象位置不同,第一个在函数调用时只初始化了一次锁对象,而第二个在两次函数调用时分别初始化了一次锁对象(共两次),这两个对象并不是同一个对象了~所以做不到同步,这个相当两个不同的同步锁,在交替着执行(同一固定锁内部仍然同步)。类似于下面代码效果:
1 public class ParamterE1 implements Runnable { 2 Object mutex = new Object(); 3 public void run(){ 4 5 fun(); 6 } 7 8 public void fun(){ 9 synchronized(this){ 10 for (int i = 0; i < 50; i++) { 11 System.out.print(i+" "); 12 } 13 System.out.println(); 14 } 15 16 synchronized (mutex) { 17 for (int i = 100; i < 150; i++) { 18 System.out.print(i+" "); 19 } 20 System.out.println(); 21 } 22 23 } 24 } 25 26 27 public class javaTest { 28 29 public static void main(String[] args) { 30 31 ParamterE1 p=new ParamterE1(); 32 Thread t=new Thread(p); 33 34 Thread t1=new Thread(p); 35 36 t.start(); 37 t1.start(); 38 } 39 }
运行结果(两次)
1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 2 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 4 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 2 100 0 101 102 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 3 44 45 46 47 48 49 4 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
当同一固定锁,固定两个不同的代码块的情况
1 public class ParamterE1 implements Runnable { 2 3 public void run(){ 4 5 fun(); 6 } 7 8 public void fun(){ 9 synchronized(this){ 10 for (int i = 0; i < 50; i++) { 11 System.out.print(i+" "); 12 } 13 System.out.println(); 14 } 15 16 synchronized (this) { 17 for (int i = 100; i < 150; i++) { 18 System.out.print(i+" "); 19 } 20 System.out.println(); 21 } 22 23 } 24 } 25 26 public class javaTest { 27 28 public static void main(String[] args) { 29 30 ParamterE1 p=new ParamterE1(); 31 Thread t=new Thread(p); 32 33 Thread t1=new Thread(p); 34 35 t.start(); 36 t1.start(); 37 } 38 }
运行结果(多次运行两种不同情况):
1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 2 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 4 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 3 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 4 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
6、线程的控制
(1)启动线程
无论是通过继承Thread类实现多线程还是实现Runnable接口实现多线程,如果要启动都需要Thread类的start()方法,该方法完成线程执行的一些初始化工作。
(2)挂起和回复线程
在java2之前,用户看到suspend()和resume()用来阻塞和唤醒线程,但是在java2中这两个方法不再使用。首先分析一下使用suspend()方法会发生什么问题。suspend()方法的作用是挂起拥有锁的线程,但是与wait()不同,他不会释放锁。如果一线程调用suspend()方法,把另一个线程挂起,此时被挂起的线程在等待回复,而挂起它线程在等待获得所(该锁就是被挂起的线程对象),此时就会发生死锁。
(3)线程休眠方法
java提供一种控制线程的方法sleep(intmilisseconds),这里称为线程的休眠,他将线程停止一段时间。
1 public class ParamterE1 implements Runnable { 2 3 public void run(){ 4 5 try { 6 fun(); 7 } catch (InterruptedException e) { 8 // TODO Auto-generated catch block 9 e.printStackTrace(); 10 } 11 } 12 13 public void fun() throws InterruptedException{ 14 synchronized(this){ 15 for (int i = 0; i < 50; i++) { 16 System.out.print(i+" "); 17 } 18 System.out.println(); 19 } 20 Thread.sleep(1000); 21 synchronized (this) { 22 for (int i = 100; i < 150; i++) { 23 System.out.print(i+" "); 24 } 25 System.out.println(); 26 } 27 28 } 29 } 30 31 public class javaTest { 32 33 public static void main(String[] args) throws InterruptedException { 34 35 ParamterE1 p=new ParamterE1(); 36 Thread t=new Thread(p); 37 38 Thread t1=new Thread(p); 39 40 t.start(); 41 t1.start(); 42 } 43 }
运行结果(多次运行的结果):
1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 3 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 4 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
(4)线程加入的方法
调度方法有一个是join()方法,它使一个线程1跟在当前线程2后面运行,当线程1运行完后,再继续运行线程2.
下面程序标红的语句我觉得是join()函数使用的关键
1 public class javaTest2 extends Thread { 2 3 public void run(){ 4 for(int i=50;i<100;i++){ 5 System.out.print(i+" "); 6 } 7 System.out.println(); 8 } 9 } 10 11 public class ParamterE1 implements Runnable { 12 public javaTest2 jt2; 13 public void run(){ 14 15 synchronized(this){ 16 for (int i = 0; i < 50; i++) { 17 System.out.print(i+" "); 18 } 19 System.out.println(); 20 } 21 22 try { 23 jt2.join(); 24 } catch (InterruptedException e) { 25 // TODO Auto-generated catch block 26 e.printStackTrace(); 27 } 28 synchronized (this) { 29 for (int i = 100; i < 150; i++) { 30 System.out.print(i+" "); 31 } 32 System.out.println(); 33 } 34 35 } 36 37 38 } 39 40 public class javaTest { 41 42 public static void main(String[] args) throws InterruptedException { 43 44 ParamterE1 p=new ParamterE1(); 45 javaTest2 jt1=new javaTest2(); 46 p.jt2=jt1; 47 Thread t=new Thread(p); 48 49 jt1.start(); 50 t.start(); 51 52 } 53 }
运行结果:
1 50 0 51 1 52 2 53 3 54 4 55 5 56 6 57 7 58 8 59 9 60 10 61 11 62 12 63 13 64 14 65 15 66 67 68 69 70 71 72 73 16 74 17 75 18 76 19 77 20 78 21 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 2 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 3 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
join()函数就是让当前运行的线程停止运行,开始运行加入的线程,继续按它的顺序运行。(个人觉得我上述代码没有体现出这一点~)
5、中断线程
中断是指一个线程停止当前正在做的事情,转而去做一些其他事情。这由程序员来决定一个线程如何响应一个中断。不过更经常的是,结束进程。
一个线程通过调用Thread对象的interrupt()方法发出一个中断信号给要中断的线程。要使中断机制正确的工作,被中断的线程必须支持自身的中断。
public class ParamterE1 implements Runnable { public javaTest2 jt2; public void run(){ for (int i = 0; i < 50; i++) { System.out.print(i+" "); } System.out.println(); try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } for (int i = 100; i < 150; i++) { System.out.print(i+" "); } System.out.println(); } } public class javaTest { public static void main(String[] args) throws InterruptedException { ParamterE1 p=new ParamterE1(); Thread t=new Thread(p); t.start(); t.interrupt(); } }
运行结果:
1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 2 java.lang.InterruptedException: sleep interrupted 3 100 at java.lang.Thread.sleep(Native Method) 4 at test.ParamterE1.run(ParamterE1.java:16) 5 at java.lang.Thread.run(Thread.java:745) 6 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
当线程处于sleep状态时,直接被interrupt()函数打断,直接执行下面的语句。
6、结束进程
在java2中stop()方法不在被支持,在将来的版本中该方法可能被替代但是由于其天生的不安全性,替换的方法也不会取的好的效果。尽管在java2中不在支持盖房大,但是java仍然包含了改API,也就是说程序员仍然可以调用该方法来结束进程。
但调用stop()方法终止一个进程时,会释放该进程持有的所有锁,而问题是用户无法知道代码目前的工作内容,这是导致stop()方法不安全的因素。
public class ParamterE1 implements Runnable { public javaTest2 jt2; public void run(){ for (int i = 0; i < 50; i++) { System.out.print(i+" "); } System.out.println(); try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } for (int i = 100; i < 150; i++) { System.out.print(i+" "); } System.out.println(); } } public class javaTest { public static void main(String[] args) throws InterruptedException { ParamterE1 p=new ParamterE1(); Thread t=new Thread(p); t.start(); Thread.sleep(1); t.stop(); } }
运行结果:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
分析:只执行了前一个循环,后一个循环还没执行就应经线程终止。
如果想安全的结束进程又不使用stop()方法,则只有通过某种替代方法。在线程内部设计一个变量和一个可以设置该变量的方法。而该变量的取值作为结束线程的标志。线程间协作方式结束线程,代码如下:
class CoTest extends Thread{ private Boolean stopthread; public void stopThread() { stop=true;//值为true则线程退出run()方法 } public void run(){ while(!stop){ //值为false则线程继续执行 //to do something } // do clearing work } }
7、线程间通信(待完善)
8、多线程死锁
对于java语言来讲没有很好的预防死锁的方法,只有依靠读者谨慎的设计来避免死锁的发生。下面提供一个避免死锁的基本原则。、
(1)避免使用suspsend()和resume()方法,这些方法具有与生俱来产生死锁的缺点。
(2)不要长时间I/O操作的方法施加锁。
(3)使用多个锁时,确保所有线程都按相同的顺序获得锁。
9、多线程的缺点(不想说~~)
以上是关于多线程编程的主要内容,如果未能解决你的问题,请参考以下文章