Java多线程之线程创建

一、程序、进程与线程的理解:

1、程序(program):

是为完成特定任务、用某种语言编写的一组指令的集合。
即指一段静态的代码,静态对象。

2、进程(process):

是程序的一次执行过程, 或是正在运行的一个程序。
是一个动态的过程:有它自身的产生、存在和消亡的过程,即生命周期。
(1)例如:运行中的QQ,运行中的MP3播放器。
(2)程序是静态的,进程是动态的。
(3)进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域。

  

3、线程(thread):

进程可进一步细化为线程,是一个程序内部的一条执行路径。
(1)若一个进程同一时间并行执行多个线程,就是支持多线程的。
(2)线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小。
(3)一个进程中的多个线程共享相同的内存单元/内存地址空间:
它们从同一堆中分配对象,可以访问相同的变量和对象。
这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。

  

二、单核CPU与多核CPU的理解:

1、单核CPU:

其实是一种假的多线程,因为在一个时间单元内,也只能执行一个线程的任务。
例如:虽然有多车道,但是收费站只有一个工作人员在收费,只有收了费才能通过,
那么CPU就好比收费人员。如果有某个人不想交钱,那么收费人员可以把他“挂起”。
但是因为CPU时间单元特别短,因此感觉不出来。

2、多核CPU:

如果是多核的话,才能更好的发挥多线程的效率。 (现在的服务器都是多核的)
在一个Java应用程序java.exe, 其实至少有三个线程:main()主线程、gc()、垃圾回收线程、
异常处理线程。当然如果发生异常,会影响主线程。

三、并行与并发的理解:

1、并行:多个CPU同时执行多个任务。比如多个人同时做不同的事。
2、并发:一个CPU(采用时间片)同时执行多个任务。比如秒杀、多个人做同一件事。

四、多线程的理解:

1、背景:

以单核CPU为例, 只使用单个线程先后完成多个任务(调用多个方法),
肯定比用多个线程来完成用的时间更短,为何仍需多线程呢?

2、多线程程序的优点:

(1)提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
(2)提高计算机系统CPU的利用率。
(3)改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改。

3、何时需要多线程:

(1)程序需要同时执行两个或多个任务。
(2)程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
(3)需要一些后台运行的程序时。

五、线程的创建和使用:

1、Thread类的特性:
(1)每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为线程体
(2)通过该Thread对象的start()方法来启动这个线程,而非直接调用run()。
(3)Java语言的JVM允许程序运行多个线程,它通过java.lang.Thread类来体现。

2、Thread类构造器:
(1)Thread():创建新的Thread对象。
(2)Thread(String threadname):创建线程并指定线程实例名。
(3)Thread(Runnable target):指定创建线程的目标对象,它实现了Runnable接口中的run方法。
(4)Thread(Runnable target, String name):创建新的Thread对象。

3、Thread类成员方法:
●void start():启动线程,并执行对象的run()方法。
●run():线程在被调度时执行的操作。
●String getName():返回线程的名称。
●void setName(String name):设置该线程名称。
●static Thread currentThread():返回当前线程。
在Thread子类中就是this,通常用于主线程和Runnable实现类。
●static void yield():线程让步。
暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程。
若队列中没有同优先级的线程,忽略此方法。
●join():当某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,
直到join()方法加入的join线程执行完为止。低优先级的线程也可以获得执行。
●static void sleep(long millis):(指定时间:亳秒)。
令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。
抛出InterruptedException异常。

4、API中创建线程的两种方式:
继承Thread类的方式、实现Runnable接口的方式。
方式一:继承Thread类
(1)定义子类继承Thread类。
(2)子类中重写Thread类中的run方法。
(3)创建Thread子类对象,即创建了线程对象。
(4)调用线程对象start方法:启动线程,调用run方法。

 1 //例子:遍历100以内的所有的偶数
 2 //1. 创建一个继承于Thread类的子类
 3 class MyThread extends Thread {
 4     //2. 重写Thread类的run()
 5     @Override
 6     public void run() {
 7         for (int i = 0; i < 100; i++) {
 8             if(i % 2 == 0){ 
 9                 System.out.println(Thread.currentThread().getName() 
10                         + ":" + i);
11             }
12         }
13     }
14 }
15 
16 public class ThreadTest {
17     public static void main(String[] args) {
18         //3. 创建Thread类的子类的对象
19         MyThread t1 = new MyThread();
20 
21         //4.通过此对象调用start():启动当前线程、调用当前线程的run()
22         t1.start();
23         //问题一:我们不能通过直接调用run()的方式启动线程。
24         //t1.run();
25 
26         //问题二:再启动一个线程,遍历100以内的偶数。
27         // 不可以还让已经start()的线程去执行。会报IllegalThreadStateException
28         //t1.start();
29         //我们需要重新创建一个线程的对象
30         MyThread t2 = new MyThread();
31         t2.start();
32 
33         //如下操作仍然是在main线程中执行的。
34         for (int i = 0; i < 100; i++) {
35             if(i % 2 == 0){
36                 System.out.println(Thread.currentThread().getName() 
37                         + ":" + i + "***********main()************");
38             }
39         }
40     }
41 }

注意事项:
1、如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。
2、run()方法由JVM调用,什么时候调用,执行的过程控制都有操作系统的CPU调度决定。
3、想要启动多线程,必须调用start方法。
4、一个线程对象只能调用一次star()方法启动,如果重复调用了,
则将抛出以上的异常“llegalThreadStateException"。

方式二:实现Runnable接口
(1)定义子类,实现Runnable接口。
(2)子类中重写Runnable接口中的run方法。
(3)通过Thread类含参构造器创建线程对象。
(4)将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
(5)调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。

 1 //1. 创建一个实现了Runnable接口的类
 2 class MThread implements Runnable{
 3     //2. 实现类去实现Runnable中的抽象方法:run()
 4     @Override
 5     public void run() {
 6         for (int i = 0; i < 100; i++) {
 7             if(i % 2 == 0){
 8                 System.out.println(Thread.currentThread().getName()
 9                         + ":" + i);
10             }
11 
12         }
13     }
14 }
15 
16 public class ThreadTest1 {
17     public static void main(String[] args) {
18         //3. 创建实现类的对象
19         MThread mThread = new MThread();
20         //4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
21         Thread t1 = new Thread(mThread);
22         t1.setName("线程1");
23         //5. 通过Thread类的对象调用start():启动线程、
24         // 调用当前线程的run()-->调用了Runnable类型的target的run()
25         t1.start();
26 
27         //再启动一个线程,遍历100以内的偶数
28         Thread t2 = new Thread(mThread);
29         t2.setName("线程2");
30         t2.start();
31     }
32 }

比较创建线程的两种方式:
开发中:优先选择实现Runnable接口的方式。
1、 实现的方式没有类的单继承性的局限性。
2.、实现的方式更适合来处理多个线程有共享数据的情况。
联系:public class Thread implements Runnable
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。

猜你喜欢

转载自www.cnblogs.com/ZengBlogs/p/12203181.html