相关知识:
什么是多线程 ?
线程实现的两种方式 ?
线程的调度 ?
多线程的互斥 ?
进程与线程:
并发执行:多个程序块同时运行的现象被称为并发执行。
进程:简单地说,在多任务系统中,每个独立执行的程序称为进程,也就是“正在运行的程序”。
线程:是程序中的一条执行路径。在一个程序中可以同时运行多个不同的线程来执行不同的任务。
多线程是指程序中包含多条执行路径。
多线程是Java语言的一大特性。多线程允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立。
注意:
一个进程中至少有一个线程。
Java VM 启动的时候会有一个进程javaw.exe.
该进程中至少一个线程负责java程序的执行。
而且这个线程运行的代码存在于main方法中。
该线程称之为主线程。
进程是包含线程的
多线程基本概念:
进程与线程的区别:
进程是程序的一次动态执行过程。每个进程都有自己独立的一块内存空间。线程是指进程中的一个执行流程,通常在一个进程中可以包含若干个线程,它们共享进程所拥有的资源。
在引入线程的操作系统中,通常把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不占有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度。
线程实现的两种方式 :
线程的创建包括定义线程体和创建线程对象并启动线程两个方面的内容。线程的行为由线程体决定,线程体是在Runnable接口中的run()方法中定义的。
由于Thread类实现了Runnable接口,即包含了run()方法的定义,因此可以通过继承Thread类或实现Runnable接口两种途径来定义线程体。
方法一 继承Thread类:
1、定义Thread类的子类
public class ThreadSubclassName extends Thread{
public ThreadSubclassName(){
..... // 编写子类的构造方法
}
public void run(){
..... // 定义线程体
}
}
2、用定义的线程子类ThreadSubclassName创建线程对象:
ThreadSubclassName threadObject =
new ThreadSubclassName();
3、启动该线程对象表示的线程:
threadObject.start(); //启动线程
创建线程的步骤:
- 定义类继承Thread。
- 重写Thread类中的run方法。目的:将自定义代码存储在run方法,让线程运行。
- 创建子类的实例就是创建一个线程。
- 调用线程的start方法,该方法两个作用:启动线程,调用run方法。
案例—— 用Thread类实现多线程:
package thread;
//一个进程包含一个或者多个线程
//线程结束但是进程不一定结束,进程结束那么线程就结束
//多线程同时并发多个过程
//线程与线程之间是独立的
public class __1_多线程启动与名字设置 {
public static void main(String[] args) {
MyThread mt = new MyThread("one");
MyThread mt2 = new MyThread("two");
//启动多线程(多出一条路径,与主路径同时执行)------每次运行结果都不同,cpu分配不同,进行资源的抢夺
//start方法启动线程同时运行run方法
mt.start();
mt2.start();
for(int i = 0 ; i < 100 ; i++)
System.out.println("方法 "+i);
}
}
class MyThread extends Thread{//已经重写了run方法,但还是最好写出来
//想让这个对象有一个名字(不同的线程用不同的名字加以区分)
// private String name;
MyThread(String name){
// this.setName(name);//设置线程的名称(更改默认的线程的名称)
super(name);
}
public void run() {
for(int i = 0 ; i < 100 ; i++)
// System.out.println(name+" "+i);
//this.getName() 获取线程的名称(默认的取名字的方法)
// System.out.println(this.getName()+" "+i);
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
上面的代码运行结果会出现3次数值1~100
因为有两次start进行两条线程的运行,而且main()方法主函数中也存在一个for循环进行输出
注意:一个线程对象只能启动一个线程,无论你调用多少遍start()方法,结果都只有一个线程
举例:
package thread;
public class _2_多窗口卖火车票 {
public static void main(String[] args) {
huoCar car1 = new huoCar("窗口1:");
huoCar car2 = new huoCar("窗口2:");
huoCar car3 = new huoCar("窗口3:");
huoCar car4 = new huoCar("窗口4:");
car1.start();
car2.start();
car3.start();
car4.start();
}
}
class huoCar extends Thread{
private static int ticket = 100;
huoCar(String name){
super(name);
}
public void run() {
while(true) {
if(ticket>0) {
System.out.println(Thread.currentThread().getName()+"售出"+ticket +"号票🎫");
ticket--;
// System.out.println(Thread.currentThread().getName()+ticket);
}else
break;
}
}
}
上面huoCar类中,使用了全局变量static,故在主函数中,四个窗口共同售卖100张票,如果不使用static全局变量的话,那么每一个窗口即每一个线程都会分别售卖100张票
但是,结果会出现下面的情况:
所以,这就涉及到同步锁的问题,后面会讲到。
方法二 实现Runnable接口:
- 定义Runnable接口的子类,重写run()方法 。
class SubThread implements Runnable - 创建子类SubThread的对象
SubThread RunnableObject = new SubThread(); - 用带有Runnable参数的Thread类构造方法创建线程对象
Thread threadObject = new Thread(RunnableObject); - 启动线程对象ThreadObject表示的线程
threadObject.start();
用Runnable接口实现的多线程:
Thread类中带有Runnable接口的构造方法有:
public Thread(Runnable target);
public Thread(Runnable target, String name);
其中,参数Runnable target表示该线程执行时运行target的run()方法,String name以指定名字构造线程。
案例 Runnable接口实现多线程:
package thread;
public class _3_Runnable接口卖火车票 {
public static void main(String[] args) {
MyRun mr = new MyRun();
//创建线程对象(每一个线程共用同一个对象)
Thread t1 = new Thread(mr,"1号窗口");
Thread t2 = new Thread(mr,"2号窗口");
Thread t3 = new Thread(mr,"3号窗口");
Thread t4 = new Thread(mr,"4号窗口");
//启动
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class MyRun implements Runnable{
private int ticket = 100;
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
if(ticket>0) {
System.out.println(Thread.currentThread().getName()+"售出"+ticket +"号票🎫");
ticket--;
}else
break;
}
}
}
上面的代码中,没有使用全局变量也能够让四个窗口同时售卖100张票
是因为
MyRun mr = new MyRun();
Thread t1 = new Thread(mr,“1号窗口”);
Thread t2 = new Thread(mr,“2号窗口”);
Thread t3 = new Thread(mr,“3号窗口”);
Thread t4 = new Thread(mr,“4号窗口”);
上面的代码中传入的是同一个类的对象,也是共同售卖同100张票
Thread与Runnable两种方法的比较:
实现runnable接口相对于继承Thread类来说,具有以下优点:
适合多个相同程序代码的线程去处理同一资源的情况。
可以避免由于Java单继承特性带来的局限。
有利于程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。