哈喽~大家好呀,这篇来看看JAVA进程和线程。
个人主页:个人主页
系列专栏:【日常学习上的分享】
与这篇相关的文章:
Redis快速入门及在Java中使用Redis Redis快速入门及在Java中使用Redis_程序猿追的博客-CSDN博客 为什么不推荐使用Lombok?@Data不香吗? 为什么不推荐使用Lombok?@Data不香吗?-CSDN博客 Mybatis报错: Parameter ‘XXX‘ not found. Available parameters are [arg1, arg0, param1, param2]解决方案及问题原因 Mybatis报错: Parameter ‘XXX‘ not found. Available parameters are [arg1, arg0, param1, param2]解决方案及问题原因-CSDN博客
目录
一、前言
1、进程和线程概念
2、线程的优先级
二、Thread 类
三、Runnable 接口
四、线程同步
1、synchronized
2、使用方式
五、死锁问题
六、生产者与消费者实战案例
七、图书推荐
什么是进程
进程就是正在运行的程序,它是系统资源调度的独立单位,并且一个进程可以执行多个任务,而线程就是程序执行的任务,它是程序使用CPU的基本单位,因此也可以说线程是依赖于进程的。像比如任务管理器里面的运行任务就是线程。
单进程与多进程的概述
单进程的计算机一次只能做一件事情,而多进程的计算机可以做到一次做不同的事情,比如一边听音乐,一边听打游戏,这两件事情虽然感觉起来是在同时一起进行的,但其实是CPU在做着程序间的高效切换,这才让我们觉得是同时进行的。
什么是线程
线程
是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元
,是处理器
调度和分派的基本单位
。
一个进程可以有一个或多个线程,各个线程之间共享程序的内存空间(也就是所在进程的内存空间)。
线程的状态
线程的生命周期
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。
Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。
默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。
使用 Thread 来创建线程,类继承与 Thread 类。继承类必须重写 run() 方法,该方法是新线程的入口点。它也必须调用 start() 方法才能执行。
格式
package com.zte.thread;
public class ThreadA extends Thread{
// run() 方法线程执行的第一步,同main方法
@Override
public void run() {
}
}
示例
public class ThreadA extends Thread{
// run() 方法线程执行的第一步,同main方法
@Override
public void run() {
for (int i = 1; i <= 200; i++) {
System.out.println("i : " + i);
}
}
}
public class ThreadB extends Thread{
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("j :" + i);
}
}
}
public class Text {
public static void main(String[] args){
ThreadA a = new ThreadA();
ThreadB b = new ThreadB();
a.start();
b.start();
}
}
运行结果
打印太多了,我们只看关键的一部分,很明显多个线程明显在抢占资源
扩:
这里我们看下 start() 与 run() 的区别
start() :
它的作用是启动一个新线程。
通过start()方法来启动的新线程,处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行相应线程的run()方法,这里方法run()称为线程体,它包含了要执行的这个线程的内容,run方法运行结束,此线程随即终止。start()不能被重复调用。用start方法来启动线程,真正实现了多线程运行,即无需等待某个线程的run方法体代码执行完毕就直接继续执行下面的代码。这里无需等待run方法执行完毕,即可继续执行下面的代码,即进行了线程切换。
run() :
run()就和普通的成员方法一样,可以被重复调用。
如果直接调用run方法,并不会启动新线程!程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到多线程的目的。
总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里
简单来说就是 start() 可以创建多个线程,不能重复调用,而 run() 创建单个线程,能重复调用。
创建一个线程,最简单的方法是创建一个实现 Runnable 接口的类,使用 run 来调用线程
示例
public class ThreadA implements Runnable{
// run() 方法线程执行的第一步,同main方法
@Override
public void run() {
for (int i = 1; i <= 200; i++) {
try {
Thread.sleep(1000); // 让线程睡眠一会
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("i : " + i);
}
}
}
public class ThreadB implements Runnable{
@Override
public void run() {
for (int i = 0; i < 200; i++) {
try {
Thread.sleep(1000); // 让线程睡眠一会
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("j :" + i);
}
}
}
public class Text {
public static void main(String[] args){
ThreadA a = new ThreadA();
ThreadB b = new ThreadB();
a.run();
b.run();
}
}
扩:
1、当我们使用了 Runnable 实例化对象就不能调用 start 方法了,只能用 run 了。
2、sleep 的意思是强制当前正在执行的线程休眠(暂停执行),例如:【Thread.sleep(long millis)】当线程睡眠时它不会在苏醒之前返回到可运行状态;当睡眠时间到期后才会返回到可运行状态。里面放的毫秒值。
什么是 synchronized ?
答:synchornized 是 Java 中的一个关键字,解决的是多个线程之间访问资源的同步性,synchronized 关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。
在上面我们遇到了线程抢占的问题,而 synchronized 就是来解决这问题的。
应用场景
修饰实例方法:作用于当前对象实例加锁,进入同步代码前要获得 当前对象实例的锁
synchronized void method() {
//业务代码
}
修饰静态方法: 也就是给当前类加锁,会作用于类的所有对象实例 ,进入同步代码前要获得 当前 class 的锁。
synchronized static void method() {
//业务代码
}
修饰代码块 :指定加锁对象,对给定对象/类加锁。synchronized(this|object) 表示进入同步代码库前要获得给定对象的锁。synchronized(类.class) 表示进入同步代码前要获得 当前 class 的锁
synchronized(this) {
//业务代码
}
案例(银行存钱与取钱)
分析:银行比作容器,取钱与存放是两个进程,并且不能同时进行。
public class Account extends Thread{
private int balance = 1000;
Object o = new Object(); // 或者用 this
public void withdraw(int n){
synchronized (o){
balance -= n;
System.out.println("取了 : " + n + " 还剩 : " + balance);
}
}
public void deptdraw(int n){
synchronized (o){
balance += n;
System.out.println("存了 : " + n + " 还剩 : " + balance);
}
}
}
public class PersonA extends Thread{
private Account account;
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
account.withdraw(100);
}
}
}
public class PersonB extends Thread{
private Account account;
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
account.deptdraw(100);
}
}
}
public class Text {
public static void main(String[] args){
Account acc = new Account();
PersonA a = new PersonA();
PersonB b = new PersonB();
a.setAccount(acc);
b.setAccount(acc);
b.start();
a.start();
}
}
运行结果
什么是死锁?
答:线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。当线程进入对象的synchronized代码块时,便占有了资源,直到它退出该代码块或者调用wait方法,才释放资源,在此期间,其他线程将不能进入该代码块。当线程互相持有对方所需要的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁。
简单来讲就是,t1 线程在等 t2 线程,t2 线程也在等 t1 线程,这就陷入到了死循环了,这就叫死锁。
示例
public class AddAndSub {
public Object objectA = new Object();
public Object objectB = new Object();
public void add(){
synchronized (objectA){
System.out.println(Thread.currentThread().getName() + "我获取到了a");
try {
objectA.wait();
System.out.println("我等到了");
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (objectB){
System.out.println(Thread.currentThread().getName() + "我获取到了b");
}
System.out.println(Thread.currentThread().getName() + "我失去了b");
}
System.out.println(Thread.currentThread().getName() + "我失去了a");
}
public void sub(){
synchronized (objectB){
System.out.println(Thread.currentThread().getName() + "我获取到了b");
try {
Thread.sleep(1000);
// System.out.println("我等到了");
} catch (InterruptedException e) {
e.printStackTrace();
}
// synchronized (objectB){
System.out.println(Thread.currentThread().getName() + "我获取到了a");
// objectA.notify();
// }
System.out.println(Thread.currentThread().getName() + "我失去了a");
}
System.out.println(Thread.currentThread().getName() + "我失去了b");
}
}
public class TB extends Thread{
public AddAndSub addAndSub;
@Override
public void run() {
addAndSub.sub();
}
}
public class TA extends Thread{
public AddAndSub addAndSub;
@Override
public void run() {
addAndSub.add();
}
}
public class Text {
public static void main(String[] args){
AddAndSub d = new AddAndSub();
TA a = new TA();
TB b = new TB();
a.setName("线程A");
b.setName("线程B");
a.addAndSub = d;
b.addAndSub = d;
a.start();
b.start();
a.setPriority(Thread.MAX_PRIORITY);
}
}
死锁了,进程 A 一直在等进程 B ,但 B 一直在等 A。
分析:有一个箱子,生产者生产牛奶,放入到箱子里面,消费者在箱子里面拿牛奶
示例
public class Box {
private int milk = 0;
private boolean state = false;
public synchronized void put(int milk){
if (state){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.milk = milk;
System.out.println("放了" + this.milk + "瓶奶");
state = true;
notifyAll();
}
public synchronized void get(){
if (!state){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// this.milk = milk;
System.out.println("拿走了" + this.milk + "瓶奶");
state = false;
notifyAll();
}
}
public class shengchan implements Runnable {
private Box b;
public shengchan(Box box) {
this.b = box;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
b.put(i + 1);
}
}
}
public class xiaofei implements Runnable {
private Box b;
public xiaofei(Box box) {
this.b = box;
}
@Override
public void run() {
while (true) {
b.get();
}
}
}
public class Main {
public static void main(String[] args) {
Box box = new Box();
shengchan shengchan = new shengchan(box);
xiaofei xiaofei = new xiaofei(box);
Thread threadA = new Thread(shengchan);
Thread threadB = new Thread(xiaofei);
threadA.start();
threadB.start();
}
}
运行结果
内容简介
本书从 MATLAB 基础语法讲起,介绍了基于 MATLAB 函数的科学计算问题求解方法,实现了大量科学计算算法。
本书分为三大部分。第 1 章和第 2 章为 MATLAB 的基础知识,对全书用到的 MATLAB 基础进行了简单介绍。第 3 ~ 12 章为本书的核心部分,包括线性方程组求解、非线性方程求解、数值优化、数据插值、数据拟合与回归分析、数值积分、常微分方程求解、偏微分方程求解、概率统计计算及图像处理与信号处理等内容。第 13 ~ 15 章为实战部分,以实际生活中的数学问题为例,将前文介绍的各类科学计算算法应用其中。
本书内容全面、通俗易懂,适合有一定 MATLAB 基础、想要进行进阶学习的读者。
购买链接