1:多线程简介
不同程序块一起运行,多任务处理机制
所谓多线程是指一个进程在执行过程中可以产生多个线程,这些线程可以同时存在、同时运行,形成多条执行线。一个进程可能包含了多个同时执行的线程
1:进程:一个正在运行的程序就是一个进程
程序: 是为了完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码,
每个独立执行的程序称为进程
2:线程:在应用程序中的执行路径
线程是比进程更小的执行单位。
线程是CPU调度和分派的基本单位
线程是进程内部单一的一个顺序控制流,所谓多线程是指一个进程在执行过程中可以产生多个线程,这些线程可以同时存在、同时运行,形成多条执行线。一个进程可能包含了多个同时执行的线程。
3 :进程与线程的关系
进程的产生,肯定会产生至少一个以上的线程;
进程关闭,该进程内的线程会全部销毁;
线程销毁,进程未必会关闭
一个进程有多个线程。
多线程是实现并发机制的一种有效手段。进程和线程一样,都是实现并发的一个基本单位
线程和进程的主要差别体现如下:
区别:
进程:每个进程都有自己独立的代码和数据空间,进程间的切换开销大
线程:一个进程内的多个线程,共享代码和数据空间,线程间的切换开销比较小
2:多线程-实现
多线程,指的是一个进程内的多个任务并发执行;
多线程的好处:可以更高效地利用CPU资源,同时,让固定流程的程序更加灵活;
注意:多个线程之间,谁先抢占到资源,谁就先执行
1: 继承Thread类
线程的操作首先一定要有一个线程的主体操作类,这个主体操作类就可以通过继承Thread类来完成,而继承Thread类之后还要去覆写Thread类中的run()方法,此方法的功能与main()方法类似,属于线程的启动点.
/*
* 1:编写一个类,继承自Thread类
* 2:重写run方法
* */
public class MyThread extends Thread{
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name+"执行run方法的线程");
}
}
public class Test1 {
public static void main(String[] args) {
MyThread myThread1 = new MyThread();
myThread1.start();
MyThread myThread2 = new MyThread();
myThread2.start();
MyThread myThread3 = new MyThread();
myThread3.start();
}
}
start()方法执行,首先做一个判断,这里是判断该线程是否已经启动了,如果是已经启动的线程,会报IllegalThreadStateException异常
结论:线程不允许重复启动
在多线程使用时,要想启动多线程,必须通过start()方法
2: 实现Runnable接口
多线程类实现Runnable接口后,还是需要Thread类下的start()方法来启动线程
public class MyRunnable implements Runnable{
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name+"获取当前线程对象");
}
}
class Test2{
public static void main(String[] args) {
MyRunnable r1 = new MyRunnable();
MyRunnable r2 = new MyRunnable();
MyRunnable r3 = new MyRunnable();
new Thread(r1).start();
new Thread(r2).start();
new Thread(r3).start();
}
}
3 :实现Callable接口
1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
//java并发工具包
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class MyCallable implements Callable<Integer> {
//先继承Callable类,重写call方法
@Override
public Integer call() throws Exception {
String name = Thread.currentThread().getName();
System.out.println(name+"执行了");
Thread.sleep(10000);
return 1024;
}
}
class Test3{
public static void main(String[] args) throws Exception {
MyCallable myCallable = new MyCallable();
/*
* 创建一个FutureTask,线程start执行call方法
* */
FutureTask futureTask = new FutureTask(myCallable);
new Thread(futureTask).start();
Integer result = (Integer) futureTask.get();
System.out.println("获取线程执行的结果"+result);
System.out.println("main.....");
}
}
eg.2.0版本
package Thread;
import java.util.concurrent.*;
public class TestThread {
public static void main(String[] args) throws Exception {
testCallable();
}
public static void testCallable() throws Exception {
Callable callable = new MyThreadCallable();
FutureTask task = new FutureTask(callable);
new Thread(task).start();
System.out.println(task.get());
Thread.sleep(10);//等待线程执行结束
//task.get() 获取call()的返回值。若调用时call()方法未返回,则阻塞线程等待返回值
//get的传入参数为等待时间,超时抛出超时异常;传入参数为空时,则不设超时,一直等待
System.out.println(task.get(100L, TimeUnit.MILLISECONDS));
}
}
class MyThreadCallable implements Callable {
@Override
public Object call() throws Exception {
System.out.println("通过实现Callable,线程号:" + Thread.currentThread().getName());
return 10;
}
}
4:三种实现的区别
-
采用继承Thread类方式:
(1)优点:编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this,即可获得当前线程。
(2)缺点:因为线程类已经继承了Thread类,所以不能再继承其他的父类。
-
采用实现Runnable接口方式:
(1)优点:线程类只是实现了Runable接口,还可以继承其他的类。在这种方式下,可以多个线程共享同一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
(2)缺点:编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。
-
Runnable和Callable的区别:
(1)Callable规定的方法是call(),Runnable规定的方法是run().
(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
(3)call方法可以抛出异常,run方法不可以,因为run方法本身没有抛出异常,所以自定义的线程类在重写run的时候也无法抛出异常
(4)运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
!!!
目录
max:start()和run()的区别
-
start()方法用来,开启线程,但是线程开启后并没有立即执行,他需要获取cpu的执行权才可以执行
-
run()方法是由jvm创建完本地操作系统级线程后回调的方法,不可以手动调用(否则就是普通方法)
5:线程的常用方法
Thread.sleep(毫秒数),让线程休眠,时间到,自动唤醒并继续执行
Thread.currentThread():表示获取当前正在执行的线程对象
getName():获取线程名字
public finalboolean isAlive():测试线程是否处于活动状态
优先级:可以给线程设置优先级,范围是1-10,10优先级最高
setPriority(数字):给线程设置优先级,这里优先级高,并不代表一定先执行,只是增加了抢占到资源的机会
getPriority():获取线程的优先级,
------------------------------------------
Thread提供了三个优先级常量:
Thread.MAX_PRIORITY=10
NORM_PRIORITY = 5
MIN_PRIORITY = 1
Main方法的优先级,是5
6.yield()和join()
yield():当前线程暂停执行,与其他线程同时抢占资源,如果还是自己抢占到,则继续执行后续的代码,如果是其他线程抢占到,则其他线程先执行.
join():当前线程暂停执行,新加入的线程开始执行,当新线程执行完之后,再执行当前线程
6.1:yieId()
先准备一个A线程,在执行A线程过程中准备B线程,并让B线程就绪,然后A线程执行到一半之后,A线程礼让,此时A和B都就绪,然后各自抢占资源,谁抢到资源谁执行,如下:
//A线程代码如下:
package cn.sz.gl.test02;
public class MyRunA implements Runnable {
@Override
public void run() {
MyRunB b = new MyRunB();
Thread tb = new Thread(b,"B");
tb.start();
System.out.println("线程A启动..");
Thread.currentThread().yield();
System.out.println("线程A结束..");
}
}
//B线程如下:
package cn.sz.gl.test02;
public class MyRunB implements Runnable {
@Override
public void run() {
System.out.println("线程B启动..");
System.out.println("线程B结束...");
}
}
//测试方法如下:
package cn.sz.gl.test02;
public class Test {
public static void main(String[] args) {
MyRunA a = new MyRunA();
Thread ta = new Thread(a,"A");
ta.start();
}
}
6.2:join()
先准备一个A线程,在执行A线程过程中准备B线程,并让B线程就绪,然后A线程执行到一半之后,让B线程调用join方法,此时A线程阻塞,B 线程启动执行,当B线程执行完毕后,A线程再继续执行
//A线程代码如下:
package cn.sz.gl.test02;
public class MyRunA implements Runnable {
@Override
public void run() {
MyRunB b = new MyRunB();
Thread tb = new Thread(b);
tb.start();
System.out.println("线程A启动..");
try {
tb.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程A结束..");
}
}
//B 线程代码如下:
package cn.sz.gl.test02;
public class MyRunB implements Runnable {
@Override
public void run() {
System.out.println("线程B启动..");
System.out.println("线程B结束...");
}
}
//测试代码如下:
package cn.sz.gl.test02;
public class Test {
public static void main(String[] args) {
MyRunA a = new MyRunA();
Thread ta = new Thread(a,"A");
ta.start();
}
}
3:总结
3.1:多线程的三种实现方式+线程池:
- 继承Thread
- 实现Runable接口
- 实现Callable接口
- 线程池
3.2:三种实现的区别
3.3:start()和run()的区别
-
start()方法用来,开启线程,但是线程开启后并没有立即执行,他需要获取cpu的执行权才可以执行
-
run()方法是由jvm创建完本地操作系统级线程后回调的方法,不可以手动调用(否则就是普通方法)
3.4:yield()和join()
yield():当前线程暂停执行,与其他线程同时抢占资源,如果还是自己抢占到,则继续执行后续的代码,如果是其他线程抢占到,则其他线程先执行.
join():当前线程暂停执行,新加入的线程开始执行,当新线程执行完之后,再执行当前线程
4、建议采纳
如有建议或者错误请私信我进行修改,感谢!!!