并发编程之多线程基础
为了打牢自己专业知识的基础技能,由此准备开始开始一系列的java基础技能博客的编写!
线程与进程的区别
线程:是一组指令的集合,是一条执行路径,通常由操作系统负责多个线程的调度和执行。
进程:每个系统上运行的程序都是一个进程,是一个独立的程序
总结:进程是所有线程的集合,每一个线程是一个进程的执行路径
多线程的应用场景,比如:迅雷软件的同时下载,数据库连接池、分批发送短信等, 多线程的目的就是为了提高程序的运行效率。
多线程:
在同一时刻有多条不同的执行路径同时执行,其目的是为了提高程序运行的效率。
多线程的创建方法
在了解多线程的创建方法之前,我们先了解一下其他方面的知识:
1:进程中一定存在的线程就是主线程
2:线程分类:用户线程,守护线程
3:了解一下同步和异步的概念: 同步:表示代码从上往下顺序执行,比如http的请求,同步表示的就是当代码程序中有一个方法出现异常那么下面的方法就不会继续的去执行了。异步:每个方法都有自己的线程,每个方法都有自己的执行路径他们线程之间是想不不影响的。例如mq的异步加载。
多线程的创建方法:
1:继承Thread类,
如:class threadDemo1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("i" + i);
}
}
}
publicclass ThreadDemo2 {
publicstaticvoid main(String[] args) {
System.out.println("-----多线程创建开始-----");
// 1.创建一个线程
CreateRunnable createThread = new CreateRunnable();
// 2.开始执行线程 注意 开启线程不是调用run方法,而是start方法
System.out.println("-----多线程创建启动-----");
Thread thread = new Thread(createThread);
thread.start();
System.out.println("-----多线程创建结束-----");
}
}
2:实现Runnable的接口的方法
class CreateRunnable implements Runnable {
@Override
publicvoid run() {
for (inti = 0; i< 10; i++) {
System.out.println("i:" + i);
}
}
}
publicclass ThreadDemo2 {
publicstaticvoid main(String[] args) {
System.out.println("-----多线程创建开始-----");
// 1.创建一个线程
CreateRunnable createThread = new CreateRunnable();
// 2.开始执行线程 注意 开启线程不是调用run方法,而是start方法
System.out.println("-----多线程创建启动-----");
Thread thread = new Thread(createThread);
thread.start();
System.out.println("-----多线程创建结束-----");
}
}
3:匿名内部类的方法
Thread thread = new Thread(new Runnable() {
public void run() {
for (int i = 0; i< 10; i++) {
System.out.println("i:" + i);
}
}
以上就是创建线程的方式,总结: 开启线程不是调用的run方法,而是使用start的方法,调用run知识使用实例调用方法。
我们在使用线程的过程中,我们会用到线程对象及其名称,下图就是我们常用线程的api方法:
start() 启动线程
currentThread() 获取当前线程对象
getID() 获取当前线程ID Thread-编号 该编号从0开始
getName() 获取当前线程名称
sleep(long mill) 休眠线程
Stop() 停止线程,
常用线程构造函数
Thread() 分配一个新的 Thread 对象
Thread(String name) 分配一个新的 Thread对象,具有指定的 name正如其名。
Thread(Runable r) 分配一个新的 Thread对象
Thread(Runable r, String name) 分配一个新的 Thread对象
多线程的状态
线程从创建、运行到结束线程处于下面的五个状态: 新建状态、就绪状态、运行状态、阻塞状态、死亡状态。
新建状态:就是我们在new 创建的过程,这个时候线程还没有运行, 例如在new Thread® ,这个线程没有运行,是处于新生状态时,程序还没有开始运行线程中的代码;
就绪状态: 就是程序调用statrt()方法的过程,start()方法创建线程运行的系统资源,并调度线程运行run()方法,当start()方法返回后,就是处于就绪状态,在处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。所以单cpu的计算机系统,是不可能同时运行多个线程
运行状态: 当现成获得CPU时间后,他才进入运行状态,真正开始执行run方法;
阻塞状态:在线程运行的过程中可能由于各种原因使得线程进入阻塞状态:
1:当线程调用sleep方法进入睡眠状态;
2:线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回得到它的调用者
3:线程试图得到锁,而该锁被其他线程所得到的
4:线程等待某一个触发的条件
当线程从阻塞状态还原的时候是要回到就绪状态的,然后在继续运行状态
死亡状态:线程会有两个原因造成死亡;
1:run方法正常退出而自然死亡。
2:一个未捕获的异常终止了run方法而使线程死亡
检查线程是否死亡,使用isAlive方法,如果线程运行、阻塞那么这个方法会返回true,如果线程是处于不是可运行,或者线程死亡,那么则会返回false;
守护线程
我们在上面提到,线程分类:用户线程、守护线程
守护线程:就是和主线程相关,用户创建的线程就是指自定义创建的线程,主线程停止,用户线程不会停止
GC线程,属于守护线程,守护线程的特征和主线程一起销毁;
非守护线程和主线程互不影响;
主线程和子线程没有相互影响,我们从例子中去看
public class Thread0001 {
// main 方法就是主线程的入口,我们从之前了解到,线程中肯定存在的线程就是主线程
/**
* 1:主线程创建用户线程,但是他们相互是没有影响的
* 2:守护线程和主线程同时销毁
* @param args
*/
public static void main(String[] args) {
// 子线程
Thread t1 = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
System.out.println("子线程执行" + i );
}
}
});
// 代表将子线程设置为守护线程
// t1.setDaemon(true);
t1.start();
// 主线程
for (int i = 0; i < 10; i++) {
System.out.println("子线程执行" + i );
}
System.out.println("主线程执行完毕");
}
}
我们从程序中执行就可以知道主线程和子线程是互相不影响的;
我们调用方法 t1.setDaemon(true); 将子线程设置为守护线程我们看运行结果
我们从运行结果上可以看到主线程和守护线程同时销毁;
join方法
比如说:当我们一个线程在调用join方法的时候那么这个程序中就会认为将这个调用的线程作为优先级优先执行如下代码:
public static void main(String[] args) throws InterruptedException {
// 子线程
Thread t1 = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
System.out.println("子线程执行" + i );
}
}
});
// 代表将子线程设置为守护线程
t1.start();
t1.join(); //join方法必须是在start方法之前也就是必须是在启动之后才行
// 主线程
for (int i = 0; i < 10; i++) {
System.out.println("主线程执行" + i );
}
System.out.println("主线程执行完毕");
}
从执行结果上我们就可以看出当t1线程调用join方法的时候,这个时候t1这个线程的优先级就大于主线程的优先级
额外线程知识: 在java线程中可以通过priority的方法来设置优先级,也就是说使用线程setPriority(10);
来设置优先级 分为1-10个等级
yield方法,线程调用这个方法的目的就是暂停当前正在执行的线程,并去执行其他的线程,这个方法的目的是让当前正在运行的线程回到可运行状态,也是为了让具有相同优先级的线程之间能够适当的轮换执行。当然可能会出现效果不明显的现象原因就是因为在让步的过程中还会可能被再次选中也就达不到让步的效果。