进程
- 概述:进程就是正在运行的应用程序,使系统进行资源分配和调用的独立单位,每一个进程都有它自己的内存空间和系统资源
- 多进程的意义:我们使用的计算机可以一边玩游戏,一边听音乐,而这就是多进程的体现
那么,对于单核计算机而言,游戏进程和音乐进程使同时运行的吗?
不是,同时运行只是我们的感觉,CPU在同一时刻只能执行一个进程,之所以会有这种感觉,是因为CPU在这些进程间不断的高速切换,而这种速度使我们体会不到的
多进程的作用就是提高CPU的使用率
线程
- 概述:在一个进程内部会执行很多个任务,而每一个任务就代表一个线程,线程是CPU调度的基本单位,依赖于进程存在
- 多线程的意义:程序在运行期间,会抢占CPU的执行权,多线程在抢占到CPU执行权的概率会比单线程大,也就是说,多线程执行的时间比单线程多,所以就提高的程序的使用率,至于回事哪个线程抢占到执行权是不确定的,所以多线程具有随机性
- 线程的多种状态
并发和并行
并发:类似于多线程的原理,逻辑上同时发生,指在某一个时间内同时运行多个程序
并行:物理上同时发生,指在某一个时间点同时运行多个程序
多线程
- 多线程创建方式一:继承Thread类,重写run方法
package org.westos.demo2;
//定义一个类继承Thread
public class MyThread extends Thread{
//重写run方法,一般run方法里写的是耗时操作
@Override
public void run() {
//模拟耗时操作
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
package org.westos.demo2;
public class MyTest {
public static void main(String[] args) {
//创建子类对象
MyThread myThread = new MyThread();
//调用start方法开启线程
myThread.start();
}
}
这里注意,获得对象后,不能直接调用run方法,这不是启动线程,只是普通的方法调用,要想开启线程,要通过调用start方法,开启线程后,执行的是MyThread类里的run方法
- Thread类的相关方法
public final String getName():获取线程名称
public final void setName(String name):设置线程名称
public static Thread currentThread():获取当前执行的线程
package org.westos.demo2;
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//获取线程对象
Thread thread = Thread.currentThread();
//调用getName获取线程名称
String name = thread.getName();
System.out.println(name+":"+i);
}
}
}
package org.westos.demo2;
public class MyTest {
public static void main(String[] args) {
//调用currentThread方法获取当前线程对象
Thread thread = Thread.currentThread();
thread.setName("主线程");
String name = thread.getName();
System.out.println(name);
//创建子类对象
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
MyThread myThread3 = new MyThread();
//调用setName设置线程名字
myThread1.setName("线程1");
myThread2.setName("线程2");
myThread3.setName("线程3");
myThread1.start();
myThread2.start();
myThread3.start();
}
}
public final int getPriority() :获取线程的优先级
public final void setPriority(int newPriority):设置线程的优先级
package org.westos.demo2;
public class MyTest {
public static void main(String[] args) {
//线程存在两种调度模型
//分时调度模型:给每个线程平均配分CPU时间片
//抢占式调度模型:优先级高的先使用,如果相同,会随机选择一个,Java使用的就是这种模型
//创建子类对象
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
//调用setPriority方法设置线程优先级
myThread1.setPriority(Thread.MAX_PRIORITY);
//如果不设置优先级,默认为5,优先级范围是1-10
//调用getPriority方法获取优先级
int priority = myThread2.getPriority();
System.out.println(priority);
myThread1.start();
myThread2.start();
}
}
同样run方法里重写100次循环,可以看出,第一个线程抢占的多一些,因为第一个线程设置了优先级为10,而第二个线程是默认的优先级,也就是5
优先级的大小仅仅代表抢到CPU执行权的概率变大了,并不一定就非先执行优先级高的,所以以后遇到线程并不是按照优先级高的线程执行就不用感到奇怪了
public static void sleep(long millis) :让线程处于休眠状态
package org.westos.demo2;
public class MyTest {
public static void main(String[] args) throws InterruptedException {
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread1.start();
//开启第一个线程之后
//调用sleep方法让线程休眠一定时间,单位是毫秒
Thread.sleep(2000);
myThread2.start();
}
}
由运行结果可知第一个线程运行完后,才运行第二个线程,是因为在第一个线程运行完之后线程进入休眠,设定的时间是2秒,这2秒够第一个线程完成循环,而休眠时间到之后,继续执行之后的代码,也就是执行第二个线程
public final void join():加入线程,也就是执行完该线程才执行其他线程
package org.westos.demo2;
public class MyTest {
public static void main(String[] args) throws InterruptedException {
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread1.start();
//调用join方法让之前的线程先执行完
myThread1.join();
myThread2.start();
}
}
跟上述运行结果相同,但原理不同,这次是因为先执行完第一个线程,在执行第二个线程
注意:join方法用在线程开启后
public static void yield(): 暂停当前正在执行的线程对象,并执行其他线程
package org.westos.demo2;
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//调用yield方法礼让线程
Thread.yield();
System.out.println(this.getName()+":"+i);
}
}
}
从运行结果来看好像没什么区别,其实是因为礼让线程只是让当前线程暂停执行,但这个暂停时间是很短的,之后还可以继续跟第二个线程抢占CPU时间片,而抢占时间片是随机的
public final void setDaemon(boolean on):将该线程标记为守护线程
package org.westos.demo2;
public class MyTest {
public static void main(String[] args) throws InterruptedException {
//主线程为用户线程
Thread thread = Thread.currentThread();
thread.setName("主线程");
String name = thread.getName();
System.out.println(name);
//当用户线程执行完毕,守护线程立马dead
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
//标记守护线程,调用在开启线程之前
myThread1.setDaemon(true);
myThread2.setDaemon(true);
myThread1.start();
myThread2.start();
}
}
在所有用户线程运行完之后,守护线程不管是否在运行,都会马上结束
public final void stop():停止线程的运行
public void interrupt():打断线程的阻塞状态
package org.westos.demo2;
public class MyThread extends Thread{
@Override
public void run() {
//让线程休眠,其实就是处于阻塞状态
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {
if(i>50){
//调用stop方法让线程死亡,不推荐使用
this.stop();
}
System.out.println(this.getName()+":"+i);
}
}
}
package org.westos.demo2;
public class MyTest {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
myThread.start();
//调用interrupt打断阻塞状态
myThread.interrupt();
}
}
按理说,开启线程要等休眠时间过才可以执行,但运行代码可得不需要等待,那是因为调用了interrupt方法打断了阻塞
- 多线程创建方式二:实现Runnable接口,重写run方法
这种方式的好处是扩展性强,还可以再去继承其他类
package org.westos.demo2;
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
package org.westos.demo2;
public class MyTest {
public static void main(String[] args) {
//先创建子类对象
MyRunnable myRunnable = new MyRunnable();
//创建Thread对象,将实现接口类对象当作参数传入
Thread thread = new Thread(myRunnable);
//同样可以调用Thread类的方法
thread.setName("支线程");
thread.start();
}
}
- 多线程创建方式三:实现Callable接口,重写call方法
这种方式的好处是可以有返回值,且可以抛出异常
package org.westos.demo2;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
int num;
public MyCallable(int i){
num=i;
}
@Override
public Integer call() throws Exception {
//实现100以内的累加,返回总和
int sum = 0;
for (int i = 0; i <= num; i++) {
sum+=i;
}
return sum;
}
}
package org.westos.demo2;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class MyTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable(100);
FutureTask<Integer> task = new FutureTask<>(myCallable);
Thread thread = new Thread(task);
thread.start();
//获取累加之后的返回值
Integer integer = task.get();
System.out.println(integer);
}
}
创建多线程的方式有三种,选择哪个方式,根据个人需求选择