您现在的位置是:首页 > 正文

进程和线程

2024-04-01 07:19:04阅读 4

一、什么是进程、什么是线程

进程是指⼀个内存中运⾏的应⽤程序,每个进程都有⼀个独⽴的内存空间,⼀个应⽤程序可以同时运⾏多个进程。进程之间相互独立,互不影响,系统通过分配不同的端口号判断运行的程序。

线程是进程中的基本单位,一个进程由一个或者多个线程组成。线程之间数据共享,运行时通过抢占cpu来执行。

二、并行和并发

并发:并发是指一个处理器同时处理多个任务,任务之间交替执行

并行:并行是指多个处理器或者是多核的处理器同时处理多个任务

三、创建线程

因为Java是单继承机制,因此创建线程多用实现接口的方式

3.1、继承Thread类创建

1.创建一个继承于Thread类的子类
2.重写Thread类的run()方法
3.创建Thread类的子类的对象
4.作为参数放入new Thread()对象中
5.通过此对象调用start()执行线程
//核心代码
package study01;
​
class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(i);
        }
    }
}
public class Test01 {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        new Thread(myThread).start();
    }
}

3.2、实现Runnable接口(多用)

1.创建一个实现了Runnable接口的类
2.实现类去重写Runnable中的抽象方法:run()
3.创建实现类的对象
4.将此对象作为参数传递到new Thread()对象中
5.通过Thread类的对象调用start()
//核心代码
1、实现Runnable接口写法:
class MyThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(i);
        }
    }
}
public static void main(String[] args) {
    MyThread1 myThread = new MyThread();
    //在创建`Thread`时作为参数传递并启动。
    new Thread(myThread).start();
}
​
2、使用匿名内部类式写法:
public static void main(String[] args) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println(i);
            }
        }
    }).start();
}

四、线程方法

4.1、构造方法

Thread()        分配一个新的Thread对象
​
Thread(Runnable target)     分配一个新的Thread对象
​
Thread(Runnable target, String name)        分配一个新的Thread对象,并起一个名字

4.2、成员方法

static Thread       currentThread()         返回对当前正在执行的线程对象的引用
String              getName()               返回此线程的名称
void                setName(String name)    将此线程的名称更改为等于参数name
​
//更改优先级不太稳定,因为更改优先级后还是抢占式运行
//不方便使用(了解即可):
int                 getPriority()           返回此线程的优先级
void        setPriority(int newPriority)    更改此线程的优先级
​
//睡眠线程无法确定合适的睡眠时间
static void         sleep(long millis)      使正在执行的线程以指定的毫秒数暂停

五、线程同步

当多个线程同时请求并修改痛一个数据的时候,会导致数据不准确的情况,相互之间产生问题。容易出现线程安全的问题。

因为在同一个线程内部,无法确定多个线程的执行顺序,例如若是多个线程同时获取某个属性的值并抢占式执行修改该属性的程序,就可能出现属性的值混乱的情况。

因此线程同步至关重要,对于多线程的程序来说,同步指的是在一定的时间内只允许某一个线程访问某个资源,这样就可以避免数据不准确的情况。

创建相同线程类的多个线程:
​
new Thread(saleTicket, "线程1").start();
new Thread(saleTicket, "线程2").start();

5.1、同步方法

只能有一个线程进入到方法中,其他线程在方法的外面等待

同步方法(使用synchronized修饰重写方法):
class SaleTicket implements Runnable { 
    @Override
    public synchronized void run () {
        
    }
​
但是这个方法过于极端,虽然只允许一个线程进入,但是只有一个线程能进入
不过可以将synchronized改写为外置方法,而后调用,就可以避免上边的极端:
class SaleTicket implements Runnable {
    @Override
    public  void run() {
        while (true) {
                test();
                if (ticket <= 0) {
                    break;
                }
        }
    }
}
​
public synchronized void test() {
        
}

5.2、同步代码块

将一段代码放到synchronized 然后括起来。就会对这段代码加上锁。

同步代码块(代码块写在循环里,每个线程都有机会进入): 
class SaleTicket implements Runnable {
    @Override
    public  void run() {
		while (true) {
			synchronized (this) {
			
			}
		}
	}
}

六、Java中的锁

synchronized被成为隐式锁,会自动释放,是一个非公平的锁。

Lock锁被成为显示锁。

两个锁都可以解决线程同步的问题。但是synchronized 更加强大,更加粒度化,更加灵活。所以一般开发时候用synchronized。

6.1、Lock锁

Lock是一个接口,实现ReentrantLock接口。

有两个重要方法:lock() 和 unlock()。

class SaleTicket implements Runnable {
	ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            
        }
    }
}

七、死锁

开发中禁止出现死锁!

并发场景,多个线程,线程之间在共享数据的时候是互不相让。

死锁是一种状态,当两个线程互相持有对象所需要的资源的时候,这两个线程又都不主动释放资源就会导致死锁。代码无法正常执行。

八、守护线程

守护线程是用来守护非守护线程的,当非守护线程停止运行时,守护线程也会停止。

在在 Java 中,每次程序运⾏⾄少启动两个线程。⼀个是 main 线程,⼀个是垃圾处理线程。

守护线程必须在主线程的前面启动才会和主线程抢占运行

class MyThread implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("守护线程");
        }
    }
}

public static void main(String[] args) {
	//获取当前线程
	Thread thread1 = Thread.currentThread();
	
	Thread thread2 = new Thread(new MyThread8());
    //设置thread2为守护线程
    thread2.setDaemon(true);
    //启动thread2线程
    thread2.start();
    
     for (int i = 0; i < 20; i++) {
            System.out.println("主线程");
     }
}

九、线程的生命周期

1.线程的创建启动
    使用new关键字创建了一个线程之
    线程调用start()方法启动
    
2.线程可运行状态
    抢占cpu开始准备、等待分配cpu

3.线程运行状态
    已抢占到cpu、执行run()方法

4.线程的阻塞
    wait、sleep、锁 

5.线程的消亡
    destroy   

十、Object类下面的和线程相关的方法

wait()方法可以使当前线程等待
直到另一个线程调用该对象的notify()方法或者notifyAll()方法
    
//作为对象型数据
class Message {
    
}
//创建等待线程
class WaitThread implements Runnable {
    private Message message;
    //构造方法
    public NotifyThread(Message message ) {
        this.message = message;
    }
    @Override
    public void run() {
        synchronized (message) {
            message.wait();
        }
    }
}
//创建唤醒线程
class NotifyThread implements Runnable {
    private Message message;
	//构造方法
    public NotifyThread(Message message ) {
        this.message = message;
    }
    @Override 
    public void run() {
        synchronized (message) {
            message.notifyAll();
        }
    }
}
//主线程
public class Demo {
    public static void main(String[] args) {
        new Thread(waitThread1, "wait2线程").start();
        new Thread(waitThread2, "wait1线程").start();
        new Thread(notifyThread, "notify线程").start();
    }
}

十一、join方法

作用:让父线程等待,一直等到子线程结束以后,父线程才会执行。

class FatherThread implements Runnable {
    //父线程中启动子线程
	Thread thread = new Thread(new SonThread());
    thread.start();
    
    //父线程等待
    thread.join();
}

class SonThread implements Runnable {

}

public class Demo {
    public static void main(String[] args) {
        new Thread(new FatherThread()).start();
    }
}

十二、生产者消费者模式

消费者需要购买产品,如果生产者有,则直接购买,如果没有,则唤醒生产者生产,消费者等待生产者生产。生产者生产产品后唤醒消费者,消费者购买(可以循环)

//产品类
class Goods {

}

//消费者线程
class Customer implements Runnable {
    private Goods goods;
    //构造方法
     public Customer(Goods goods) {
        this.goods = goods;
    }
    @Override
    public void run() {
     	while (true) {
            synchronized (goods) {
                if (!goods.isProduct()) {
                    购买;
                    //设置生产者需要生产
                    goods.setProduct(true);
                    //唤醒生产者生产
                    goods.notify();
                } else {
                    //没有产品,等待
                    goods.wait();
                }
            }
        }
    }  
}

//生产者线程
class Productor implements Runnable {
    private Goods goods;
	//构造方法
    public Productor(Goods goods) {
        this.goods = goods;
    }
    @Override
    public void run() {
        while (true) {
            synchronized (goods) {
                生产;
                //设置生产者不需要生产
                goods.setProduct(false);
                //唤醒消费者
                goods.notify();
            } else {
                //产品有剩余,等待生产
                goods.wait();
            }
        }
    }
}

十三、线程池

线程池一个容纳了多个线程的容器,其中的线程可以反复的使用。无需反复创建线程而消耗更多的资源。

ThreadPoolExecutor方法线程池是最原始、也是最推荐的手动创建线程池的方式(一共有其中创建线程池的方法,但是开发只用这一种),它一共有七个参数。

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)
    
1、核心线程数corePoolSize
2、最大线程数maximumPoolSize
3、空闲线程存活时间keepAliveTime
4、时间单位unit
5、阻塞队列workQueue
(6、7变量可以不写)
   

排队策略:
    1、无界队列:
    无界队列是一个没有预定义容量的队列,LinkedBlockingQueue将导致新任务一直在等待,不会有超过核心线程数的线程被创建,因此最大线程数是不起作用的。
    LinkedBlockingQueue:
	5个任务————核心5个线程     任务不会放到队列中排队
	6个任务————核心5个线程     队列中一个只要线程执行完五个以后,最后一个任务随机从5个线程中抽取一个然后执行最后的线程
	12个任务————核心5个线程    队列中有七个线程,五个五个地执行,最后两个任务随机从5个线程中抽取一个然后执行最后的线程
    
    2、有界队列:
    有界队列能在有限的最大线程数内防止资源耗尽,队列的大小和最大线程数可以互相替换,但是它也难调整和控制。
    new ArrayBlockingQueue<>(10):
	5个任务————核心5个线程   任务不会放到队列中排队
	8个任务————核心5个线程   有界对列中存三个
	16个任务————核心5个线程  有界对列存10个  有界对列可以放10个,再开2个核心线程

网站文章

  • idea黑暗护眼主题

    idea黑暗护眼主题

    idea黑暗护眼主题

    2024-04-01 07:18:59
  • C++ 中数组和vector

    C++ 中数组和vector

    首先定义数组必须给出其长度,想要改变其大小也有几种方案 1、用宏定义 #define ARRAYSIZE 10 int array[ARRAYSIZE] 这样每次程序运行时只需改变宏就可以方便的改变数...

    2024-04-01 07:18:50
  • 密封类sealed

    密封类sealed

    2024-04-01 07:18:43
  • STM32-启动文件详解

    STM32-启动文件详解

    还是那句老话基础不牢地动山摇,今天主要来分析由汇编语言编写的启动文件,在进行main函数之前到底做了哪些准备工作,本篇文章所涉及到的部分汇编指令,以及CPU寄存器SP,PC,LR作用,以及最重要的什么是堆和栈请看->《FreeRTOS-ARM架构与程序的本质》,里面已经详细的阐述了以上问题,本文主要是了解启动文件的作用。

    2024-04-01 07:18:02
  • packetbeat对mysql进行抓包 配置文件

    我可以给你一个关于Packetbeat对MySQL的抓包配置的链接:https://www.elastic.co/guide/en/beats/packetbeat/current/packetbeat-configuration-mysql.html。

    2024-04-01 07:17:54
  • 2014计算机基础知识试题,云南省2014年事业单位考试计算机基础知识试题

    2014计算机基础知识试题,云南省2014年事业单位考试计算机基础知识试题

    云南省2014年事业单位考试计算机基础知识试题 (4页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦!9.9 积分航帆培训祝大家考试成功1 在Windows ...

    2024-04-01 07:17:47
  • 不root开启位置服务器,ubuntu开启SSH服务,并容许ROOT权限远程登陆。

    服务器配完ubuntu系统以及LNMP环境之后,想用WINSCP远程登陆,就须要开启SSH服务才能支持。数据库SSH服务分为客户端和服务器。顾名思义,我想用WINSCP远程登陆Ubuntu服务器,因此...

    2024-04-01 07:17:02
  • 每天一个小知识

    每天一个小知识

    注解生效,需要在Spring配置文件中启用方法级别的安全性配置,并配置相应的权限切面。是Spring Security中的注解,用于在方法调用前进行权限验证。以上是一个简单的示例,可以根据自己的需求和...

    2024-04-01 07:16:54
  • ucl计算机教授汪军,UCL汪军教授开课:带你入坑多智能体机器学习(免费)!

    ucl计算机教授汪军,UCL汪军教授开课:带你入坑多智能体机器学习(免费)!

    人工智能已经在围棋、图像识别和语音识别等领域达到甚至超越了人类专家水平,但智能化的机器离我们仍然很远。要想实现通用智能,AI智能体必须学习如何在共享环境中与「他人」进行互动,由此产生了人工智能的一个前...

    2024-04-01 07:16:47
  • Win10电脑重装系统更新关闭了还自动打开怎么解决?

    Win10电脑重装系统更新关闭了还自动打开怎么解决?

    由于电脑系统更新有时候会遇到一些问题,例如会带来一些新的BUG,这使得有用户就会关闭系统更新,但是近期有用户反映更新服务禁止了还更新的情况,那么Win10电脑重装系统更新关闭了还自动打开怎么解决?3、...

    2024-04-01 07:16:06