谈到线程,不得不先要理解什么是进程。进程是一个正在执行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫做一个控制单元。线程(Thread)就是进程中的一个独立的控制单元,线程在控制着进程的执行。一个进程中至少有一个线程,即至少有一个控制单元。
Java虚拟机启动时会有一个进程java.exe,该进程中至少一个线程负责java程序的执行, 而且该线程称之为主线程。需要强调的是JVM在启动时不止有一个进程,还有负责
垃圾回收机制的线程。
如何在自定义的代码中,自定义一个线程 呢?线程存在于某一个进程当中,进程是系统创建的,所以Java中已经提供了此类事物的体现,java.lang包中有一个Thread类。
创建线程的一种方式就是:
1. 定义类继承Thread类
2. 重写(覆盖)Thread中的run( )方法。目的是:将自定义的代码存储在run方法中,让线程运行
3. 调用线程的start( )方法。 该方法有两个作用:启动线程,调用run()方法
示例代码1:
package com.itheima.day11;
class Demoextends Thread{
public void run(){
for (int x = 0; x < 500; x++) {
System.out.println("demo run---"+x);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
//继承完之后,创建对象,即创建好了一个线程
Demo d = new Demo();
//开启线程并执行该线程的run()方法,start是调用的底层,让控制台执行的动作
d.run();
for (intx = 0; x < 500; x++) {
System.out.println("Hello World!--"+x);
}
}
}
发现:
每一次的运行结果都不同,因为多个线程都在获取CPU的执行权,CPU执行到哪个进程的线程,就会执行哪一个进程
明确一点:在某一时刻,只能有一个程序在运行(多核除外)
原理:
CPU在做着快速的切换,以达到看上去同时运行的效果
这就是多线程的一个特性:随机性(哪个程序抢到CPU资源,它就会执行,执行多长时间,由CPU决定)
事实:
Windows本身是一个多任务操作系统,表面看似在同时执行多个程序,其实,CPU在某一个时间内只能执行一个程序,它是在多个程序之间进行快速的切换,速度非常之快,所以会认为是同时执行,其实是在切换每一个进程中的线程,若一个进程中有多个线程,CPU也在进行切换,这就是为什么同一时间内开的程序越多,电脑就会运行的
越慢(多个线程在抢夺CPU资源,但是是有CPU决定的),但是若为双核,多核,就能够实现同时执行,双核多核
以后内存就是 瓶颈,临时存储空间要足够大,双核标配内存要在2G以上
run( )和start( )的特点:
Thread类用于描述线程,该类定义了一个功能,即run()方法,用于存储线程要运行的代码。
主线程运行的代码,存储在main()方法中,这是由JVM定义的,这就是虚拟机调用main()方法的原因
因为是主线程在调用.Java中规定了自定义的线程的代码,存储在Thread的run()方法中 start()调用的是 Thread类
所属对象的run()方法,要沿袭父类功能,重写父类内容
调用start()和run() 的不同 :
A. d.start();--->开启线程并执行该线程的run()方法,start是调用的底层,让控制台执行的动作.打印结果是
交替打印
B. d.run();---->仅仅是对象调用方法,而线程创建了,并没有执行.(仅仅是封装了线程要运行的代码).打印结果是
先将run中封装的代码执行完之后再执行start的代码,没有启动主线程
创建线程的第二种方式:实现Runnable接口
步骤:
1. 定义类实现Runnable
2. 覆盖Runnable接口中的run()方法
3. 通过 Thread类创建线程对象
4. 将Runnable接口的子类对象传递给Thread类的构造函数
为什么要将Runnable接口的子类对象传递给Thread的构造函数呢?
因为,自定义的run()方法所属的对象是Runnable接口的子类对象
所以要让线程指定对象的run()方法,就必须明确该run方法所属对象
5. 调用Thread类的start()方法,开启线程并调用Runnable接口子类的run()方法
实现方式和继承方式有什么不同呢?
实现方式好处:避免了单继承的局限性
在定义线程时,建议使用实现方式
存放线程代码位置不同:
继承Thread:线程代码存放在Thread子类的run()方法中
实现Runnable,线程代码存放在接口的子类run()方法中
注意:
Thread类本身也实现Runnable接口,Runnable的定义其实就是在确线程运行代码所存放的位置
示例代码2:
package com.itheima.day11;
class Ticket1 implements Runnable{
private int tick = 100;
public void run(){
while(true){
if(tick>0){
System.out.println(Thread.currentThread().getName()+" sale:"+tick--);
}
}
}
}
public class ThreadRunnableDemo {
public static void main(String[] args) {
Ticket1 t = new Ticket1();
//创建了一个线程
Thread t1 = new Thread(t);
Thread t2 = newThread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
/**
* 分析:
*作用是开启线程,调用的是Thread类中的run()方法,但是此时的Ticket3实现的是
*Runnable接口,其中也有run方法,此时Thread中的run()中没有卖票的代码
*/
t1.start();
t2.start();
t3.start();
t4.start();
}
}