Java中多线程的学习和使用(一)概述及创建方式

多线程的学习

一.多线程概述

(一).进程与线程

在说多线程或者线程之前,让我们来了解一下更显而易见的进程概念。那什么是进程呢?

进程就是正在进行中的程序。

Windows操作系统中在任务栏处右击,弹出的菜单中可看见任务管理器,打开它,就可以看见当前运行的程序和进程列表。

 

进程:是一个正在执行中的程序。

     每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。

线程:就是进程中的一个独立的控制单元。

          线程在控制进程的执行。

一个进程中至少有一个线程。

无论是qq还是迅雷,只要他们启动程序,就会在内存中开辟一段空间,产生地址值,进程就是用于标注这段空间的,用于封装里面的控制单元。

当出现’’java.lang.NoClassDefFoundError:com/sun/tools/javac/main’’错误的时候,证明java程序找不到tools.jar文件,也就是说环境变量有可能配置不对,或源文件丢失。

Java VM启动时就会有一个进程java.exe,该进程中至少有一个线程负责java程序的执行,而且这个线程运行的代码存在于main方法中。该线程称之为主线程。

扩展:其实更细节说明jvm,jvm不止一个线程,还有负责垃圾回收机制的线程。

有多条执行路径的程序,我们就称之为多线程程序。

多线程存在的意义。

同时进行,提高效率。

如何在我们的程序中自定义多线程程序。

通过对api的查找,java已经提供了对线程这类事物的描述。

Java.lang包下有一个Thread类,用于创建程序中的执行线程。就是用于描述控制单元这样d额一个对象。jVM允许应用程序并发地运行多个执行线程。

创建方式:

一种是将类声明称Thread的子类(继承Thread),该类应重写Thread类的run方法。

步骤:

1.自定义一个类继承Thread.

2.复写Thread类中的run()方法.

3.创建线程对象,调用start()方法启动线程。

//自定义一个类继承Thread

写个小demo:

复制代码
 1 class Demo extends Thread{
 2 
 3          //复写Thread类中run()方法
 4 
 5          public void run(){
 6 
 7                    for (int x=0;x<60;x++) {
 8 
 9                             System.out.println("demo run"+x);
10 
11                    }
12 
13          }
14 
15 }
16 
17  
18 
19 class ThreadDemo{
20 
21          public static void main(String[] args) {
22 
23                    //创建对象调用方法
24 
25                    //创建自定义的线程对象实际上就是创建了线程
26 
27                    Demo d = new Demo();
28 
29                    //调用start()方法,启动线程;jvm调用线程对象的run()方法,执行内部代码
30 
31                    d.start();
32 
33                    for (int x=0;x<60;x++) {
34 
35                             System.out.println("hello world!"+x);
36 
37                    }
38 
39          }
40 
41 }
复制代码

 

执行结果:

 

大家会看到执行的结果是交替进行的,并且还是不规则的交替打印。其原理是这样的:

 

上图解释了,两个线程同时进行,那么不规则如何解释呢?

 

也就是说:

因为多个线程都在获取cpu的执行权,cpu执行到谁,谁就运行。明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)cpu在做着快速的切换,以达到看上去是同时运行的效果。我们可以形象的吧多线程的运行形容为在互相抢夺cpu的执行权。

这就是多线程的一个特性:随机性。

谁抢到谁执行,至于执行多长时间,cpu说的算。

接下来思考:我们为什么要复写这个run()方法呢?

Thread类用于描述线程。该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run()方法。

解释:

既然是用于描述线程,Thread类里面就会有很多功能用于操作线程。我们创建线程的目的其实是为了让线程执行一些代码,那么线程就需要在描述过程中定义这些代码存放的位置。线程要启动,要运行,要运行什么,这个什么在哪啊?线程提供了一个存储空间。这个空间就是run()方法。

也就是说,Thread类中的run()方法,用于存储线程要运行的代码。同理,主线程要运行的代码,放在main()方法中,这个是jvm定义的。

这是从run()方法本身说,那么从start()方法说,就是:

我们在主线程main()方法中创建Thread类对象,也就是创建了一个线程,这个线程对象通过调用start()方法,将线程启动。而这个start()方法又会调用Thread类中的run()来运行里面的的代码,如果我们不复写run(),直接创建线程对象,直接start(),我们不会得到我们想要的结果,因为父类Thread类中的run()方法中可能什么也没有写,start()方法调用run()也是白调用。所以,我们需要复写run()方法,里面写上我们自定义的代码,这样,run()方法才有意义。我们自定义的线程类继承了Thread类,调用start()方法,而start()方法会调用run()父类方法,因为我们复写了父类中的方法,父类会找子类,所以实际会调用我们写的run(0方法,这样我们自定义的代码才会运行。

因此,复写Thread类中的run()方法

目的:将自定义代码存储在run()方法中,让线程运行。

注意:在主线程中线程对象.start()方法和主线程中线程对象直接.run()方法的区别(面试)

d.start();//开启线程并执行该线程的run()方法

//d.run();仅仅是对象调用方法,在主线程中执行,而线程创建了,并没有运行。即还是单线程程序。

小练习:

创建两个线程,和主线程交替运行。

复制代码
 1 class Test extends Thread{
 2 
 3                                                                                                                                                            private String name;
 4 
 5                                                                                                                                                                    Test(String name){
 6 
 7                                                                                                                                                                this.name = name;
 8 
 9                                                                                                                                                            }
10 
11                                                                                                                                                            public void run(){
12 
13                                                                                                                                                                for (int x=0;x<60;x++) {
14 
15                                                                                                                                                                         System.out.println(name + "test....run"+x);
16 
17                                                                                                                                                                }       
18 
19                                                                                                                                                            }
20 
21 }
22 
23  
24 
25 class ThreadDemo{
26 
27                                                                                                                                                            public static void main(String[] args) {
28 
29                                                                                                                                                                //创建对象调用方法
30 
31                                                                                                                                                                //创建自定义的线程对象实际上就是创建了线程
32 
33                                                                                                                                                                Demo d = new Demo();
34 
35                                                                                                                                                                //调用start()方法,启动线程;jvm调用线程对象的run()方法,执行内部代码
36 
37                                                                                                                                                                //d.start();//开启线程并执行该线程的run()方法
38 
39                                                                                                                                                                //d.run();仅仅是对象调用方法,在主线程中执行,而线程创建了,并没有运行。即还是单线程程序。
40 
41                                                                                                                                                                //测试小练习
42 
43                                                                                                                                                                Test t1 = new Test("one");
44 
45                                                                                                                                                                Test t2 = new Test("two");
46 
47                                                                                                                                                                t1.start();
48 
49                                                                                                                                                                t2.start();
50 
51                                                                                                                                                                for (int x=0;x<60;x++) {
52 
53                                                                                                                                                                        System.out.println("hello world!"+x);
54 
55                                                                                                                                                                }
56 
57                                                                                                                                                            }
58 
59 }                                                                                                                                                             
复制代码

线程运行状态(线程的生命周期)

线程在运行过程中,有几种状态是咱们必须掌握的。只有掌握这几种状态,我们才知道线程是怎样运作的。

 

获取线程对象以及名称

线程也有自己的名称,怎么获取呢?

找线程对象的方法吧。线程名称应该是定义在线程这类事物中。所以怎样获取这名称,是不是线程最熟悉?

Void setName(String name)

String getName()

线程都有自己默认的名称:Thread-编号,该编号从0开始。

复制代码
 1 class Test extends Thread{
 2 
 3                                                                                                                                                            private String name;
 4 
 5                                                                                                                                                            Test(String name){
 6 
 7                                                                                                                                                                this.name = name;
 8 
 9                                                                                                                                                            }
10 
11                                                                                                                                                            public void run(){
12 
13                                                                                                                                                                for (int x=0;x<60;x++) {
14 
15                                                                                                                                                                        System.out.println(this.getName() + "test....run"+x);
16 
17                                                                                                                                                                }       
18 
19                                                                                                                                                            }
20 
21 }
22 
23  
24 
25 class ThreadDemo{
26 
27                                                                                                                                                            public static void main(String[] args) {
28 
29                                                                                                                                                                //创建对象调用方法
30 
31                                                                                                                                                                //创建自定义的线程对象实际上就是创建了线程
32 
33                                                                                                                                                                Demo d = new Demo();
34 
35                                                                                                                                                                //调用start()方法,启动线程;jvm调用线程对象的run()方法,执行内部代码
36 
37                                                                                                                                                                //d.start();//开启线程并执行该线程的run()方法
38 
39                                                                                                                                                                //d.run();仅仅是对象调用方法,在主线程中执行,而线程创建了,并没有运行。即还是单线程程序。
40 
41                                                                                                                                                                //测试小练习
42 
43                                                                                                                                                                Test t1 = new Test("one");
44 
45                                                                                                                                                                Test t2 = new Test("two");
46 
47                                                                                                                                                                t1.start();
48 
49                                                                                                                                                                t2.start();
50 
51                                                                                                                                                                for (int x=0;x<60;x++) {
52 
53                                                                                                                                                                        System.out.println("hello world!"+x);
54 
55                                                                                                                                                                }
56 
57                                                                                                                                                            }
58 
59 }
复制代码

 

复制代码
 1 class Test extends Thread{
 2 
 3                                                                                                                                                            //private String name;
 4 
 5                                                                                                                                                            Test(String name){
 6 
 7                                                                                                                                                                //this.name = name;
 8 super(name);
 9                                                                                                                                                            }
10 
11                                                                                                                                                            public void run(){
12 
13                                                                                                                                                                for (int x=0;x<60;x++) {
14 
15                                                                                                                                                                        System.out.println(this.getName() + "test....run"+x);
16 
17                                                                                                                                                                }       
18 
19                                                                                                                                                            }
20 
21 }
22 
23  
24 
25 class ThreadDemo{
26 
27                                                                                                                                                            public static void main(String[] args) {
28 
29                                                                                                                                                                //创建对象调用方法
30 
31                                                                                                                                                                //创建自定义的线程对象实际上就是创建了线程
32 
33                                                                                                                                                                Demo d = new Demo();
34 
35                                                                                                                                                                //调用start()方法,启动线程;jvm调用线程对象的run()方法,执行内部代码
36 
37                                                                                                                                                                //d.start();//开启线程并执行该线程的run()方法
38 
39                                                                                                                                                                //d.run();仅仅是对象调用方法,在主线程中执行,而线程创建了,并没有运行。即还是单线程程序。
40 
41                                                                                                                                                                //测试小练习
42 
43                                                                                                                                                                Test t1 = new Test("one");
44 
45                                                                                                                                                                Test t2 = new Test("two");
46 
47                                                                                                                                                                t1.start();
48 
49                                                                                                                                                                t2.start();
50 
51                                                                                                                                                                for (int x=0;x<60;x++) {
52 
53                                                                                                                                                                        System.out.println("hello world!"+x);
54 
55                                                                                                                                                                }
56 
57                                                                                                                                                            }
58 
59 }
复制代码

 

这里博主本来有两个疑惑来着,后来经过博主苦思冥想,终于破解:

1.第一个既然打印的this.getName(),为什么获取到的不是one,two?

2.都已经super(name);了,为什么获取到的还是new对象时的name?

回答:

1.this是代表本类对象的引用,这个大家都知道,所以this.getName()是在本类中调用getName()方法,本类有吗?没有。那么他就会去父类Thread类中找,父类有吗?有,获取的是什么呢?由于我们没有创建父类的对象,更没有创建父类创建线程名称的构造函数,更没有调用父类setName()的方法,所以,父类的getName()方法,返回的是默认的setName()方法的值,也就是Thread-编号。

2.super(name)是在构造方法里,调用的是父类的构造方法,而父类的该方法又被子类对象复写,所以,又会去找子类,所以相当于饶了父类这一个弯子。

其实在1中也还有个小疑问没有解开,就是当set(),get()方法组合与构造函数同时操作同一事物时,哪一个会好使。

Thread类中还有个静态方法:

Static Thread currentThread()   返回对当前正在执行的线程对象。

也可以获取当前线程的名称,而且比this更通用。

总结一下:

获取线程名称:

Static Thread currentThread():获取当前的线程对象

getName()

设置线程名称

setName()或者构造函数。

 

 

小案例:

需求:简单的卖票程序,多个窗口同时卖票。

代码如下:

复制代码
 1 class Ticket extends Thread{
 2 
 3                                                                                                                                                            private int tick = 100;
 4 
 5                                                                                                                                                            public void run(){
 6 
 7                                                                                                                                                                while(true){
 8 
 9                                                                                                                                                                         if(tick>0){
10 
11                                                                                                                                                                         System.out.println("sale:" + tick--);
12 
13                                                                                                                                                                         }
14 
15                                                                                                                                                                }
16 
17                                                                                                                                                            }
18 
19 }
20 
21 class TicketDemo{
22 
23                                                                                                                                                            public static void main(String[] args){
24 
25                                                                                                                                                                Ticket t1 = new Ticket();
26 
27                                                                                                                                                                Ticket t2 = new Ticket();
28 
29                                                                                                                                                                Ticket t3 = new Ticket();
30 
31                                                                                                                                                                Ticket t4 = new Ticket();
32 
33                                                                                                                                                                t1.start();
34 
35                                                                                                                                                                t2.start();
36 
37                                                                                                                                                                t3.start();
38 
39                                                                                                                                                                t4.start();
40 
41                                                                                                                                                            }
42 
43 }
复制代码

 

但结果我们发现,每一个线程都会从100打印到1,也就是说每一个线程都卖了100张票,怎么解决呢?

让四个线程共享这100张票就可以了。

既然是共享的,没有特有的数据参与运算,我们就可以把100设为静态就搞定了。

但我们一般不这样,因为设为static后,它的生命周期过长。于是,我们就只创建一个对象,让它执行四次start()方法,结果发现也是可行的,只不过报了一个异常:

Java.lang.IllegalThreadStateExceptionThread-0…sale:99

这是无效的线程状态异常。已经运行的程序是不需要在开启的。那么显然,第一种创建方式,已经不行了,接下来我们来介绍一下第二种创建线程的方法。

创建线程的另外一种方法是声明实现Runnable接口的类。然后实现run方法。然后可以创建该类的实例,在创建时,作为一个参数来传递并启动。

Runnable接口里只有一个run()方法。

Thread类有一个构造方法可以接受Runnable接口类型的对象。

复制代码
 1 class Ticket implements Runnable{//extends Thread{
 2 
 3                                                                                                                                                            private int tick = 100;
 4 
 5                                                                                                                                                            public void run(){
 6 
 7                                                                                                                                                                while(true){
 8 
 9                                                                                                                                                                         if(tick>0){
10 
11                                                                                                                                                                         System.out.println("sale:" + tick--);
12 
13                                                                                                                                                                         }
14 
15                                                                                                                                                                }
16 
17                                                                                                                                                            }
18 
19 }
20 
21 class TicketDemo{
22 
23                                                                                                                                                            public static void main(String[] args){
24 
25                                                                                                                                                                // Ticket t1 = new Ticket();
26 
27                                                                                                                                                                // Ticket t2 = new Ticket();
28 
29                                                                                                                                                                // Ticket t3 = new Ticket();
30 
31                                                                                                                                                                // Ticket t4 = new Ticket();
32 
33                                                                                                                                                                // t1.start();
34 
35                                                                                                                                                                // t2.start();
36 
37                                                                                                                                                                // t3.start();
38 
39                                                                                                                                                                // t4.start();
40 
41                                                                                                                                                                Ticket t = new Ticket();//
42 
43                                                                                                                                                                //记住,开启线程的只能是Thread类或者Thread类子类的对象
44 
45                                                                                                                                                                Thread t1 = new Thread(t);//
46 
47                                                                                                                                                                Thread t2 = new Thread(t);
48 
49                                                                                                                                                                Thread t3 = new Thread(t);
50 
51                                                                                                                                                                Thread t4 = new Thread(t);
52 
53                                                                                                                                                                t1.start();
54 
55                                                                                                                                                                t2.start();
56 
57                                                                                                                                                                t3.start();
58 
59                                                                                                                                                                t4.start();
60 
61  
62 
63                                                                                                                                                            }
64 
65 }
复制代码

 

创建线程的第二种方法,实现Runable接口

步骤:

1.定义类实现Runnable接口

2.覆盖Runnable接口中的run()方法

3通过Thread类建立线程对象

4将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数

5调用Thread类的start方法开启线程(让其调用Runnable接口子类的run()方法)。

其中复写Runnable接口中的run()方法我们前文已经分析过了,将线程要运行的代码存放在该run()方法中。

那么,为什么要将Runnable接口的子类对象传递给Thread的构造函数?

因为,自定义的run()方法所属的对象是Runnable接口的子类对象,所以要让线程去执行指定对象的run()方法。就必须明确该run()所属对象。

那么,创建线程的第二种实现方式和第一种继承方式有什么区别呢?(面试)

实现方式好处:避免了单继承的局限性。

在定义线程时,建议使用实现方式。

继承Thread线程代码存放在Thread子类run()方法中

实现Runnable,线程代码存放在接口的子类的run()方法中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值