程序、进程、线程
-
程序:静态的代码,安装到硬盘上
-
进程:运行中的程序,是操作系统分配资源的最小单位
-
线程:线程是进程(程序)中最小的执行单元,是操作系统进行任务调度的最小单位是CPU进行调度的最小单位.
进程和线程的关系
-
一个进程可以包含多个线程,一个线程只能属于一个进程,线程不能脱离进程而独立运行
-
每一个进程至少包含一个线程(称为主线程);在主线程中开始执行程序, java 程序的入口main()方法就是在主线程中被执行的.
-
在主线程中可以创建并启动其它的线程;
-
一个进程内的所有线程共享该进程的内存资源。
以QQ聊天为例,登录QQ后我们可以打开多个聊天窗口,同时和张三、李四、王五...聊天. 登录QQ就是通过主线程启动了QQ这个进程,相当于进入main()方法.而我们打开多个聊天窗口相当于创建了多个线程,这些线程都隶属于QQ这个进程.如图:
一个简单的线程
创建包:com.ffyc.javathread包
创建类:Sample类
package com.ffyc.javathread;
public class Sample {
//方法1
public void method1(){
System.out.println("method1");
}
//方法2
public void method2(){
method1();
System.out.println("method2");
}
/**
*主方法(主线程),程序的入口
* 这不是多线程案例,是单线程
* @param args
*/
public static void main(String[] args) {
System.out.println("主线程开始");
Sample s = new Sample();
s.method2();
System.out.println("主线程结束");
}
}
这是一个单线程的典例,输出的结果为:
主线程开始
method1
method2
主线程结束
在类中,从main()方法进入程序,首先打印输出:"主线程开始";然后创建了Sample这个类的对象,调用该类里的方法:method2()-->进入该方法,method2()方法调用了方法:method1()-->进入该方法,执行method1()的方法体:打印输出"method1",执行完method1()后,返回method2()中继续向后执行,打印输出:"method2",执行完method2()方法体后返回到主方法,执行面的语句,输出:"主线程结束".如图:
调用的过程可以用下图表示:
显然,整个程序是顺着一条线完成的,这是一个典型的单线程.
创建线程
创建线程有两种方式,我们依次介绍:
-
继承Thread类的方式
-
实现Runnable接口的方式
一、继承Thread类的方式
-
在Java中要实现线程,最简单的方式就是扩展Thread类,重写其中的run方法,方法原型如下:
-
Thread类中的run方法本身并不执行任何操作,如果我们重写了run方法,当线程启动时,它将执行 run方法。
-
main()方法也是一个线程,还是启动程序的主线程,所以下面的main()方法中,start()方法启动ThreadDemo线程后,就有两个线程.这两个线程的运行没有固定的先后顺序,由操作系统的调度决定.
创建包:com.ffyc.javathread.demo1
创建类:test
创建类:ThreadDemo
package com.ffyc.javathread.demo1;
//ThreadDemo类
public class ThreadDemo extends Thread{
/* 要在线程中执行的任务,重写在Thread类的run()方法中,线程要执行的操作*/
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("ThreadDemo"+i);
}
}
}
//Test测试类
public class Test {
/* 启动java程序的主线程*/
public static void main(String[] args) {
/*在主线程中创建线程并启动 */
ThreadDemo td = new ThreadDemo(); //创建线程
/*td.run();
切忌调用run()启动线程
这只是一个普通的方法调用,即ThreadDemo类的对象调用该对象中的方法,不是启动线程
启动线程有特定的方法 : start()
*/
td.start();
/*start()方法才是启动线程的方法,底层调用start0()方法,这是一个native方法(本地方法),由操作系统分配*/
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
}
}
}
.***run()方法重写的内容就是线程启动后要执行的内容.main()方法执行时:
-
创建了一个ThreadDemo对象,此时是创建了一个线程(ThreadDemo这个类继承(extends)了Thread类,重写了run()方法)
-
调用Start()方法启动线程
-
此时main()方法的主线程和刚启动的另一个线程由CPU调度执行,没有固定的先执行哪个,后执行哪个
-
main线程要执行的是:
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
}
td线程要执行的是run()方法中的:
for (int i = 0; i < 1000; i++) {
System.out.println("ThreadDemo"+i);
}
他们交替执行的结果可以自己运行看一下.
二、实现Runnable接口的方式
1. java.lang.Runnable接口中仅仅只有一个抽象方法:
public void run();
2. 也可以通过实现Runnable接口的方式来实现线程,只需要实现其中的run方法即可;
3. Runnable接口的存在主要是为了解决Java中不允许多继承的问题
如果使用继承(extends)Thread类的方式实现创建一个进程,那么这个类将不能再继承其他的类,但是如果是实现接口,那么可以实现多个接口,且不影响继承想继承的类.
***实现runnable接口的方法:
public class MyThread implements runnable{
@Override
public void run(){
方法体;
}
}
***创建接口的关键语句:
MyThread r = new MyThread();
//创建一个对象
Thread thread = new Thread(r);/*创建一个线程作为外壳,将r包起来*/
thread.start();/*启动线程*/
二、举个例子
创建包:package com.ffyc.javathread.demo2
创建类:Test类
创建类:MyThread类
package com.ffyc.javathread.demo2;
public class MyThread implements Runnable {
/*
* 我们自己写的MyThread类实现Runnable接口,这个不叫线程类
* 只能叫线程要执行的任务
* MyThread myThread = new MyThread();
* myThread . 不出start方法,无法启动线程
* */
@Override
public void run() {
/*输出的内容:用线程类Thread调用currentThread()方法获取当前的线程,
再调用getName()方法获取该线程的名字+获取到的线程得我优先级*/
System.out.println(Thread.currentThread().getName()+Thread.currentThread().getPriority());
}
}
package com.ffyc.javathread.demo2;
/*
线程的默认优先级是5,但是也可以通过get
*/
public class Test {
public static void main(String[] args) {
/*MyThread myThread = new MyThread();
myThread. 不出start方法,无法启动线程
*/
MyThread myth = new MyThread();//创建对象
Thread th = new Thread(myth,"窗口1");
//创建了一线程,并为这个线程分配了一个任务,创建线程名称:"窗口1"
th.start();//启动线程
Thread th2 = new Thread(myth); //创建第二个线程
th2.setName("窗口2"); //用setName()方法为线程命名:"窗口2"
th2.start(); //启动线程th2
Thread.currentThread().setPriority(10);获取当前线程,并给它设置优先级:10
th.setPriority(8);//给th线程设置优先级 8
th2.setPriority(6);//给th2设置优先级 6
System.out.println("main"+Thread.currentThread().getPriority());
//输出main方法(主线程)的优先级
}
}
上面的部分main()方法中有3个线程:main()方法的主线程,th线程和th2线程.三者的先后顺序也是有CPU的调度决定的.
线程的常用类
1. 构造方法; //构造方法之间的调用 用的是this(所调用的构造方法的参数列表)
2. run(); 用于线程执行的任务
3. start(); 用于启动线程
4. Thread.currentThread() 用于获得当前线程的引用
5. getName(); 获得线程的名字
7. setName();给线程命名
常用类请大家自己尝试使用~