JavaSE|多线程

多线程概述

引入

多线程程序引入
假如一个程序只有一条执行路径,那么该程序就是单线程程序。
假如一个程序有多条执行路径,那么该程序就是多线程程序。

多线程概述

要想说线程,首先必须得聊聊进程,因为线程是依赖于进程存在的。那么,什么是进程呢?通过任务管理器我们就可以看到进程的存在。

什么是进程?

进程就是正在运行的程序,是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。

多进程有什么意义?

单进程计算机只能做一件事情。而我们现在的计算机都可以一边玩游戏(游戏进程),一边听音乐(音乐进程),所以我们常见的操作系统都是多进程操作系统。比如:Windows,Mac和Linux等,能在同一个时间段内执行多个任务。

那么对于单核计算机来讲,游戏进程和音乐进程是同时运行的吗?不是。因为CPU在某个时间点上只能做一件事情,计算机是在游戏进程和音乐进程间做着频繁切换,且切换速度很快,所以,我们感觉游戏和音乐在同时进行,其实并不是同时执行的。

多进程的作用不是提高执行速度,而是提高CPU的使用率

什么是线程?

在一个进程内部又可以执行多个任务,而这每一个任务我们就可以看成是一个线程。线程是程序中单个顺序的控制流,是程序使用CPU的基本单位

多线程有什么意义?

多线程的作用不是提高执行速度,而是为了 提高应用程序的使用率

多线程却给了我们一个错觉:让我们认为多个线程是并发执行的。其实不是。因为多个线程共享同一个进程的资源(堆内存和方法区),但是栈内存是独立的,一个线程一个栈。所以他们仍然是在抢CPU的资源执行。一个时间点上只有能有一个线程执行。而且谁抢到,这个不一定,所以,造成了线程运行的随机性。

什么是并发?

注意两个词汇的区别:并行和并发。

  • 并行是逻辑上同时发生,指在某一个时间内同时运行多个程序。
  • 并发是物理上同时发生,指在某一个时间点同时运行多个程序。

那么,我们能不能实现真正意义上的并发呢,是可以的,多个CPU就可以实现,不过你得知道如何调度和控制它们。

举例

扫雷游戏是一个进程,在扫雷游戏中有倒计时线程、游戏线程等。

Java程序运行原理

java 命令会启动 java 虚拟机,启动 JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。在此之前的所有程序都是单线程的。

思考:JVM的启动是单线程的还是多线程的?
JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。

小结

  • 进程就是正在运行的程序,多进程的作用是提高CPU的使用率
  • 线程是程序使用CPU的基本单位。多线程的作用是为了 提高应用程序的使用率
  • 线程是抢CPU执行的,运行具有随机性
  • JVM的启动是多线程的,至少启动了垃圾回收线程和主线程

多线程实现有两种方法:

  • 继承Thread类
  • 实现Runnable接口

Thread 类

通过继承Thread类实现多线程步骤:

  1. 自定义类MyThread继承Thread类
  2. 在MyThread类中重写run()方法
  3. 创建MyThread类的对象
  4. 启动线程对象

问题:

  1. 为什么要重写run()方法
    run()方法里面封装的是被线程执行的代码
  2. 启动线程对象用哪个方法?
    start()
  3. run()和start()方法的区别?
    run()直接调用仅仅是普通方法。
    start()先启动线程,再由JVM调用run()方法

线程调度

假如我们的计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,线程只有得到 CPU时间片,也就是使用权,才可以执行指令。那么Java是如何对线程进行调用的呢?

线程的两种调度模型

  • 分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片。
  • 抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。
  • Java使用的是抢占式调度模型

如何设置和获取线程优先级

  • public final int getPriority():返回线程对象的优先级
  • public final void setPriority(int newPriority):更改线程的优先级。
    在设置优先级时如果不在Java规定的线程优先级范围内,会报错 IllegalArgumentException(非法参数异常),表明向方法传递了一个不合法或不正确的参数。

注意事项:

  • 线程默认优先级是5。
  • 线程优先级的范围是:1-10。
  • 线程优先级高仅仅表示线程获取的 CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到比较好的效果。
public class ThreadPriorityDemo {
	public static void main(String[] args) {
		ThreadPriority tp1 = new ThreadPriority();
		ThreadPriority tp2 = new ThreadPriority();
		ThreadPriority tp3 = new ThreadPriority();

		tp1.setName("东方不败");
		tp2.setName("岳不群");
		tp3.setName("林平之");

		// 获取默认优先级
		// System.out.println(tp1.getPriority());
		// System.out.println(tp2.getPriority());
		// System.out.println(tp3.getPriority());

		// 设置线程优先级
		// IllegalArgumentException(
		// tp1.setPriority(100000);
		
		//设置正确的线程优先级
		tp1.setPriority(10);
		tp2.setPriority(1);

		tp1.start();
		tp2.start();
		tp3.start();
	}
}

public class ThreadPriority extends Thread {
	@Override
	public void run() {
		for (int x = 0; x < 100; x++) {
			System.out.println(getName() + ":" + x);
		}
	}
}

线程控制

线程休眠

public static void sleep(long millis)

线程加入

public final void join():等待该线程终止。


public class ThreadJoinDemo {

	public static void main(String[] args) {
		ThreadJoin tj1 = new ThreadJoin();
		ThreadJoin tj2 = new ThreadJoin();
		ThreadJoin tj3 = new ThreadJoin();
		
		tj1.setName("李渊");
		tj2.setName("李世民");
		tj3.setName("李元霸");
		
		tj1.start();
		try {
			tj1.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		tj2.start();
		tj3.start();

	}
}

public class ThreadJoin extends Thread {
	
	@Override
	public void run() {
		for(int i = 0; i < 100; i++){
			System.out.println(this.getName() + ":" + i);
		}
	}

}

执行结果如下:
tj1执行完后,tj2和tj3才开始执行。
在这里插入图片描述

线程礼让

public static void yield():暂停当前正在执行的线程对象,并执行其他线程。

此静态方法可以让多个线程执行更和谐,但不能保证一人一次。

public class ThreadYieldDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		ThreadYield ty1 = new ThreadYield();
		ThreadYield ty2 = new ThreadYield();
		
		ty1.setName("林青霞");
		ty2.setName("刘意");
		
		ty1.start();
		ty2.start();
	}
}

public class ThreadYield extends Thread {
	@Override
	public void run() {
		for(int i = 0; i < 100; i++){
			System.out.println(this.getName() + ":" + i);
			Thread.yield();
		}
	}
}

后台线程

public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
该方法必须在启动线程前调用

public class ThreadDaemonDemo {

	public static void main(String[] args) {

		ThreadDaemon td1 = new ThreadDaemon();
		ThreadDaemon td2 = new ThreadDaemon();
		
		td1.setDaemon(true);
		td2.setDaemon(true);
		
		td1.setName("张飞");
		td2.setName("关羽");
		
		td1.start();
		td2.start();
		
		Thread.currentThread().setName("刘备");
		for(int i = 0; i < 10; i++){
			System.out.println(Thread.currentThread().getName() + ":" + i);
		}		
	}
}

public class ThreadDaemon extends Thread {
	@Override
	public void run() {
		for(int i = 0; i < 100; i++){
			System.out.println(this.getName() + ":" + i);
		}
	}
}

执行结果如下:
刘备执行完了以后,张飞和关羽也停止执行了(张飞和关羽都没有执行到99)。没有立即停是因为要把那个时间片用完。
在这里插入图片描述
类似坦克大战:
在这里插入图片描述

中断线程

public final void stop()
强迫线程停止执行。已过时,但仍可以使用。

public class ThreadStopDemo {
	public static void main(String[] args) {
		ThreadStop ts = new ThreadStop();
		ts.start();

		// 你超过三秒不醒过来,我就终止你。
		try {
			Thread.sleep(3000);
			ts.stop();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

public class ThreadStop extends Thread {
	@Override
	public void run() {
		System.out.println("开始执行:" + new Date());

		// 我要休息10秒钟,亲,不要打扰我哦
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.out.println("结束执行:" + new Date());
	}
}

执行结果如下:
执行了3s,程序就被终止了,jvm退出,走不到“结束执行”那句话。
在这里插入图片描述
public void interrupt()
中断程序。把线程终止,并抛出一个InterruptedException。

public class ThreadStopDemo {
	public static void main(String[] args) {
		ThreadStop ts = new ThreadStop();
		ts.start();

		// 你超过三秒不醒过来,我就干死你
		try {
			Thread.sleep(3000);
			// ts.stop();
			ts.interrupt();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

public class ThreadStop extends Thread {
	@Override
	public void run() {
		System.out.println("开始执行:" + new Date());

		// 我要休息10秒钟,亲,不要打扰我哦
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.out.println("结束执行:" + new Date());
	}
}

执行结果如下:
在这里插入图片描述

线程的生命周期

在这里插入图片描述

Runnable接口

通过实现Runnable接口实现多线程步骤:

  1. 自定义类MyRunnable实现Runnable接口
  2. 重写run()方法
  3. 创建MyRunnable类的对象
  4. 创建Thread类的对象,并把上一步的对象作为构造参数传递

问题:
有了继承Thread类实现多线程的方式,为什么还需要通过实现Runnable接口实现多线程的方式呢?

  1. 可以避免由于Java单继承带来的局限性。
  2. 适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想。
public class MyRunnableDemo {

	public static void main(String[] args) {

		MyRunnable my = new MyRunnable();
		
		Thread t1 = new Thread(my, "林青霞");
		Thread t2 = new Thread(my, "刘意");
		
		t1.start();
		t2.start();		

	}
}

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		for(int i = 0; i < 100; i++){
			System.out.println(Thread.currentThread().getName() + ":" + i);
		}		
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值