❤写在前面
❤博客主页:努力的小鳴人
❤系列专栏:JavaSE超详总结😋
❤欢迎小伙伴们,点赞👍关注🔎收藏🍔一起学习!
❤如有错误的地方,还请小伙伴们指正!🌹
🔥系列传送门:
【05章Java异常处理】一篇短文教你玩转Java异常处理【热榜】
【附章2Java面向对象编程】盘点关键字详解 this、super、final【热榜】
【04章Java面向对象编程(下)】面向对象编程的必杀技【热榜】
【04章Java面向对象编程(中)】解密 继承性和多态性【热榜】
【04章Java面向对象编程(上)】万事万物皆对象的初体验【全站热榜第二】
【附章1Java数组中常见算法】图文并茂详解十大排序算法让您回味无穷
【03章Java数组】程序员必看数组详解
【02章Java基本语法】详细讲解让你重新认识Java基本语法和流程流程控制
【01章Java语言概述】Java学了很久了吧,快回来熟悉熟悉吧(详细)
文章目录
一、基本概念
🔥程序
定义:为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象
🔥进程
定义:是程序的一次执行过程,或是正在运行的一个程序;
它自身的产生、存在和消亡的过程为生命周期
🔥线程
- 定义:进程可进一步细化为线程,是一个程序内部的一条执行路径
线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器
- 一个进程中的多个线程共享相同的内存单元/内存地址空间,它们从同一堆中分配对象,可以访问相同的变量和对象
🎁注:若一个进程同一时间并行执行多个线程,就是支持多线程的
🔥并行与并发
●并行
:多个CPU同时执行多个任务,比如:多个人同时做不同的事。
●并发
:一个CPU同时执行多个任务,比如:多个人做同一件事
二、线程的创建和使用
Java语言的JVM允许程序运行多个线程,它通过java.lang.Thread类来体现
🔥方式一:继承Thread类
- 定义子类继承Thread类
- 子类中重写Thread类中的run方法
- 创建Thread子类对象,即创建了线程对象
- 调用线程对象start方法:启动线程,调用run方法
小试牛刀:
class MyThread extends Thread{
public MyThread(){
super();
}
public void run(){
for(int i = 0;i<100;i++){
System.out.println("子线程:" + i);
}
}
}
public class TestThread{
public static void main(String[] args){
//1.创建线程
MyThread m = new MyThread();
//2.启动线程,并调用当前线程的run()方法
m.start;
}
}
🔥方式二:实现Runnable接口
- 定义子类,实现
Runnable接口
- 子类中重写Runnable接口中的
run方法
- 通过Thread类含参构造器创建线程对象
- 将Runnable接口的
子类对象作为实际参数
传递给Thread类的构造器中
5)调用Thread类的start方法
:开启线程,调用Runnable子类接口的run方法
小试牛刀:
//1.创建一个实现了runnable接口的类
class MThread implements Runnable{
//2.实现类去实现runnable中的抽象方法:run()
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
//3.创建实现类的对象
MThread mThread = new MThread();
//4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
Thread t1 = new Thread(mThread);
t1.setName("线程一");
//5。通过Thread类的对象调用start() : ①启动线程 ②调用当前线程的run()
t1.start();
//再启动一个线程,遍历100以内的偶数
Thread t2 = new Thread(mThread);
t2.setName("线程二");
t2.start();
}
}
🎁注:想要启动多线程,必须调用start方法
🔥JDK5.0新增线程创建方式
👌方式三:实现Callable接口
与使用Runnable相比, Callable功能更强大
相比run()方法:可以有返回值、方法可以抛出异常、支持泛型的返回值、需要借助FutureTask类,比如:获取返回结果
- 步骤:
- 创建一个实现
callable实现类
- 实现call方法,将次线程需要执行的操作声明在
call()
中 - 创建Callable接口实现类的对象
- 将此Callable接口实现类的
对象作为传递到FultureTast构造器
中,创建FutureTast的对象 - 将FutureTast的对象作为参数传递到Thread类的构造器中,创建Thread对象,并start()
- 上代码:
//1.创建一个实现callable实现类
class NumThread implements Callable {
//2.实现call方法,将次线程需要执行的操作系统费声明在call()中
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
if (i%2==0){
System.out.println(i);
sum += i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
//3.创建Callable接口实现类的对象
NumThread numThread = new NumThread();
//4.将此Callable接口实现类的对象作为传递到FultureTast构造器中,创建FutureTast的对象
FutureTask futureTask = new FutureTask(numThread);
//5.将FutureTast的对象作为参数传递到Thread类的构造器中,创建Thread对象,并start()
new Thread(futureTask).start();
try {
//get()返回值即为FutureTest构造器参数Callable实现类重写的call()的返回值
Object sum = futureTask.get();
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
👌方式四:使用线程池
- 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁、实现重复利用
- 步骤:
- 提供指定线程数量的
线程池
- 执行指定的线程操作,需要提供实现Runnable或者Callable接口实现类的对象
- 关闭线程池
- 上代码:
class NumberThread implements Runnable{
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
if (i % 2 != 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
//1.提供指定线程数量的线程池
//ExecutorService是接口,service是其实现类的对象
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor)service;
//设置线程池属性
// System.out.println(service.getClass());找出ExecutorService的实现类
// service1.setCorePoolSize(15);//核心池大小
// service1.setKeepAliveTime();//线程没有任务时最多保持多长时间后会终止
// service1.setMaximumPoolSize(100);//最大线程数
//2.执行指定的线程操作,需要提供实现Runnable或者Callable接口实现类的对象
service.execute(new NumberThread());//适用于Runnable
service.execute(new NumberThread1());//适用于Runnable
// service.submit(Callable callable);//适用于Callable
//3.关闭线程池
service.shutdown();
}
}
三、线程的生命周期
一个完整的生命周期中通常经历如下的五种状态:新建、就绪、运行、阻塞、死亡
知识点尽在图中
:
对图中的解析:
释放锁的操作
●当前线程的同步方法、同步代码块执行结束
●当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、该方法的继续执行
●当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束
●当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁
不会释放锁的操作
●线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行
●线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁
四、线程的同步
Java对于多线程的安全问题提供了专业的解决方式:同步机制
🔥Synchronized
- 同步
代码块
:
synchronized (对象){
// 需要被同步的代码;
} - synchronized还可以放在方法声明中,表示整个方法为同步方法。
如:
public synchronized void show (String name){
….
}
🔥锁
- 定义:通过显式定义同步锁对象来实现同步。使用Lock对象充当
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具
class A{
private final ReentrantLock lock = new ReenTrantLock();
public void m(){
lock.lock();
try{
//保证线程安全的代码;
}
finally{
lock.unlock();
}
}
}
- synchronized的锁是什么?
●任意对象都可以作为同步锁。所有对象都自动含有单一的锁(监视器)。
●同步方法的锁:静态方法(类名.class)、非静态方法(this)
●同步代码块:自己指定,很多时候也是指定为this或类名.class
在线程的生命周期图下解析中也有分析
👌死锁
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁,出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
代码如下:
public class DeadLockTest {
public static void main(String[] args) {
final StringBuffer s1 = new StringBuffer();
final StringBuffer s2 = new StringBuffer();
new Thread() {
public void run() {
synchronized (s1) {
s2.append("A");
synchronized (s2) {
s2.append("B");
System.out.print(s1);
System.out.print(s2);
}
}
}
}.start();
}
}
🔥Synchronized与锁的对比
- Lock是显式锁(手动开启和关闭锁),synchronized是隐式锁,出了作用域自动释放
Lock只有代码块锁,synchronized有代码块锁和方法锁
- 用Lock锁,JVM将花费较少的时间来调度线程,并具有更好的扩展性(提供更多的子类)
🎁注: 必须确保使用同一个资源的多个线程共用一把锁
五、线程的通信
🔥wait()、notify()、notifyAll()
- wait():令当前线程释放对象监控权 ,然后进入等待
- notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待
- notifyAll():唤醒正在排队等待资源的所有线程结束等待
传送门>>>康师傅的经典例题:生产者/消费者问题
这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则会报java.lang.IllegalMonitorStateException异常
🎁总结:多线程个人感觉比较难,不太好理解和运用,多看几遍总结总结
👌 作者算是一名Java初学者,文章如有错误,欢迎评论私信指正,一起学习~~
😊如果文章对小伙伴们来说有用的话,点赞👍关注🔎收藏🍔就是我的最大动力!
🚩不积跬步,无以至千里,书接下回,欢迎再见🌹