版权声明:嘤嘤嘤,小白的东西,大牛们应该看不上吧。 https://blog.csdn.net/qq_37384180/article/details/83304968
一、线程和进程的概念
进程:正在执行的程序。应用程序在内存中开辟的空间。
线程:进程的执行单位。负责程序中进程的运行。每个进程至少有一个线程,允许进程中有多个线程。称为多线程。此程序称为多线程程序。
程序启动了多线程,可以实现多功能程序同时进行。也称并发。
【例子】平常看到的多线程
360中我们可以在杀毒的同时,进行垃圾清理。如果是单线程,就必须杀毒后,再垃圾清理。
多线程,其实就是合理的分配cpu资源。电脑之所以能同时运行聊天,音乐,游戏也是因为多线程。但如果多线程多了,比如我开了LOL,DOTA2,CF,DNF,WOW那么,经常就能看到程序未响应,是因为CPU在执行任务的时候忙不过来,过度的分配了cpu的资源,也就是说线程如果多到一定程度,就会降低性能。
为什么要用到多线程?
public class Demo{
public static void main(String[] args){
show();
System.out.println("Hello World")
}
public static void show(){
for(int x=1;x<=10000000;i++){
System.out.prinltn(i);
}
}
}
如果此时我要先执行show(),在执行到Hello World就要有个漫长的时间。现实中有很多例子,就比如考试的时候,做数学题,第一题就是奥数思考题,做出来的人很稀少。如果把时间花在这上面,后面的题就来不及做了。
所以就跟以上程序一样,如果遇到了循环而导致了在指定的任务上停留时间过长,无法执行剩余代码。就需要用到多线程来执行其他代码。
二、线程的定义
定义线程目前学到的有两种:
一、继承Thread类:
1.子类继承Thread类:可以直接用Thread类,但是里面的Thread构造函数如果不传值。那就是执行空代码。至于里面传的什么值,到Runnable再细讲,所以如果不重写Thread的run方法,就会执行空代码。并不是我们想要的线程任务。
2.重写run方法
3.创建子类对象:就是创建线程。Thread类是线程,那么子类对象也是线程。
4.调用start方法,开启并让线程执行,同时jvm还会去执行run方法
【例子】
public class Thread1 extends Thread{
@override
public void run(){
for(int i=0;i<=5;i++){
System.out.println(Thread.currentThread.getName()+":"+i);
}
}
}
public class Demo{
public static void main(String[] args){
Thread1 t1=new Thread1();
Thread1 t2=new Thread1();
t1.start();
t2.run();
}
}
运行结果:
main: 1
Thread-0:1
main: 2
Thread-0:2
main: 3
main: 4
main: 5
Thread-0:3
Thread-0:4
Thread-0:5
先创建t1和t2两个线程对象,之后t1线程start()运行,且jvm调用run(),而t2.run()只是单纯的调用函数,并没有调用start()运行线程,所以此时t1的线程为主线程。
如果t2.run()和t.start()顺序相反一下,那么就会按照正常的执行顺序。
主线程:jvm启动后,必会有一个线程从main()执行,在main()结束。此线程成为主线程。
主线程内容先执行,因为主线程因为有main(),先占用了cpu资源,先执行主线程。当然也可以设置优先级。
static Thread |
currentThread() 返回对当前正在执行的线程对象的引用。 |
String |
getName() 返回该线程的名称。 |
线程之间,互不影响
public class Thread1 extends Thread{
@override
public void run(){
int[] arr=new int[3];
System.out.println(arr[4]);
}
}
public class Thread2 extends Thread{
@override
public void run(){
System.out.println(Thread.currenThread.getName());
}
}
public class Demo{
public static void main(String[] args){
Thread1 t1=new Thread1();
Thread2 t2=new Thread2();
t1.start();
t2.start();
}
}
Thread-1
Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: 4
at com.Thread.Thread1.run(Thread1.java:8)
二、实现Runnable接口:优于Thread
1.子类实现Runnble接口:避免了继承Thread类的局限性。
例如:360安全卫士需要多线程,继承了Thread类,而360安全卫士也要继承于父类“杀毒软件”,所以这时候需要实现接口。
public class Thread extends Object implements Runnable
在API中,Thread类其实就是实现了Runnable接口,并且重写run()
2.重写run方法
3.创建Thread对象:因为Runnable是接口无法创建对象,其子类Thread类是线程对象。
4. 把Runnable作为参数传递给Thread的构造函数
5.调用start方法开启线程
API文档中有这么一段话:
设计该接口的目的是为希望在活动时执行代码的对象提供一个公共协议。例如,
Thread
类实现了Runnable
。激活的意思是说某个线程已启动并且尚未停止。
Runnable
为非Thread
子类的类提供了一种激活方式。通过实例化某个Thread
实例并将自身作为运行目标,就可以运行实现Runnable
的类而无需创建Thread
的子类。大多数情况下,如果只想重写run()
方法,而不重写其他Thread
方法,那么应使用Runnable
接口。这很重要,因为除非程序员打算修改或增强类的基本行为,否则不应为该类创建子类。
以上那段话的意思,按本人理解差不多就是:
Runnable接口的目的就是为那些正在执行代码的对象提供了一个公共协议:某个线程已经启动,并且未停止。
Thread为Runnable的子类,但是有局限性。那么如果不是Thread子类的类呢,如果想要激活此公共协议,那么只要先实现Runnable,实例化Thread的类将自身作为运行目标(作为参数),就可以不创建Thread子类了。
大多数情况下,如果只是重写run方法(只是重写了线程功能而已),不需要重写其他Thread类方法,那么就该实现Runnable接口,这很重要,因为除非程序员打算(修改或增强Thread类的基本行为),否则不应该为Thread类派生子类。
简而言之:就是本来如果不想为Thread类增强基本行为,只是想重写run()方法。那么就不应该继承Thread类,而是去实现Runnable的接口。
把Runnable作为参数传递给Thread的构造函数
之前Thread没说清楚的,放在这里比较合适。
Thread类中有一个run方法,里面写的内容:如果target对象不为空,那么就运行target的run方法。
那么target是什么呢?
源代码里写着是Runnable类的引用变量,并且上面写着what will be run,也就是说target就是运行的内容。
那么target从哪来呢?在Thread的有参构造函数中,会传入init方法中
所以,就是说我们先利用子类实现Runnble后,创建对象,利用多态传参,并且将此对象通过init方法赋给Thread类中target。之后run方法调用的就是这个taget
【例子】
通过售货机卖票如果要卖1000张票。
package com.ThreadTest;
public class Ticket implements Runnable{
private boolean state=true;
private int ticket;
public void setTicket(int ticket) {
this.ticket = ticket;
}
public int getTicket() {
return ticket;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(state) {
if (ticket>0) {
System.out.println(Thread.currentThread().getName()+":"+ticket--);
}else {
state=false;
}
}
}
}
package com.ThreadTest;
public class Demo {
public static void main(String[] args) {
Ticket t=new Ticket();
t.setTicket(1000);
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}