是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程,提高性能。
举个例子,如上图,你正在开开心心的打游戏,旁边有可乐,好烟伺候着,是不是很爽啊~~此时你的右手一会点鼠标,一会又拿烟,一会又在取可乐,忙的不亦说乎,假如你的右手足够快,上面三种动作相当于同时进行,你的右手就相当于电脑的 CPU,鼠标,好烟,可乐相当于在电脑中实际运行的三个程序,也就是相当于三个进程,电脑上CPU执行进程的操作非常快,以至于我们会误以为多个进程在同时执行,实际上,在某个确定时刻,CPU只会执行一个进程。
并行:在同一时刻,有多个指令在多个CPU上同时执行。
举个例子,饭店厨房,有三个厨师分别在做西红柿炒番茄、青椒肉丝不放青椒不放肉丝(ps:有的朋友就想说了,那你丫的还炒什么饭),海参炒饭,此时三个厨师同时在做三种饭,这就是并行关系。
并发:在同一时刻,有多个指令在单个CPU上交替执行。
接着上面的例子继续讲,突然老板把其他两个厨师叫走, 只剩下这一个厨师,继续做饭,此时,他需要一会做西红柿炒番茄,一会去做青椒肉丝,一会又去做海参炒饭,忙的满头大汗,嚷着让老板加钱(ps:当然老板是不会加的),这一个厨师同时做三种饭,就相当于并发关系。
进程:是正在运行的程序
线程:是进程中的单个顺序控制流,是一条执行路径。
方法名 | 说明 |
---|---|
void run() | 在线程开启后,此方法将被调用执行 |
void start() | 使此线程开始执行,Java虚拟机会调用 run 方法() |
public class MyThread extends Thread {
@Override
public void run() {
//代码就是线程在开启之后执行的代码
for (int i = 0; i < 100; i++) {
System.out.println("线程开启了"+ i);
}
}
}
public class Demo {
public static void main(String[] args) {
//创建一个线程对象
MyThread t1 = new MyThread();
//创建一个线程对象
MyThread t2 = new MyThread();
// 开启一条线程
t1.start();
//t1.run(); 表示的仅仅是创建对象,用对象去调用方法,并没有开启多线程。
//开启第二条线程
t2.start();
}
}
注意:
1.多线程程序涉及到CPU的切换,cpu在多个线程之间切换是随机的。
2.如果我们不去调用start()方法开启线程,直接去调用MyThread类中重写的run方法例如这样:
public class Demo1 {
public static void main(String[] args) {
//创建一个线程对象
MyThread t1 = new MyThread();
//创建一个线程对象
MyThread t2 = new MyThread();
t1.run();
t2.run();
}
}
这样表示的仅仅只是创建对象,用对象去调用方法,并没有开启多线程。
运行结果:
方法名 | 说明 |
---|---|
Thread(Runnable target) | 分配一个新的Thread对象 |
Thread(Runnable target,String name) | 分配一个新的Thread对象 |
public class MyRunnable implements Runnable {
@Override
public void run() {
//表示线程启动后执行的代码
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"第二种方式实现多线程"+ i);
}
}
}
public class Demo {
public static void main(String[] args) {
//创建了一个参数对象
MyRunnable mr = new MyRunnable();
//创建了一个线程对象,并把参数传递给这个线程
//在线程启动之后,执行的就是参数里面的 run 方法
Thread t1 = new Thread(mr);
//开启线程
t1.start();
MyRunnable mr2 = new MyRunnable();
Thread t2 = new Thread(mr2);
t2.start();
}
}
注意:
Thread.currentThread().getName()是获取线程的名称,此时我们没有设置线程的名称,通过查看源码,默认情况下线程名称为 Thread-后边的数字(默认从零开始)
我们也可以通过第二个构造方法,给线程设置名称,代码如下:
public class Demo1 {
public static void main(String[] args) {
//创建了一个参数对象
MyRunnable mr = new MyRunnable();
//创建了一个线程对象,并把参数传递给这个线程
//在线程启动之后,执行的就是参数里面的 run 方法
Thread t1 = new Thread(mr,"线程一");
//开启线程
t1.start();
MyRunnable mr2 = new MyRunnable();
Thread t2 = new Thread(mr2,"线程二");
t2.start();
}
}
运行结果:
方法名 | 说明 |
---|---|
V call() | 计算结果,如果无法计算结果,抛出一个异常 |
FutureTask(Callable callable) | 创建一个 FutureTask,一旦运行就执行给定的 Callable |
V get() | 如有必要,等待计算完成,然后获取其结果 |
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println("跟女孩表白"+i);
}
//返回值就表示线程运行完毕之后的结果
return "答应";
}
}
public class Demo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//线程开启之后需要执行里边的call方法
MyCallable mc = new MyCallable();
//可以获取线程执行完毕之后的结果,也可以作为参数传递给 Thread对象
FutureTask<String> ft = new FutureTask<>(mc);
//创建线程对象
Thread t1 = new Thread(ft);
//开启线程
t1.start();
String s = ft.get();
System.out.println(s);
}
}
方法名 | 说明 |
---|---|
void setName(String name) | 将此线程的名称更改为参数 name |
String getName() | 返回此线程的名称 |
Thread currentThread() | 返回对当前正在执行的线程对象的引用 |
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName()+"@@@"+i);
}
}
}
public class Demo {
//1.线程是有默认名字的,格式:Thread-编号
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("小蔡");
t2.setName("小强");
t1.start();
t2.start();
}
}
public class MyThread extends Thread {
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName()+"@@@"+i);
}
}
}
public class Demo {
//1.线程是有默认名字的,格式:Thread-编号
public static void main(String[] args) {
MyThread t1 = new MyThread("小蔡");
MyThread t2 = new MyThread("小强");
t1.start();
t2.start();
}
}
public class Demo {
public static void main(String[] args) {
//返回当前正在执行的线程对象的引用
String name = Thread.currentThread().getName();
System.out.println(name);
}
}
运行结果:
方法名 | 说明 |
---|---|
static void sleep(long millis) | 使当前正在执行的线程停留(暂停执行)指定的毫秒数 |
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
public class Demo {
public static void main(String[] args) throws InterruptedException {
System.out.println("睡觉前");
Thread.sleep(3000);
System.out.println("睡醒了");
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
t1.start();
t2.start();
}
}
线程调度
举个例子来理解分时调度模型和抢占式调度模型
如上图,一个拳头准备打底下的几个人(ps:是不是一个比一个贱啊),分时调度模型就是 拳头会挨个打每一个人,而抢占式调度模型就是 当第一个人比其他人更贱时,他的优先级更高,可能当拳头打完第一个人和第二个人时,又返回来打第一个人(ps:显然他更欠打),这里的拳头就相当于 CPU,而每个人就相当于多个线程。
优先级相关方法
方法名 | 说明 |
---|---|
final int getPriority() | 返回此线程的优先级 |
final void setPriority(int newPriority) | 更改此线程的优先级线程默认优先级是5;线程优先级的范围是: 1-10 |
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"----"+i);
}
return "线程执行完毕了";
}
}
public class Demo {
public static void main(String[] args) {
//优先级:1 - 10 默认值:5
MyCallable mc = new MyCallable();
FutureTask<String> ft=new FutureTask<>(mc);
Thread t1 = new Thread(ft);
t1.setName("飞机");
t1.setPriority(10);
// System.out.println(t1.getPriority());
t1.start();
MyCallable mc2 = new MyCallable();
FutureTask<String> ft2=new FutureTask<>(mc2);
Thread t2 = new Thread(ft2);
t2.setName("坦克");
t2.setPriority(1);
// System.out.println(t2.getPriority());
t2.start();
}
}
运行结果
注意:
线程的优先级越高,只能说明它抢到CPU的几率越高,虽然线程坦克优先级比线程飞机高,但是线程飞机先抢到的 CPU使用权。我们查看 Thread类的源码,可以看到线程优先级的范围。
相关方法
方法名 | 说明 |
---|---|
void setDaemon(boolean on) | 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出 |
public class MyThread1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName()+"---"+ i);
}
}
}
public class MyThread2 extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName()+"---"+i);
}
}
}
public class Demo {
public static void main(String[] args) {
MyThread1 t1 = new MyThread1();
MyThread2 t2 = new MyThread2();
t1.setName("女神");
t2.setName("备胎");
//把第二个线程设置为守护线程
t2.setDaemon(true);
t1.start();
t2.start();
}
}
本文讲述了并发和并行,进程和线程的相关概念,并举了形象的例子以便于读者理解,还讲述了实现多线程的三种方式,以及三种方式对比之下的优缺点,并且介绍了线程休眠、线程优先级、守护线程的相关知识点,最后,希望大家多多关注,你们的支持,是我更新的动力!