admin管理员组文章数量:1655506
进程与线程
1.什么是进程
进程就是正在运行的程序,它会占用对应的内存区域,由CPU进行执行与计算
1.1进程的特点
独立性
进程是系统中独立存在的实体,它可以拥有自己独立的资源,每个进程都拥有自己私有的地址空间,在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间
动态性
进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中的指令集合,程序加入了时间的概念后称为进程。
并发性
多个进程可以在单个处理器CPU上并发执行,多个进程之间不会互相影响
2.什么是线程
线程是操作系统OS能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位. 一个进程可以开启多个线程,其中有一个主线程来调用本进程中的其他线程。
特性:抢占式运行(CPU在执行的时候,按照时间片来执行的,单位的时间片是抢占是执行),资源共享性
2.1线程与进程的关系
2.2进程和线程的区别
进程是一个应用程序,是独立的
线程是进程中最小的基本单位
一个进程由多个线程组成
进程有独立性和互斥性
线程有抢占式资源共享特性
3.并发和并行
并发:同时发生,轮流交替执行
并行:同时执行
4.创建线程的两种方式方式
4.1继承Thread类
重写Thread中的run方法,实现该子类,然后调用start()方法
class MyThread1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 500; i++) {
System.out.println("我是myThread1线程:" + i);
}
}
}
class MyThread2 extends Thread {
@Override
public void run() {//run方法中写功能代码 就是一个线程中执行的一个功能
for (int i = 0; i < 500; i++) {
System.out.println("我是mythread2线程:" + i);
}
}
}
public class Demo1 {
public static void main(String[] args) {
//官方手册中说,要去实例化Thread的子类,并启动线程
MyThread1 myThread1 = new MyThread1();
//启动线程 使用start方法 在主线程中开启子线程
myThread1.start();
MyThread2 myThread2 = new MyThread2();
myThread2.start();
//一个是MyThread1线程 一个是主线程(main) 一个垃圾回收机制线程
for (int i = 0; i < 500; i++) {
System.out.println("主函数线程:" + i);
}
}
}
4.2实现Runnable接口
使用一个类去实现Runnable接口,创建这个类的对象,然会调用start()方法
class MyThread3 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("MyThread3:" + i);
}
}
}
class MyaThread4 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("MyThread4:" + i);
}
}
}
public class Demo2 {
public static void main(String[] args) {
MyThread3 myThread3 = new MyThread3();
//Thread(Runnable target) 参数是Runnable这个接口对象
//分配一个新的 Thread对象。
Thread thread = new Thread(myThread3);
thread.start();
MyaThread4 myaThread4 = new MyaThread4();
Thread thread1 = new Thread(myaThread4);
thread1.start();
for (int i = 0; i < 100; i++) {
System.out.println("main主线程:" + i);
}
}
}
5.线程下面的方法
构造方法:
方法名 | 作用 |
---|---|
Thread() | 分配一个新的Thread对象,无构造方法 |
Thread(Runnable target) | 分配一个新的Thread对象,有参构造方法 |
Thread(Runnable target,String name) | 分配一个新的Thread对象,重命名 |
线程方法:
返回值 | 方法名 | 作用 |
---|---|---|
static Thread | currentThread() | 返回对当前正在执行的线程对象的引用 |
String | getName() | 返回此线程的名称。 |
void | setName(String name) | 将此线程的名称更改为等于参数 name 。 |
int | getPriority() | 返回此线程的优先级。 |
void | setPriority(int newPriority) | 更改此线程的优先级。设置优先并不一定优先,只是增加了执行的概率。最小值是1,最大值是10,默认的是5 |
static void | sleep(long millis) | 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。 |
public class Test3 {
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
Thread thread = Thread.currentThread();
thread.setName("MyThread线程");
System.out.println(thread.getName());
thread.setPriority(1);
System.out.println(thread.getPriority());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
System.out.println("吃大盘鸡"+i);
}
}
}).start();
Thread thread = Thread.currentThread();
thread.setName("主线程");
thread.setPriority(10);
System.out.println(thread.getPriority());
System.out.println(thread.getName());
Thread.sleep(1000);
for (int i = 0; i < 10; i++) {
System.out.println("吃大米饭"+i);
}
}
}
10
主线程
MyThread线程
1
吃大米饭0
吃大米饭1
吃大米饭2
吃大米饭3
吃大米饭4
吃大米饭5
吃大米饭6
吃大米饭7
吃大米饭8
吃大米饭9
吃大盘鸡0
吃大盘鸡1
吃大盘鸡2
吃大盘鸡3
吃大盘鸡4
吃大盘鸡5
吃大盘鸡6
吃大盘鸡7
吃大盘鸡8
吃大盘鸡9
6.线程的同步和锁
6.1线程同步代码块(多用)
作用:把出现线程安全问题的核心代码给上锁。
原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行。
语法格式:
synchronized(锁对象) {
多条语句操作共享资源的代码
}
线程锁的对象要求:
原则上:锁对象只要对于当前并发的线程来说是同一个对象就可以了
规范上:建议使用共享资源作为锁对象。
对于实例方法建议使用this作为锁对象,此时this应该代表共享资源对象才优雅(例如账户对象)
对于静态方法建议使用字节码类名.class对象作为锁对象。
6.2线程同步方法
作用:把出现线程安全问题的核心方法给上锁。
原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行
语法格式:
修饰符 synchronized 返回值类型 方法名称(形参列表) {
多条语句操作共享资源的代码
}
6.3Lock锁
为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock,更加灵活、方便。
Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作。
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来构建Lock锁对象。
方法名称 | 作用 |
---|---|
public ReentrantLock | 获得Lock锁的实现类对象 |
void lock() | 获得锁 |
void unlock() | 释放锁 |
案例:
class MyLock implements Runnable{
private Lock lock = new ReentrantLock();
private int title = 20;
@Override
public void run() {
while (true) {
try {
lock.lock();//上锁
if (title > 0) {
System.out.println(Thread.currentThread().getName() + ",正在销售:" + title);
title--;
} else {
System.out.println("卖完啦");
break;
}
} catch (Exception e) {
e.getMessage();
} finally {
lock.unlock();//释放锁
}
}
}
}
public class Test4 {
public static void main(String[] args) {
MyLock myLock = new MyLock();
Thread thread1 = new Thread(myLock,"线程1");
thread1.start();
Thread thread2 = new Thread(myLock,"线程2");
thread2.start();
Thread thread3 = new Thread(myLock,"线程3");
thread3.start();
}
}
线程1,正在销售:20
线程3,正在销售:19
线程3,正在销售:18
线程3,正在销售:17
线程3,正在销售:16
线程3,正在销售:15
线程3,正在销售:14
线程3,正在销售:13
线程3,正在销售:12
线程3,正在销售:11
线程3,正在销售:10
线程3,正在销售:9
线程3,正在销售:8
线程3,正在销售:7
线程3,正在销售:6
线程3,正在销售:5
线程3,正在销售:4
线程3,正在销售:3
线程3,正在销售:2
线程3,正在销售:1
卖完啦
卖完啦
卖完啦
6.4死锁
1.死锁是指两个或两个以上的进程在执行过程中,由于竞争资源(锁)或者由于彼此通信而造成的一种阻塞的现象。
2.若无外力作用,它们都将无法推进下去。
3.然后这个时候系统就处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
产生死锁的必要条件:
1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用。
2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
3.请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
4.循环等待,即存在一个等待循环队列:p1要p2的资源,p2要p1的资源,就形成了等待环路。
死锁的现象:
多线程间由于竞争资源导致互相僵持的一种状态
synchronized嵌套请求是死锁的一种形式
7.线程通信(生产者消费者模式)
多个线程因为在同一个进程中,所以互相通信比较容易的。
线程通信的经典模型:生产者与消费者问题。生产者负责生成商品,消费者负责消费商品。生产不能过剩,即消费不能没有。一般是生产->消费,生产-消费这样一种模型。
注意:
线程通信通常是多个线程在操作同一个资源才需要进行通信。
线程通信必须先保证线程安全,否则毫无意义,代码也会报错!
为了体现生产和消费过程中的等待和唤醒,有以下几个方法(Object类中)
方法名称 | 作用 |
---|---|
void wait() | 让当前线程等待,直到另一个线程调用该对象的notify()方法或者notifyAll()方法 |
void notify() | 唤醒正在等待对象监视器(锁对象)的单个线程 |
void notifyAll() | 唤醒正在等待对象监视器(锁对象)的所有线程 |
上述方法必须使用锁对象(对象监视器)进行调用
案例:
生活中例子:
卖家: 汽车厂商
买家: 咱们75名学生
张启想买一辆 比亚迪汉 , 告知汽车厂商我要买车。这个张启会进入倒等待状态
等到比亚迪厂家造完完以后,再通知张启来提车。如果比亚迪厂家有现车,张启就直接提车。
如果产品需要生产的话,消费者进入到阻塞状态
如果产品不需要生产的话,消费者直接购买
class Goods {
private String name;//名字
private double price;//价格
private boolean isProduct;//是否有这个商品, true 需要生产
//false 不需要生产商品
public Goods(String name, double price, boolean isProduct) {
this.name = name;
this.price = price;
this.isProduct = isProduct;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public boolean isProduct() {
return isProduct;
}
public void setProduct(boolean product) {
isProduct = product;
}
@Override
public String toString() {
return "Goods{" +
"name='" + name + '\'' +
", price=" + price +
", isProduct=" + isProduct +
'}';
}
}
//消费者线程
class Customer implements Runnable {
private Goods goods;
public Customer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (true) {
synchronized (goods) {
//一直消费
//goods.isProduct() true 需要生产 没有商品
//false 不需要生产的
if (!goods.isProduct()) {
System.out.println("消费者购买了:" + goods.getName() + ",价格为:" + goods.getPrice());
//购买完以后 商品没了 isProduct 是true 没有商品了
goods.setProduct(true);
//唤醒生产者去生产
goods.notify();
} else {
//需要生产 消费者进入阻塞
try {
goods.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
//生产者线程
class Productor implements Runnable {
private Goods goods;
public Productor(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
int count = 0 ;
while (true) {//一直生产商品
synchronized (goods) {
if (goods.isProduct()) {//true 需要生产者
//造车,奇数造一种车, 偶数的话造另外一种车
if (count % 2 == 0) {//偶数
goods.setName("玛莎拉蒂");
goods.setPrice(200);
} else {
goods.setName("五菱宏光");
goods.setPrice(400);
}
//生产完以后一定要记得 将标记改false
goods.setProduct(false);
System.out.println("生产者生产了:" + goods.getName() + ",价格为:" + goods.getPrice());
count++;
//生产者完了以后,人家消费者在等待这你呢
//唤醒消费者
goods.notify();
} else {
//不需要生产车,你就等着就行
try {
goods.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
public class Demo2 {
public static void main(String[] args) {
Goods goods = new Goods("东风", 89, false);
//false不需要生产的 谁阻塞 生产者阻塞
//最好是让生产者线程先执行
Productor productor = new Productor(goods);
new Thread(productor).start();
Customer customer = new Customer(goods);
new Thread(customer).start();
/**
* 消费者购买了:东风,价格为:89.0
* 生产者生产了:玛莎拉蒂,价格为:200.0
* 消费者购买了:玛莎拉蒂,价格为:200.0
* 生产者生产了:五菱宏光,价格为:400.0
* 消费者购买了:五菱宏光,价格为:400.0
* 生产者生产了:玛莎拉蒂,价格为:200.0
* isProduct = false
* 走了else 代码是生产者阻塞了 消费在睡了一秒之后立马执行
* 看消费者 有商品 买走了,之后 isProduct=true
* 唤醒生产者了,
* 生产者生产了:玛莎拉蒂,价格为:200.0 isProduct=false
*
*/
}
}
8.线程状态(生命周期)
1.新建状态(New 创建线程对象) : 当线程对象创建后就进入了新建状态.如:Thread t = new MyThread();
2.就绪状态(Runnable start方法): 当调用线程对象的start()方法,线程即为进入就绪状态.
(处于就绪(可运行)状态的线程,只是说明线程已经做好准备,随时等待CPU调度执行,并不是执行了t.start()此线程立即就会执行)
3.运行状态(Running): 当CPU调度了处于就绪状态的线程时,此线程才是真正的执行,即进入到运行状态。(就绪状态是进入运行状态的唯一入口,也就是线程想要进入运行状态状态执行,先得处于就绪状态)
4.阻塞状态(Blocked 无法获得锁对象): 处于运状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入就绪状态才有机会被CPU选中再次执行.
根据阻塞状态产生的原因不同:
阻塞状态又可以细分成三种:
(1)等待阻塞:运行状态中的线程执行wait()方法,本线程进入到等待阻塞状态
(2)同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程占用),它会进入同步阻塞状态
(3)其他阻塞:调用线程的**sleep()**或者join()或发出了I/O请求时,线程会进入到阻塞状态.当sleep()状态超时.join()等待线程终止或者超时或者I/O处理完毕时线程重新转入就绪状态
**5.死亡状态(Dead):**线程执行完了或者因异常退出了run()方法,该线程结束生命周期
版权声明:本文标题:【Java:线程与进程 详解+案例】 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/xitong/1729694197a1210380.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论