一、线程定义
首先,关于线程的定义,线程:系统中的最小执行单元,同一进程中有多个线程,线程可以共享资源,一旦出现共享资源,必须注意线程安全!!Java线程分为两类,一类是守护线程,典型是垃圾回收GC;第二类是用户线程,当JVM中都是JVM守护线程,那么当前的JVM将退出。
线程与进程不同,进程是程序的执行过程,进程拥有独立运行所需要的全部资源,具有动态性,即运行的程序就叫进程,不运行就叫程序 。
二、单线程与多线程
单线程
每个正在运行的程序(即进程),至少包括一个线程,这个线程叫主线程。主线程在程序启动时被创建,用于执行main函数
只有一个主线程的程序,称作单线程程序,即我们之前编写的一般程序。
主线程负责执行程序的所有代码(UI展现以及刷新,网络请求,本地存储等等)。这些代码只能顺序执行,无法并发执行
多线程
拥有多个线程的程序,称作多线程程序。iOS允许用户自己开辟新的线程,相对于主线程来讲,这些线程,称为子线程
可以根据需要开辟若干子线程
子线程和主线程都是独立的运行单元,各自的执行互不影响,因此能够并发执行
三、多线程的创建
1.线程创建原理
操作系统创建了进程后,会创建一个线程执行进程中的代码,这也就是主线程。主线程在运行过程中,可能会创建其他线程(辅助线程),这些创建的线程又可以创建其他线程,这样就使得多个线程在同一个进程中执行。每个线程都独立运行,共享进程提供的各种资源,如代码、数据、虚拟地址空间等,每个线程可以执行进程中的不同代码,进程中的同一段代码也可以由多个线程执行(可以同时被多个线程访问的代码、数据等资源就是临界资源)。
线程就是程序中的一个执行路径, 它在进程上下文中执行,有一定的生命周期。
线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立。 线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单。
多个线程的执行是并发的,也就是在逻辑上“同时”,而不管是否是物理上的“同时”。如果系统只有一个CPU,那么真正的“同时”是不可能的,但是由于CPU的速度非常快,用户感觉不到其中的区别,因此我们也不用关心它,只需要设想各个线程是同时执行即可。
2.多线程的创建主要有两种方式:继承Thread类、实现Runnable接口
2.1.继承Thread类
即创建的线程类继承Thread类,重写其中的run方法,若在主类中调用线程的话,则new出线程类,调用它的start方法即可,这里的start方法完成了两个工作:启动子线程,调用子线程的run方法;
//线程类
class TestThread extends Thread {
@Override
public void run() {
for(int i=1;i<=100;i++){
if(i%2==0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
主类中调用:
TestThread myThread = new TestThread();
TestThread2 myThread2 = new TestThread2();
myThread.setName("偶数线程");//为线程命名
myThread2.setName("奇数线程");
myThread.start();//启动线程
myThread2.start();
Thread.currentThread().setName("主线程");//Thread.currentThread()表示当前的线程
for(int i=1;i<=100;i++){
System.out.println(Thread.currentThread().getName()+ ":" + i);
}
2.2实现Runnable接口
即创建的线程类实现Runnable接口,实现其中的run方法,若在主类中调用线程的话,则new出线程类,这里注意不能直接调用它的start方法,而是通过Thread 线程名 = new Thread(new出来的类),然后调用这个线程的start的方法才能启动线程,反之线程的启动该必须调用start方法!比extends Thread类的方法略微复杂,但这种方式更具优势(下面在做具体说明)
class TestThread2 implements Runnable{
public void run() {
for(int i=1;i<=100;i++){
if(i%2!=0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
然后调用时一定注意,不是直接调用start方法,
TestThread2 myThread2 = new TestThread2();
Thread t2 = new Thread(myThread2);
t2.setName("奇数线程");//为线程命名
t2.start();//启动线程
Thread.currentThread().setName("主线程");//Thread.currentThread()表示当前的线程
for(int i=1;i<=100;i++){
System.out.println(Thread.currentThread().getName()+ ":" + i);
}
在实际应用中,我们经常用到多线程,如车站的售票系统,车站的各个售票口相当于各个线程。当我们做这个系统的时候可能会想到两种方式来实现,继承Thread类或实现Runnable接口,现在看一下这两种方式实现的两种结果。
- package com.threadtest;
- class MyThread extends Thread{
- private int ticket = 10;
- private String name;
- public MyThread(String name){
- this.name =name;
- }
- public void run(){
- for(int i =0;i<500;i++){
- if(this.ticket>0){
- System.out.println(this.name+"卖票---->"+(this.ticket--));
- }
- }
- }
- }
- public class ThreadDemo {
- public static void main(String[] args) {
- MyThread mt1= new MyThread("一号窗口");
- MyThread mt2= new MyThread("二号窗口");
- MyThread mt3= new MyThread("三号窗口");
- mt1.start();
- mt2.start();
- mt3.start();
- }
- }
运行结果如下:
- 一号窗口卖票---->10
- 一号窗口卖票---->9
- 二号窗口卖票---->10
- 一号窗口卖票---->8
- 一号窗口卖票---->7
- 一号窗口卖票---->6
- 三号窗口卖票---->10
- 一号窗口卖票---->5
- 一号窗口卖票---->4
- 一号窗口卖票---->3
- 一号窗口卖票---->2
- 一号窗口卖票---->1
- 二号窗口卖票---->9
- 二号窗口卖票---->8
- 三号窗口卖票---->9
- 三号窗口卖票---->8
- 三号窗口卖票---->7
- 三号窗口卖票---->6
- 三号窗口卖票---->5
- 三号窗口卖票---->4
- 三号窗口卖票---->3
- 三号窗口卖票---->2
- 三号窗口卖票---->1
- 二号窗口卖票---->7
- 二号窗口卖票---->6
- 二号窗口卖票---->5
- 二号窗口卖票---->4
- 二号窗口卖票---->3
- 二号窗口卖票---->2
- 二号窗口卖票---->1
通过实现Runnable接口的代码如下:
- package com.threadtest;
- class MyThread1 implements Runnable{
- private int ticket =10;
- private String name;
- public void run(){
- for(int i =0;i<500;i++){
- if(this.ticket>0){
- System.out.println(Thread.currentThread().getName()+"卖票---->"+(this.ticket--));
- }
- }
- }
- }
- public class RunnableDemo {
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- //设计三个线程
- MyThread1 mt = new MyThread1();
- Thread t1 = new Thread(mt,"一号窗口");
- Thread t2 = new Thread(mt,"二号窗口");
- Thread t3 = new Thread(mt,"三号窗口");
- // MyThread1 mt2 = new MyThread1();
- // MyThread1 mt3 = new MyThread1();
- t1.start();
- t2.start();
- t3.start();
- }
- }
运行结果如下:
- 一号窗口卖票---->10
- 三号窗口卖票---->9
- 三号窗口卖票---->7
- 三号窗口卖票---->5
- 三号窗口卖票---->4
- 三号窗口卖票---->3
- 三号窗口卖票---->2
- 三号窗口卖票---->1
- 一号窗口卖票---->8
- 二号窗口卖票---->6
为什么会出现这种结果呐。我们不妨做个比喻,其实刚的程序,
继承Thread类的,我们相当于拿出三件事即三个卖票10张的任务分别分给三个窗口,他们各做各的事各卖各的票各完成各的任务,因为MyThread继承Thread类,所以在new MyThread的时候在创建三个对象的同时创建了三个线程;
实现Runnable的, 相当于是拿出一个卖票10张得任务给三个人去共同完成,new MyThread相当于创建一个任务,然后实例化三个Thread,创建三个线程即安排三个窗口去执行。
用图表示如下:
继承Thread类和实现Runnable接口实现多线程,一个是多个线程分别完成自己的任务,一个是多个线程共同完成一个任务 。
此篇文章大多属于他人总结,本人略作收集整理,将初学者的一些问题收集起来一起解答,希望能对一些小白略有帮助。
详细了解可以去下面几篇博客。
转自:
多线程知识点总结
https://blog.csdn.net/jacksonary/article/details/72848270
单线程、多线程的区别
https://blog.csdn.net/mpp_king/article/details/77366839
Java中继承thread类与实现Runnable接口的区别
https://blog.csdn.net/zheng0518/article/details/45012633