一、程序、进程与线程的理解:
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()中。
原文地址:https://www.cnblogs.com/ZengBlogs/p/12203181.html