主要内容
程序、进程、线程的概念
Java中多线程的创建和使用
1、继承Thread类与Thread类的主要方法
2、实现Runable接口
3、线程的调度与设置优先级
一、程序、进程、线程的概念
1、程序(program):可以理解为一段静态的代码,静态对象。是为了完成特定任务、用某种语言编写的一组指令的集合。
2、进程(process):它是内存中的一段独立的空间,可以负责当前应用程序的运行。当前这个进程负责调度当前程序中的所有运行细节。
进程是一个动态过程,有它自己的产生、存在和消亡的过程。
3、线程(thread):它是位于进程中,负责当前进程中的某个具备独立运行资格的空间。
区别:进程是负责整个程序的运行,而线程是程序中具体的某个独立功能的运行。一个进程中至少应该有一个线程。
4、多线程:
在一个进程中,同时开启多个线程,让多个线程同时去完成某些任务(功能)。
(比如后台服务系统,就可以用多个线程同时响应多个客户的请求)
多线程的目的:提高程序的运行效率。
多线程运行原理:
cpu在线程中做时间片的切换。其实真正电脑中的程序的运行不是同时在运行的。
CPU负责程序的运行,而CPU在运行程序的过程中某个时刻点上,它其实只能运行一个程序。而不是多个程序。
而CPU它可以在多个程序之间进行高速的切换。而切换频率和速度太快,导致人的肉眼看不到。
每个程序就是进程, 而每个进程中会有多个线程,而CPU是在这些线程之间进行切换。
了解了CPU对一个任务的执行过程,我们就必须知道,多线程可以提高程序的运行效率,但不能无限制的开线程。
二、Java中多线程的创建和使用
1、继承Thread类
(1)实例:
package com.TestThread;
/*
* 创建一个子线程,完成1-100之间自然数的输出,同样,主线程也执行这个操作
* 创建多线程的第一种方式:继承java.lang.Thread类
*/
//1、创建一个继承于Thread的子类
class SubThread extends Thread{
//2、重写Thread类的run()方法,方法内实现此子线程要完成的功能
public void run(){
for(int i=1;i<=100;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class TestThread {
public static void main(String[] args) {
//3、创建一个子类的对象
SubThread st1=new SubThread();
SubThread st2=new SubThread();
//4、调用线程的start()方法:启动此线程,调用相应的run()方法
//一个线程只能够执行一次start()方法
//不能通过Thread实现类对象的run()去启动一个线程
st1.start();
//st1.start()
//st1.run()
st2.start();
for(int i=1;i<=100;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
(2)Thread的常用方法
实例:
package com.TestThread;
/*
* Thread的常用方法
* 1.start():启动线程并执行相应的run()方法
* 2.run():子线程要执行的代码放入run()方法
* 3.currentThread():静态的,调取当前的线程
* 4.getName():获取此线程的名字
* 5.setName():设置此线程 的名字
* 6.yield():调用此方法的线程释放当前CPU的执行权
* 7.join():在A线程中调用B线程的join()方法,表示当执行到此方法,A线程停止执行,直至B线程执行完成,A线程再接着join()之后的代码执行
* 8.isAlive():判断当前线程是否还存活
* 9.sleep(long L):显式地让当前线程睡眠L毫秒
* 10.线程通信:wait() notify() notifyAll()
* 设置线程的优先级:时间片,抢占式
* getPriority():返回线程优先值
* setPriority(int new Priority):改变线程的优先级
*/
class SubThread1 extends Thread{
public void run(){
for(int i=1;i<=100;i++){
/*try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class TestThread1 {
public static void main(String[] args) {
SubThread1 st1=new SubThread1();
SubThread1 st2=new SubThread1();
st1.setName("子线程1");
st1.start();
st2.setName("子线程2");
st2.start();
Thread.currentThread().setName("=========主线程");
for(int i=1;i<=100;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
/*if(i%10==0){
Thread.currentThread().yield();
}*/
if(i == 20){
try {
st1.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
System.out.println(st1.isAlive());
}
}
(3)设置线程的优先级:时间片,抢占式
getPriority():返回线程优先值
setPriority(int newPriority):改变线程的优先级
MAX_PRIORITY(10) MIN_PRIORITY(1) NORM_PRIORITY(5)
(4)简单练习
模拟火车站售票窗口,开启三个窗口售票,总票数为100
(此程序存在线程安全问题,打印车票时可能出现重复车票以及错票,这只是一个简单的实现,没考虑线程安全问题)
package com.TestThread;
//模拟火车站售票窗口,开启三个窗口售票,总票数为100
//方式一:继承Thread类
class Window extends Thread{
static int ticket=100;
public void run(){
while(true){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"售出的票号为:"+ticket--);
}else{
break;
}
}
}
}
public class TestWindow {
public static void main(String[] args) {
Window w1=new Window();
Window w2=new Window();
Window w3=new Window();
w1.setName("窗口1");
w2.setName("窗口2");
w3.setName("窗口3");
w1.start();
w2.start();
w3.start();
}
}
2、实现Runable接口
(1)实例:
package com.TestThread;
//方式二:实现Runnable接口
//1、创建一个实现了Runnable接口的类
class PrintNum1 implements Runnable{
//2、实现接口的抽象方法
public void run() {
//子类执行的代码
for(int i=1;i<=100;i++){
if(i%2==0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
public class TestRunnable {
public static void main(String[] args) {
//3、创建一个Runnable接口实现类的对象
PrintNum1 p=new PrintNum1();
//4、将此对象作为形参传递给Thread类的构造器中,创建Thread类的对象,此对象即为一个线程
Thread t1=new Thread(p);
//5、调用start()方法,启动线程并执行run()方法
t1.start();//启动线程,执行Thread对象生成时构造器形参的对象的run()方法
//再创建一个线程
Thread t2=new Thread(p);
t2.start();
}
}
(2)简单练习
模拟火车站售票窗口,开启三个窗口售票,总票数为100
(此程序存在线程安全问题,打印车票时可能出现重复车票以及错票,这只是一个简单的实现,没考虑线程安全问题)
package com.TestThread;
//使用实现Runnable接口方式售票
class Window1 implements Runnable{
int ticket=100;
public void run() {
while(true){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"售出的票号为:"+ticket--);
}else{
break;
}
}
}
}
public class TestWindow2 {
public static void main(String[] args) {
Window1 w=new Window1();
Thread t1=new Thread(w);
Thread t2=new Thread(w);
Thread t3=new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
3、两种方式的对比
(1)联系:Thread类是实现Runnable接口的。
(2)实现Runnable接口的方式优于继承Thread类的方式
理由:a.避免了Java单继承的局限性
b.如果多个线程要操作同一份资源(或数据),更适合使用实现的方式。