2021-08-17 00:56 多线程编程(一)----基础

一、并发基础

大神!

(一)线程、进程、程序的区别

进程:是程序的一次执行过程,或是正在运行的一个程序,是一个动态 的过程,有它自身的产生,存在和消亡的过程。-------生命周期
线程:进程可进一步细化为线程,是一个程序内部的一条执行路径
程序:是为完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码,静态对象。

(二)多进程、多线程

多进程:在操作系统中同时运行多个任务 (程序、软件)
多线程:同一个应用程序中,有多个程序流(任务)同时执行

(三)线程相关的API

来源

(四)Thread类 和 Runnable接口实现多线程

Thread实现

public class Test6 {
    public static void main(String[] args) {
        new M1().start();
        new M2().run();
    }
    static class M1 extends Thread {
        @Override
        public void run() {
            for(int i=0 ; i<10 ; i++){
                try {
                    Thread.sleep(5000);
                    System.out.println(Thread.currentThread().getName()+":"+i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    static class M2 extends Thread {
        @Override
        public void run() {
            for(int i=0 ; i<10 ; i++){
                try {
                    Thread.sleep(5000);
                    System.out.println(Thread.currentThread().getName()+":"+i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Runnable实现

public class Test7 {
    public static void main(String[] args) {
        new M1().run();
    }
    static class M1 implements Runnable{
        @Override
        public void run() {
            for(int i=0 ; i<10 ; i++){
                try {
                    TimeUnit.MILLISECONDS.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}

1.run()和start()的区别

run():开始依旧为单线程
start():开始为多线程------>用户线程
区别

2.底层

依旧是实现Runnable接口

3.Thread和Runnable的区别

大神!
(1) Thread是类,用于被继承,且只能单继承,直接调用start方法,依然要重写run方法
(2) Runnable是接口,用于被实现,可以多实现,重写run方法,但没有start方法,直接用run方法线程依旧单一,想要实现,则先实例化Thread方法,并将实现接口的类对象塞进Thread中,通过Thread调用
run方法可以包含任何代码,但是它必须是公有的,不带任何参数,没有返回值,并且不抛出任何异常

(五)线程的生命周期及状态

来源
线程的七种状态

出生状态:用户在创建线程时所处的状态,在用户使用该线程实例调用 start() 方法之前,线程都处于出生状态。
就绪状态:也称可执行状态,当用户调用 start() 方法之后,线程处于就绪状态。
运行状态:当线程得到系统资源后进入运行状态。
等待状态:当处于运行状态下的线程调用 Thread 类的 wait() 方法时,该线程就会进入等待状态。进入等待状态的线程必须调用 Thread 类的 notify() 方法才能被唤醒。notifyAll() 方法是将所有处于等待状态下的线程唤醒。
休眠状态:当线程调用 Thread 类中的 sleep() 方法时,则会进入休眠状态。
阻塞状态:如果一个线程在运行状态下发出输入/输出请求,该线程将进入阻塞状态,在其等待输入/输出结束时,线程进入就绪状态。对阻塞的线程来说,即使系统资源关闭,线程依然不能回到运行状态。
死亡状态:当线程的 run() 方法执行完毕,线程进入死亡状态。

线程的生命周期
生命周期
在这里插入图片描述

来源
生命周期流程
1. 调用new方法新创建一个线程,这时线程处于新建状态;
2. 调用start方法启动一个线程,线程处于就绪状态;
3. 处于就绪的线程会等待线程获取CPU资源,在等待其获取CPU资源之后,线程会执行run方法进入运行状态
4. 正在运行的线程在调用了yeild方法或失去处理资源后,会再次进入就绪状态;
5. 正在执行的线程在运行了sleep方法、I/O阻塞、等待同步锁、等待通知、调用suspend方法等操作后,会挂起并进入阻塞状态,进入Block池;
6. 阻塞状态的线程由于sleep时间已到、I/O方法返回、获得同步锁、等待通知、调用resume方法等情况,会再次进入就绪状态,等待CPU时间片的轮询,该线程在获取到CPU资源后,会再次进入运行状态;
7. 处于运行状态的线程,在调用run方法或call方法正常秩序完成,调用stop方法停止线程或者程序执行错误导致异常退出的时候,会进入死亡状态

New–Runnable–Running–Blocked(
(1) 等待阻塞:在运行状态的线程调用o.wait,JVM会把线程放入等待队列中(Waitting Queue),线程会转换成阻塞状态
(2) 同步阻塞:在运行状态的线程尝试获取正在被其他线程占用的对象的同步锁时,JVM会把该线程放入锁池中,此时线程转换为阻塞状态
(3) 其他阻塞:运行状态的线程在执行Thread.sleep()、Thread.join()或者发出I/O请求时,JVM把线程转换为阻塞状态,直到sleep()状态超时,Thread.join()等待线程终止或超时,或I/O处理完毕,线程才重新转为可运行状态
)–Dea(以下三种状态会转换成死亡:
(1) 线程正常结束:run方法或call方法运行结束
(2) 线程异常退出:运行中的线程抛出一个ERROR或捕获一个Exception,线程异常退出
(3) 手动结束:调用stop方法手动结束运行中的线程)

(六)Interrupt关键字的使用

JDK推荐使用Interrupt来进行中断线程,但是使用Interrupt室友限制的:对线程的Interrupt是对线程实在sleep(休眠)、wait(等待)、join(加入)状态的时刻次啊能起作用

作用原理:向线程发送一个终止信号,会影响线程内部的一个中断标识位,而线程本身状态(阻塞,终止等)不会因此而改变——>红绿灯原理

等待收到中断标识位的程序的最终处理结果来判定最后该线程的状态

(七)Callable接口

主线程分多个线程完成任务后,分线程向主线程汇总报告

(八)三种创建线程方法的区别

Thread:编写简单,可以直接操纵线程;不能再从其他类继承线程
Runnable:可以将线程代码和线程数据分开,形成清晰的模型;可以继承其他类
Callable
1.使用Callable接口规定的方法是call(),而Runnable规定的方法是run()
2.Callable的任务执行后可返回值,而Runnable不能有返回值
3.call()方法可以抛出异常,但是run不能
4.运行Callabl任务可以拿到一个Future对象,Future表示异步计算的结果
它提供了检查计算完成的方法,以等待计算的完成,并检索计算的结果
通过Future可以了解任务的执行情况,可取消任务的执行,还可获取任务执行的结果
Callable类似于Runnable接口,实现Callable和Runnable的类都是可以被其他线程执行的任务

(九)线程池

1.工作原理:JVM先根据用户参数创建一定数量的可运行的线程任务,并将其放入队列中,在线程创建后启动这些任务,如果线程数量超过了最大线程数量(用户设置的线程池大小),则超出数量的线程排队等候,在有任务执行完毕后,线程池调度器会返回可用的线程,进而再次从队列中取出任务并执行
2.作用:线程复用、线程资源管理、控制操作系统的最大并发数、以保证高校(通过线程资源复用实现)且安全(通过控制最大线程并发数实现)地运行
3.线程复用:在start方法中不断循环调用传递进来的Runnable对象,程序就会不断地额执行run方法中的代码。可以将在循环方法中不断获取的Runnable对象存放在Queue中,当前线程在获取下一个Runnable对象之前可以是阻塞的,这样既能有效控制正在执行的线程个数,也能保证系统中正在等待执行的其他线程的有序执行
4.线程池的核心组件
(1)线程池管理器:用于创建并管理线程池
(2)工作线程:线程池中执行具体任务的线程
(3)任务接口:用于定义工作线程的调用和执行策略,只有线程实现了该接口,线程中的任务才能被线程池调度
(4)任务队列:存储等待处理的任务,新的任务将会不断被加入队列中,执行完成的任务将会被从队列中移除
5.线程池的工作流程
在这里插入图片描述
1.如果正在运行的线程数量少于corePoolSize(用户定义的核心线程数),线程池就会立刻创建线程并执行线程任务
2.如果正在运行的线程数量大于等于corePoolSize,则该任务就将被放入阻塞队列中
3.在阻塞队列已满且正在运行的线程数量少于maximumPoolSize时,线程池会创建非核心线程立刻执行该任务
4.在阻塞队列已满且正在运行的线程数量大于等于maximumPoolSize时,线程池将拒绝执行线程任务并抛出RejectExeuctionException异常
5.在线程任务执行完毕后,该任务将从线程池队列中移除,线程池将从队列中取下一个线程任务继续执行
6.在线程处于空闲状态的时间超过KeepAliveTime时间时,正在运行的线程数量超过corePoolSize,该线程将会被认定为空线程并停止,因此线程池中所有线程任务都执行完毕后,线程池会收缩到corePoolSize大小
6.线程池的拒绝策略
(1)AbortPolicy:直接抛出异常,阻止线程正常运行
(2)CallerRunsPolicy:如果被丢弃的线程任务未关闭,则执行该线程任务
(3)DiscardOldestPolicy:移除线程队列中最早的一个线程任务,并尝试提交任务
(4)DiscardPolicy:丢弃当前的线程任务而不做任何处理,目前看是最好的一种方案
7.常用的线程池

名称说明
newCachedThreadPool可缓存的线程池
newFixedThreadPool固定大小的线程池
newSecheduledThreadPool可做任务调度的线程池
newSingleThreadExecutor单个线程的线程池
newWorkStealingPool足够大小的线程池

介绍链接

8.线程池的优点
(1)减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务
(2)可以根据系统的承受能力,调整线程池中工作线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)

(十)join线程

1.join线程
Thread提供了让一个线程等待另一个线程完成的方法.join()方法,当在某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞直到被join方法加入的join线程完成为止

public class JoinThread extends Thread{
	//提供一个有参数的构造器,用于设置该线程的名字
	public JoinThread(String name){
		super(name);
	}
	//重写run方法,定义线程执行体
	public void run(){
		for (int i = 0; i < 100 ; i++ ){
			
			System.out.println(getName() + "  " + i);
		}
	}
  public static void main(String[] args) throws Exception{
		//启动子线程
		//new JoinThread("新线程").start();
		for (int i = 0; i < 100 ; i++ ){
			if (i == 20){
				JoinThread jt = new JoinThread("被Join的线程");
				jt.start();
				//main线程调用了jt线程的join方法,main线程
				//必须等jt执行结束才会向下执行
				jt.join(); 
			}
			System.out.println(Thread.currentThread().getName() + "  " + i);
		}
  }
}

2. 守护线程
(1) setDaemon()方法标记一个线程是守护线程
(2) 守护线程的优先级比较低,用于为系统中的其他对象和线程提供服务。比如垃圾回收线程是一个经典的守护线程,如果在我们的程序中不再有任何线程运行时,程序就不会在产生垃圾,垃圾回收器也就无事可做,所以在回收JVM上仅剩的线程时,垃圾回收线程会自动离开。他始终在低状态下运行,用于实时监控和管理系统中的可回收资源
(3) 用户线程和守护线程的区别:
用户线程: java虚拟机在他所有用户线程都离开后则java虚拟机才离开
守护线程: 它依赖于JVM,与JVM共生死,在JVM中的所有线程都是守护线程了,JVM就可以退出了,如果还有一个或一个以上的非守护线程,则JVM不会退出,如:

/*
在多数情况下,实际上想创建的是一个能够在应用程序中做一些简单的、周期性任务的后台线程。可调用
setDaemon()方法标记一个线程是守护线程。
用户线程和守护线程的区别:
用户线程:Java虚拟机在它所有非守护线程都离开后则Java虚拟机才离开。
守护线程:守护线程则是用来服务用户线程的,如果没有其他用户线程在运行,那么就没有可服务对象,
也就没有理由继续下去,守护线程会自动离开。一旦系统内都剩下守护线程,并不影响JVM的离开。
 */
public class E extends Thread{
	public E(){//在构造函数中创建守护线程
		this.setDaemon(true);//false:为非守护线程, 
		//true:为守护线程 伺候很多个用户线程,知道用户线程over
		start();
	}
	public void run(){
		//守护线程
		for(int i=0;i<=300;i++){//定300范围可以看出效果
			try{
				Thread.sleep(100);
			}catch(InterruptedException ex) {
                ex.printStackTrace();
			}
			System.out.println("守护线程E:"+i);
			
		}
	}
	public static void main(String[] args) {
		E e=new E();//守护线程 伺候两个线程
		E1 e1=new E1();//用户线程
		/*
		 * 用户线程
		 */
		for(int i=0;i<=100;i++){
			try {
				Thread.sleep(100);
			} catch (InterruptedException ex) {
				// TODO Auto-generated catch block
				ex.printStackTrace();
			}
			System.out.println("用户线程:"+i);
		}
		
	}

}
/*
 * 可设为用户线程或守护线程
 */
class E1 extends Thread{
	public E1(){
		this.setDaemon(false);//由于设置成用户线程,该程序的守护线程等待其退出后一并退出。如果设置成守护线程,则用户线程退出后,他和另一个守护线程一并退出
		this.start();
	}
	public void run(){
		for(int i=0;i<=100;i++){
			try {
				Thread.sleep(120);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("用户线程E1:"+i);			
		}

	}
}

3.sleep方法和wait方法的区别
(1) sleep方法属于Thread类,wait方法属于Object类
(2) sleep方法暂停执行指定的时间,让出CPU给其他线程,但其监控状态依然保持,在指定的时间过后有会自动恢复运行状态
(3) 在调用sleep方法的过程中,线程不会释放对象锁
(4) 在调用wait方法时,线程会放弃对象锁,进入等待此对象的等待锁池,只有针对此对象调用notify方法后,该线程才能进入对象锁池准备获取对象锁,并进入运行状态

这东西,不全是我自己写的,不会不理解的是网上找到的,由于来自很多不同的博主,所以没法一一贴出,感谢!我只是想自己复习用,见到错误多多提醒,见到相同处还望海涵

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值