Java线程相关

并发

  • 多个不同的软件同时运行,windows/linux等操作系统,同时管理多个软件并发执行
  • 一个软件可以被多个用户同时请求,例如,多个浏览器用户同时请求淘宝,同时做支付操作和结算操作,等价于在服务端多次运行
  • 并发的目的就是要CPU不停的工作,提高CPU的工作效率
计算机是如何做到并发的

ps:只有一台计算机,一块CPU,一个核

分析硬件 – 时间片

在某一个CPU的时间点上,只能有一个程序在执行,为了能做到并发,把CPU的时间分成若干时间片段。

时间片段的划分原则由操作系统定义,每个小的时间片段只能执行一个程序。

时间片到了就把程序强制离开CPU,在从内存中选出下一个程序,继续运行。

看似在一段时间内是多个程序在同时运行。

分析软件 – 进程

一个软件动辄几百Mb,或上Gb,一个软件是不能整个存储在内存中的

进程:把大的软件分割成多个程序段,程序段可以称之为进程
多个大的程序要运行,先把程序分割成多个程序段,然后把每个程序的前一部分的若干程序段加载到内存,最终内存中放置了多个程序的多个程序段。

这些程序段要根据向优先级排队,从队头获取下一个程序段来运行。

大量的程序段也可以说是进程,在频繁切换CPU的是会占用CPU的时间,做压栈和弹栈的工作,和需要部分内存存储栈中的数据。

进程还拥有一个私有的虚拟的内存地址,该空间仅能被他包含的线程访问,线程只能归属某一个进程,并且他只能访问该进程的所有的资源。

当操作系统创建了一个进程后,该进程就会自动申请一个名为主线程或者叫首要线程。

同类的多个线程,共享一块内存空间和一组资源,线程本身有一个供程序执行的堆栈。

线程在CPU切换时负荷小,因此线程被称之为轻负荷进程,一个进程可以包含多个线程。

进程和线程的区别
  • 一个进程至少有一个线程。
  • 线程的划分尺度小于进程,使得多个线程并发性高。
  • 另外,进程在指定的过程中拥有独立的内存单元,而多个线程共享进程的内存空间,从而提高了程序的运行效率。
  • 线程在执行的过程中,与进程区别在于每个独立的线程有一个程序的运行入口,顺序执行序列和程序出口。
  • 线程不能独立运行,必须依存于应用程序中,由应用程序提供对线程的控制。
线程的应用场景:
  • 线程通常用于一个应用程序中需要同时完成多个任务的情况
  • 我们可以将每个任务定义一个线程,使得他们可以一同工作,也可以用于单一线程中完成
  • 存在多线程可以更快的完成所需要的功能
线程的使用:

在java中能通过api做线程编程

方案一:继承Thread类
  • 此类是线程类,其每一个实例对象表示一个可以并发运行的线程。
  • 通过继承此类并重写run方法,来定义一个具体的线程,在run方法中体现的是线程的功能。
  • 启动线程时,调用线程对象的start方法,而不是直接调用run方法。
  • start方法将线程纳入线程调度,使当前线程可以并发运行。
  • 当线程获取到CPU时间片后,开始自动运行run方法中的逻辑代码 。
public class MyThread extends Thread{
	public void run(){
			//线程的入口
			//线程的任务逻辑
			//线程的出口
	}
}
//使用:
MyThread mt = new MyThread();
mt.start();//调用重写的run。实际是多态
方案二:实现Runnable接口
  • 实现此接口,并重写run方法来定义线程类,然后创建线程的时候。
  • 将Runnable接口的实例传入并启动线程,这做的好处可以将线程和线程要执行的任务逻辑分离,减少耦合。
  • 同时java是单继承,定义一个实现Runnable接口,这样做可以更好的实现其他的接口和继承父类。
  • Thread的run调用重写的run。
public class MyThread implements Runnable{
	public void run(){
			//线程的入口
			//线程的任务逻辑
			//线程的出口
	}
}
//使用:
Thread t=new Thread(new MyThread());
t.start();//执行的是thread中的run方法
		  //thread中的run又调了MyThread里的run
线程的状态
创建线程的对象创建态Thread t=new Thread();
创建完的线程对象就绪态t.start();//排到线程队列中,不一定马上获取CPU
获取到CPU资源执行态执行run()方法;//线程出队列
执行到某一个点时阻塞态从CPU上下来;//IO,sleep,wait阻塞
如果正常执行完run结束态/消亡态等待GC回收
线程的状态之间的转化
  • 创建态–>就绪态
  • 就绪态–>执行态
  • 执行态–>阻塞态(挂起)
  • 执行态–>就绪态
  • 阻塞态–>就绪态
  • 执行态–>消亡态
做线程的目的

实际上是为了做多线程
多线程的目的就是为了能并发
能并发的目的就是为了提高CPU的利用率

守护线程

当进程中只剩下守护线程时,所有的守护线程强制终止

联合线程:void join()

此方法用于等待当前线程结束,比如:

t1.join();//t1线程没有结束,当前线程不会结束
线程的重要的几个关键点
  1. 线程何时启动,即线程对象.start();是有先后顺序的,有可能某个线程的启动要靠他的主线程是否启动,
  2. run方法的执行顺序,run是并发执行的,靠的是何时start()后,何时能获取到CPU
  3. run并发执行着,需要注意线程之间的依存关系,当前线程是否执行完毕,可能是依赖于另一个线程是否执行完毕
做多线程的两种方式
  1. 多个线程共享一个run()
  2. 多个线程可以每个线程一个run(),
  3. 以上两种组合,也是多线程
写多线程的步骤
  • 不需要关心线程在那个进程里
  • 每new一个Thread类或其子类的对象都是线程的对象,且要start()
  • 要根据需求考虑要有几个run的任务逻辑
  • 要根据需求考虑每个run要执行多少次
  • 要根据需求考虑run方法是否使用数据,并考虑数据安全性
用内部类创建线程对象

通常我们可以通过匿名内部类的方法创建线程,使用该方式可以简化编写代码的复杂度
当一个线程仅需要一个实例时,我们通常可以使用如下方式:

//方式一:实现类是匿名的,对象有名
Thread t=new Thread(){
	public void run(){
		System.out.println("run方法");
	}
};
t.start();
//方式二:实现类是匿名的,对象也是匿名的
new Thread(){
	public void run(){
		System.out.println("run方法");
	}
}.start();
//方式三:实现类是匿名的,对象不是匿名的
Runnable r=new Runnable(){
	public void run(){
		System.out.println("run方法");
	}
};
Thread t=new Thread(r);
t.start();
//方式四:对象是匿名的,实现类也是匿名的
new Thread(new Runnable(){
	public void run(){
		System.out.println("run方法");
	}
}).start();
//方式五:实现类是匿名的,对象是有名的,对象名t
Thread t=new Thread(new Runnable(){
	public void run(){
		System.out.println("run方法");
	}
});
t.start();
创建线程对象
  1. 继承自Thread类,重写run方法
    优点:在当前的线程类中,可以获取run方法,并重写,同时在当前线程类中也可以访问Thread类中的方法。
    缺点:当前线程类不能继承其他的类,java是单继承。
  2. 实现Runnable接口,并重写run方法
    优点:当前的类可以多实现,还可以继承,把线程对象和线程的任务逻辑分离出来。
    缺点:Runnable接口的实现类只有一个run方法,要想使用其他的线程方法需要。
    Thread t=new Thread(Runnable接口的实现类对象);
  3. 特殊用法,匿名内部类
    优点:写法简单,编码量少
    缺点:run的实现只有一次,且对象只有一个
线程常用的API
static Thread currentThread();获取当前的线程对象
long getId();获取标识符
String getName();获取线程的名字
int getPriority();获取线程的优先级 优先级有十级,1-10,10最高
boolean isAlive();获取当前线程是否为活动状态
boolean isDaemon();获取当前线程是否为守护线程
boolean isInterrupted();获取线程是否中断
static void sleep(long 毫秒数);指定的毫秒数内,让当前正在执行的线程休眠(暂停执行),此操作受操作系统计时器和调度程序精度和准确度的影响
synchronized关键字,同步。

同步资源,同步锁

  • 此关键字可以修饰在方法上
public synchronized void method(){
	//代码块 
}

当线程1调用method方法时,就会给method方法和调用该方法的对象添加一个锁,被锁的对象不能调用其他被同步锁标识的方法。但是可以调用无锁的方法。
如果方法不执行完毕,其他线程就不会执行此方法,线程排队等待

  • 此关键字修饰在对象上
synchronized(某个对象){
	//同步代码块
}

当线程1调用同步代码块时,就会给对象添加一个锁
如果代码块不执行完毕,其他线程就不会执行此代码块,线程排队等待

  • 此关键字修饰在类上
 synchronized(类名.class){
		//同步代码块
 }

当线程1调用同步代码块时,就会给类添加一个锁
如果代码块不执行完毕,其他线程就不会执行此代码块,线程排队等待

线程池

有一个Executors的线程工具类,此类提供了若干静态方法,这些静态方法用于生成线程池对象。

Executors.new SingleThreadExecutor();创建一个单线程的线程池,这个线程池只有一个线程在工作即单线程的执行任务,如果这个唯一的线程因为异常结束,那么就会有一个新的线程来替代他,因此线程池保证所有的任务是按照任务的提交的顺序来执行
Executors.newFilxedThreadPool();常见固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大的大小,线程池的大小一旦达到最大,就会保持不变,如果某个线程因为执行异常而结束,那么线程就会补充一个新的线程
Executors.newCacheedThreadPool();创建一个可以缓冲的线程池,如果线程池大小超过了处理的任务所需要的线程,就回收部分线程,当任务数增加的时候,此线程池又可以智能的添加新线程来处理任务,此线程池不会对线程池大小做限制,线程池的大小完全依赖操作系统能够创建的最大的大小
Executors.newScheduledThreadPool();创建一个大小无限制的线程池,此线程池支持定时以及周期性的执行任务的需求
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值