当前位置:   article > 正文

多线程开发:从T24开发转向Java,详解Java多线程编程

t24开发

一、线程的引入:

定义:同时对多项任务加以控制

我们上下代码:

1.未使用线程

1 packagecom.learn.chap08.sec01;2 /**

3 * 未使用线程--同步执行4 *@authorAdministrator5 *6 */

7 public classDemo1 {8

9 /**

10 * 听音乐11 */

12 public static voidmusic(){13 for (int i = 0; i < 5; i++) {14 System.out.println("听音乐");15 }16 }17

18 /**

19 * 吃饭20 */

21 public static voideat(){22 for (int i = 0; i < 5; i++) {23 System.out.println("吃饭");24 }25 }26

27 public static voidmain(String[] args) {28 music();29 eat();30 }31 }

2. 使用线程

1 packagecom.learn.chap08.sec01;2 /**

3 * 使用多线程--异步执行4 *@authorAdministrator5 *6 */

7 public class Eat extendsThread{8

9 @Override10 public voidrun() {11 for (int i = 0; i < 5; i++) {12 try{13 Thread.sleep(100); // 100毫秒14 } catch(InterruptedException e) {15 //TODO Auto-generated catch block

16 e.printStackTrace();17 }18 System.out.println("吃饭");19 }20 }21

22 }

1 packagecom.learn.chap08.sec01;2 /**

3 * 使用多线程--异步执行4 *@authorAdministrator5 *6 */

7 public class Music extendsThread {8

9 @Override10 public voidrun() {11 //TODO Auto-generated method stub

12 for (int i = 0; i < 5; i++) {13 try{14 Thread.sleep(100);15 } catch(InterruptedException e) {16 //TODO Auto-generated catch block

17 e.printStackTrace();18 }19 System.out.println("听音乐");20 }21 }22

23 }

1 packagecom.learn.chap08.sec01;2 /**

3 * 使用多线程--异步执行4 *@authorAdministrator5 *6 */

7 public classDemo2 {8 public static voidmain(String[] args) {9 /**

10 * 利用多线程--实现一边听音乐 一边吃饭11 */

12 Music musicThread = newMusic();13 Eat eatThread = newEat();14 musicThread.start();15 eatThread.start();16 }17 }

二、使用多线程

1. 继承Thread类

代码如下:

1 packagecom.learn.chap08.sec02;2

3 public class Thread1 extendsThread {4 privateString threadName;5 private int baoZi = 1;6

7 publicThread1(String threadName) {8 super();9 this.threadName =threadName;10 }11

12 @Override13 public voidrun() {14 //TODO Auto-generated method stub

15 while(baoZi<=10){16 System.out.println(this.threadName+" 吃第"+baoZi+"个包子");17 baoZi++;18 }19 }20

21 public static voidmain(String[] args) {22 System.out.println("张三、李四各自吃10个包子");23 Thread1 t1=new Thread1("张三线程");24 Thread1 t2=new Thread1("李四线程");25 t1.start();26 t2.start();27 }28

29 }

2. 实现Runnable接口

代码如下:

1 packagecom.learn.chap08.sec02;2

3 public class Thread2 implementsRunnable{4

5 privateString threadName;6 private int baoZi = 1;7

8 publicThread2(String threadName) {9 super();10 this.threadName =threadName;11 }12

13 @Override14 public voidrun() {15 //TODO Auto-generated method stub

16 while(baoZi<=10){17 System.out.println(this.threadName+" 吃第"+baoZi+"个包子");18 baoZi++;19 }20 }21

22 public static voidmain(String[] args) {23 System.out.println("张三、李四各自吃10个包子");24 Thread1 t1=new Thread1("张三线程");25 Thread1 t2=new Thread1("李四线程");26 Thread t11=new Thread(t1);27 Thread t12=new Thread(t2);28 t11.start();29 t12.start();30 }31

32 }

1 packagecom.learn.chap08.sec02;2

3 public class Thread3 implementsRunnable{4

5 privateString threadName;6 private int baoZi = 1;7

8 publicThread3(String threadName) {9 super();10 this.threadName =threadName;11 }12

13 @Override14 public synchronized voidrun() {15 //TODO Auto-generated method stub

16 while(baoZi<=10){17 System.out.println(this.threadName+" 吃第"+baoZi+"个包子");18 baoZi++;19 }20 }21

22 public static voidmain(String[] args) {23

24 Thread3 t1=new Thread3("超级张三线程");25

26 Thread t11=new Thread(t1);27 Thread t12=new Thread(t1);28 Thread t13=new Thread(t1);29 //实现资源共享

30 t11.start();31 t12.start();32 t13.start();33 }34

35 }

总结: Runnable接口 可以实现资源共享  而Thread不能。

三、线程状态

95a2618ba28eb56d9f760964e89f66f4.png

79c0c53b267821da549eb00956a07f2c.png

四、多线程常用的方法

b8b254cc5e7e841024f15d4d710e7e80.png

举例代码如下:

1 packagecom.learn.chap08.sec04;2

3 public class Demo1 implementsRunnable{4

5 @Override6 public voidrun() {7 //TODO Auto-generated method stub

8 for (int i = 0; i < 10; i++) {9 //获取当前线程

10 Thread t=Thread.currentThread();11 System.out.println(t.getName()+":"+i);12 }13

14 }15

16 public static voidmain(String[] args) {17 Demo1 demo1 = newDemo1();18 new Thread(demo1).start();19 new Thread(demo1,"线程一").start();20 new Thread(demo1,"线程二").start();21

22 Thread it = new Thread(demo1);23 System.out.println(it.isAlive());24 it.start();25 System.out.println(it.isAlive());26 }27

28 }

运行结果

false

true

Thread-1:0

Thread-1:1

Thread-1:2

Thread-1:3

Thread-1:4

Thread-1:5

Thread-1:6

Thread-1:7

Thread-1:8

Thread-1:9

线程二:0

线程二:1

线程二:2

线程二:3

线程二:4

线程二:5

线程二:6

线程二:7

线程二:8

线程二:9

Thread-0:0

Thread-0:1

Thread-0:2

Thread-0:3

Thread-0:4

Thread-0:5

Thread-0:6

Thread-0:7

Thread-0:8

Thread-0:9

线程一:0

线程一:1

线程一:2

线程一:3

线程一:4

线程一:5

线程一:6

线程一:7

线程一:8

线程一:9

1 packagecom.learn.chap08.sec04;2

3 public class Demo2 implementsRunnable{4

5 @Override6 public voidrun() {7 //TODO Auto-generated method stub

8 for (int i = 0; i < 10; i++) {9 try{10 Thread.sleep(1000); //线程休眠11 //获取当前线程

12 Thread t=Thread.currentThread();13 System.out.println(t.getName()+":"+i);14 } catch(InterruptedException e) {15 //TODO Auto-generated catch block

16 e.printStackTrace();17 }18

19 }20

21 }22

23 public static voidmain(String[] args) {24 Demo2 demo1 = newDemo2();25 newThread(demo1).start();26 new Thread(demo1,"线程一").start();27 }28

29 }

运行结果

Thread-0:0

线程一:0

Thread-0:1

线程一:1

.

.

.

1 packagecom.learn.chap08.sec04;2

3 public class Demo3 implementsRunnable{4

5 @Override6 public voidrun() {7 //TODO Auto-generated method stub

8 for (int i = 0; i < 10; i++) {9 //获取当前线程

10 Thread t=Thread.currentThread();11 System.out.println(t.getName()+":"+i);12

13 }14

15 }16

17 public static voidmain(String[] args) {18 Demo3 demo1 = newDemo3();19 Thread t1 = new Thread(demo1,"线程一");20 Thread t2 = new Thread(demo1,"线程二");21 Thread t3 = new Thread(demo1,"线程三");22 t1.setPriority(Thread.MAX_PRIORITY); // 更改线程的优先级23 t2.setPriority(Thread.NORM_PRIORITY);24 t3.setPriority(Thread.MIN_PRIORITY);25 t1.start();26 t2.start();27 t3.start();28 }29

30 }

1 packagecom.learn.chap08.sec04;2

3 public class Demo4 implementsRunnable{4

5 @SuppressWarnings("static-access")6 @Override7 public voidrun() {8 //TODO Auto-generated method stub

9 for (int i = 0; i < 10; i++) {10 try{11 Thread.sleep(100);12 //获取当前线程

13 Thread t=Thread.currentThread();14 System.out.println(t.getName()+":"+i);15 if(i==5){16 System.out.println("线程礼让:");17 Thread.currentThread().yield();// 出现@SuppressWarnings("static-access")

18 }19 } catch(InterruptedException e) {20 //TODO Auto-generated catch block

21 e.printStackTrace();22 }23 }24 }25

26 public static voidmain(String[] args) {27 Demo4 demo1 = newDemo4();28 Thread t1 = new Thread(demo1,"线程一");29 Thread t2 = new Thread(demo1,"线程二");30 t1.start();31 t2.start();32 }33

34 }

运行结果

线程一:0

线程二:0

线程一:1

线程二:1

线程一:2

线程二:2

线程二:3

线程一:3

线程一:4

线程二:4

线程一:5

线程礼让:

线程二:5

线程礼让:

线程一:6

线程二:6

线程一:7

线程二:7

线程一:8

线程二:8

线程一:9

线程二:9

五、线程同步

1. 同步方法

2. 同步锁(锁机制)

上下代码:

1 packagecom.learn.chap08.sec05;2

3 public class Demo2 implementsRunnable{4

5 private int baoZi=10;6

7 @Override8 /**

9 * 同步方法10 */

11 public synchronizedvoid run() { //用synchronized标识 的方法为同步方法 不加锁的话,下面的张三、李四、王五可能会同时进入run()方法12 //TODO Auto-generated method stub

13 while(baoZi>0){14 System.out.println(Thread.currentThread().getName()+"吃了第"+baoZi+"个包子");15 baoZi--;16 }17 }18

19 public static voidmain(String[] args) {20 Demo2 demo1=newDemo2();21 new Thread(demo1,"张三").start();22 new Thread(demo1,"李四").start();23 new Thread(demo1,"王五").start();24 }25

26 }

1 packagecom.learn.chap08.sec05;2

3 public class Demo3 implementsRunnable{4

5 private int baoZi=10;6

7 @Override8 public voidrun() {9 /**

10 * 同步块11 */

12 synchronized (this) { //同步块

13 while(baoZi>0){14 System.out.println(Thread.currentThread().getName()+"吃了第"+baoZi+"个包子");15 baoZi--;16 }17 }18 }19

20 public static voidmain(String[] args) {21 Demo3 demo1=newDemo3();22 new Thread(demo1,"张三").start();23 new Thread(demo1,"李四").start();24 new Thread(demo1,"王五").start();25 }26

27 }

下面举例:java synchronized同步方法调用另一个同步方法,锁机制问题

1 public synchronized void methodA(int a, intb);2

3 public synchronized void methodB(inta){4 methodA(a, 0);5 }

要明白两个问题,1.锁的对象是谁,2.谁持有了锁。

假设方法A和B是在同一个类Test中的两个方法。

Test t=new Test();

t.methodB();

这个时候,methodB方法被调用时,因为加了synchronized ,需要先获得一个锁,这个锁的对象应该是t,也就是当前的这个Test类的实例,而获得锁的东西是线程,也就是说当前线程拿到了t的锁(而不是你说的B方法获得锁),这个时候B方法内调用methodA,因为A也加了synchronized,也需要获得一个锁,因为A和B都是Test类中的方法,所以当前线程要获得的锁的对象也是t。由于当前线程在执行B方法时已经持有了t对象的锁,因此这时候调用methodA是没有任何影响的,相当于方法A上没有加synchronized。

另一种情况:假设现在有两个Test类

Test t1=new Test();

Test t2=new Test();

t1.methodB();//此时当前线程持有了t1对象的锁

t2.methodB();//此时当前线程也持有了t2对象的锁

当前线程持有了两把锁,锁的对象分别是两个不同的Test类的实例t1和t2,互相没有影响。

再一种情况:假设在多线程环境下,两个线程都可以访问Test t=new Test();

此时假设thread1里调用t.methodB();同时thread2里调用t.methodB()

这时假设thread1先抢到t对象的锁,那么thread2需要等待thread1释放t对象的锁才可以执行B方法。

结果像这样:

thread1获得t的锁--thread1执行methodB--thread1执行methodA--释放t的锁---thread2获得t的锁--thread2执行methodB--thread2执行methodA--释放t的锁。

synchronized还有很多种使用方法,但只有明白是那条线程获得哪个对象的锁,就很容易明白了。

2016年10月31号偶然看到了crossoverjie整理的多线程知识点(原文链接:http://www.jianshu.com/p/72d53d4f833a),感觉不错,于是在这顺便记录下,补充下知识,呵呵!

进程与线程的区别

进程

进程简单的来说就是在内存中运行的应用程序,一个进程可以启动多个线程。

比如在windows中一个运行EXE文件就是一个进程。

线程

同一进程内的线程共享此进程的地址空间,同时共享进程所拥有的内存和其他资源。

--------------------------------------------------------------------------------------------------

线程Demo-继承Thread类

首先我们我们继承java.lang.Thread类来创建线程。

1 packagetop.crosssoverjie.study.Thread;2

3 public classTestThread {4 public static voidmain(String[] args) {5 System.out.println("主线程ID是:" +Thread.currentThread().getId());6 MyThread my = new MyThread("线程1");7 my.start() ;8

9 MyThread my2 = new MyThread("线程2") ;10 /**

11 * 这里直接调用my2的run()方法。12 */

13 my2.run() ;14 }15

16 }17

18 class MyThread extendsThread {19 privateString name;20

21 publicMyThread(String name) {22 this.name =name;23 }24

25 @Override26 public voidrun() {27 System.out.println("名字:" + name + "的线程ID是="

28 +Thread.currentThread().getId());29 }30

31 }

输出结果:

主线程ID是:1

名字:线程2的线程ID是=1

名字:线程1的线程ID是=9

由输出结果我们可以得出以下结论:

my和my2的线程ID不相同,my2和主线程ID相同。说明直接调用run()方法不会创建新的线程,而是在主线程中直接调用的run()方法,和普通的方法调用没有区别。

虽然my的start()方法是在my2的run()方法之前调用,但是却是后输出内容,说明新建的线程并不会影响主线程的执行。

----------------------------------------------------------------------------------------------------

线程Demo-实现Runnable接口

除了继承java.lang.Thread类之外,我们还可以实现java.lang.Runnable接口来创建线程。

1 packagetop.crosssoverjie.study.Thread;2

3 public classTestRunnable {4 public static voidmain(String[] args) {5 System.out.println("主线程的线程ID是"+Thread.currentThread().getId());6 MyThread2 my = new MyThread2("线程1") ;7 Thread t = newThread(my) ;8 t.start() ;9

10 MyThread2 my2 = new MyThread2("线程2") ;11 Thread t2 = newThread(my2) ;12 /**

13 * 方法调用,并不会创建线程,依然是主线程14 */

15 t2.run() ;16 }17 }18

19 class MyThread2 implementsRunnable{20 privateString name ;21 publicMyThread2(String name){22 this.name =name ;23 }24

25 @Override26 public voidrun() {27 System.out.println("线程"+name+"的线程ID是"+Thread.currentThread().getId());28 }29

30

31 }

输出结果:

主线程的线程ID是1

线程线程2的线程ID是1

线程线程1的线程ID是9

notes:

实现Runnable的方式需要将实现Runnable接口的类作为参数传递给Thread,然后通过Thread类调用Start()方法来创建线程。

这两种方式都可以来创建线程,至于选择哪一种要看自己的需求。直接继承Thread类的话代码要简洁一些,但是由于java只支持单继承,所以如果要继承其他类的同时需要实现线程那就只能实现Runnable接口了,这里更推荐实现Runnable接口。

实际上如果我们查看Thread类的源码我们会发现Thread是实现了Runnable接口的:

f7b797884336ad35183abf3887670fcb.png

线程中常用的方法:

9dab10ab72ccdd8bdd69658c72d3a0db.png

方法详解- public static void sleep(long mills)

1 packagetop.crosssoverjie.study.Thread;2

3 public classTestSleep {4

5 private int i = 10;6 private Object ob = newObject() ;7

8 public static voidmain(String[] args) {9 TestSleep t = newTestSleep() ;10 MyThread3 thread1 = t.newMyThread3() ;11 MyThread3 thread2 = t.newMyThread3() ;12 thread1.start() ;13 thread2.start() ;14 }15

16 class MyThread3 extendsThread{17 @Override18 public voidrun() {19 synchronized(ob) {20 i++;21 System.out.println("i的值:"+i);22 System.out.println("线程:"+Thread.currentThread().getName()+"进入休眠状态");23 try{24 Thread.currentThread().sleep(1000) ;25 } catch(Exception e) {26 e.printStackTrace();27 }28 System.out.println("线程:"+Thread.currentThread().getName()+"休眠结束");29 i++;30 System.out.println("i的值>:"+i);31 }32 }33 }34

35 }

输出结果:

i的值:11

线程:Thread-0进入休眠状态

线程:Thread-0休眠结束

i的值>:12

i的值:13

线程:Thread-1进入休眠状态

线程:Thread-1休眠结束

i的值>:14

由输出结果我们可以得出:

当Thread0进入休眠状态时,Thread1并没有继续执行,而是等待Thread0休眠结束释放了对象锁,Thread1才继续执行。

当调用sleep()方法时,必须捕获异常或者向上层抛出异常。当线程休眠时间满时,并不一定会马上执行,因为此时有可能CPU正在执行其他的任务,所以调用了sleep()方法相当于线程进入了阻塞状态。

方法详解- public static void yield()

1 packagetop.crosssoverjie.study.Thread;2

3 public classTestyield {4 public static voidmain(String[] args) {5 MyThread4 my = newMyThread4() ;6 my.start() ;7 }8 }9 class MyThread4 extendsThread{10 @Override11 public voidrun() {12 long open =System.currentTimeMillis();13 int count= 0;14 for(int i=0 ;i<1000000;i++){15 count= count+(i+1);16 //Thread.yield() ;

17 }18 long end =System.currentTimeMillis();19 System.out.println("用时:"+(end-open)+"毫秒");20 }21 }

输出结果:

用时:1毫秒

如果将 Thread.yield()注释取消掉,输出结果:

用时:116毫秒

总结:

调用yield()方法是为了让当前线程交出CPU权限,让CPU去执行其他线程。它和sleep()方法类似同样是不会释放锁。但是yield()不能控制具体的交出CUP的时间。并且它只能让相同优先级的线程获得CPU执行时间的机会。

调用yield()方法不会让线程进入阻塞状态,而是进入就绪状态,它只需要等待重新获取CPU的时间,这一点和sleep()方法是不一样的。

方法详解- public final void join()

在很多情况下我们需要在子线程中执行大量的耗时任务,但是我们主线程又必须得等待子线程执行完毕之后才能结束,这就需要用到 join()方法了。join()方法的作用是等待线程对象销毁,如果子线程执行了这个方法,那么主线程就要等待子线程执行完毕之后才会销毁,请看下面这个例子:

1 packagetop.crosssoverjie.study.Thread;2

3 public classTestjoin {4 public static void main(String[] args) throwsInterruptedException {5 new MyThread5("t1").start() ;6 for (int i = 0; i < 10; i++) {7 if(i == 5){8 MyThread5 my =new MyThread5("t2") ;9 my.start() ;10 my.join() ;11 }12 System.out.println("main当前线程:"+Thread.currentThread().getName()+" "+i);13 }14 }15 }16 class MyThread5 extendsThread{17

18 publicMyThread5(String name){19 super(name) ;20 }21 @Override22 public voidrun() {23 for (int i = 0; i < 5; i++) {24 System.out.println("当前线程:"+Thread.currentThread().getName()+" "+i);25 }26 }27 }

输出结果:

main当前线程:main 0

当前线程:t1 0

当前线程:t1 1

main当前线程:main 1

当前线程:t1 2

main当前线程:main 2

当前线程:t1 3

main当前线程:main 3

当前线程:t1 4

main当前线程:main 4

当前线程:t2 0

当前线程:t2 1

当前线程:t2 2

当前线程:t2 3

当前线程:t2 4

main当前线程:main 5

main当前线程:main 6

main当前线程:main 7

main当前线程:main 8

main当前线程:main 9

如果我们把join()方法注释掉之后:

main当前线程:main 0

当前线程:t1 0

main当前线程:main 1

当前线程:t1 1

main当前线程:main 2

当前线程:t1 2

main当前线程:main 3

当前线程:t1 3

main当前线程:main 4

当前线程:t1 4

main当前线程:main 5

main当前线程:main 6

main当前线程:main 7

main当前线程:main 8

main当前线程:main 9

当前线程:t2 0

当前线程:t2 1

当前线程:t2 2

当前线程:t2 3

当前线程:t2 4

由上我们可以得出以下结论:

在使用了join()方法之后主线程会等待子线程结束之后才会结束。

方法详解- setDaemon(boolean on),getDaemon()

用来设置是否为守护线程和判断是否为守护线程。

notes:

守护线程依赖于创建他的线程,而用户线程则不需要。如果在main()方法中创建了一个守护线程,那么当main方法执行完毕之后守护线程也会关闭。而用户线程则不会,在JVM中垃圾收集器的线程就是守护线程。

优雅的终止线程

有三种方法可以终止线程,如下:

使用退出标识,使线程正常的退出,也就是当run()方法完成后线程终止。

使用stop()方法强行关闭,这个方法现在已经被废弃,不推荐使用

使用interrupt()方法终止线程。

具体的实现代码我将在下一篇博文中将到。。

线程的优先级

在操作系统中线程是分优先级的,优先级高的线程CPU将会提供更多的资源,在java中我们可以通过setPriority(int newPriority)方法来更改线程的优先级。

在java中分为1~10这个十个优先级,设置不在这个范围内的优先级将会抛出IllegalArgumentException异常。

java中有三个预设好的优先级:

public final static int MIN_PRIORITY = 1;

public final static int NORM_PRIORITY = 5;

public final static int MAX_PRIORITY = 10;

java多线程思维图:

a9d412c50f9cef403f6437921f11a9f9.png

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/683985
推荐阅读
相关标签
  

闽ICP备14008679号