1.进程和线程的概念
1.1进程的概念
举个例子:在操作系统中运行的程序就是进程,比如QQ、游戏、IDE等等
定义:进程是操作系统结构的基础;是一个正在执行的程序;计算机中正在运行的程序实例;可以分配给处理 器并由处理器执行的一个实体;由单一顺序的执行显示,一个当前状态和一组相关的系统资源所描述的 活动单元
特性:
1.独立性
各个进程之间是相互的独立的互不影响 的。录屏软件和idea没有关系的。
2.互斥性
每个软件系统都会分配一个独立端口号
1.2线程的概念
举个例子:如视频中同时听声音,看图像,看弹幕,等等。
定义:Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。线程是进程的一个 实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
除此之外,进程是由多个或者一个线程组成的。每个进程至少有一个线程支撑,进程包含了线程,一个线程是组成进程的最小基本单位。
特性:
1.抢占式运动
CPU在执行的时候,按照时间来执行的,单位的时间片是抢占是执行
2.资源共享性
一个线程可以共享当前CPU,网卡等
一个Java程序至少有两个线程:
main主函数线程
JVM垃圾回收器线程
1.3进程和线程的区别
进程是一个应用程序,是独立的
线程是进程中最小的基本单位。
进程有独立性和互斥性
线程有抢占式资源共享特性
2.并发和并行
并发:同时发生,轮流交替执行
并行:真正意义上的同时执行
从生活中举个例子:
比如:
你去饭店点了两个菜,生活中拿个筷子轮流夹菜哦这就是并发场景
端起盘子,同时倒到嘴里面,这就是并行场景
3.创建线程的两种方式
1.将一个类声明为
Thread
的子类。 这个子类应该重写run
方法 。 然后可以分配并启动子类的实例。
class First extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("线程"+i);
}
}
}
public class Demo10 {
public static void main(String[] args) {
First first = new First();
//不能再掉用run方法了,因为上面写的是线程
//只能通过调用start()方法来启动线程
first.start();
}
}
2.另一种方法来创建一个线程是声明实现类
Runnable
接口。 那个类然后实现了run
方法。 然后可以分配类的实例,在创建Thread
时作为参数传递,并启动。
class First1 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("我是第二种创建方式"+i);
}
}
}
public class Demo11 {
public static void main(String[] args) {
First1 first1 = new First1();
new Thread(first1).start();
}
}
4.线程下面的几个方法
构造方法:
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) 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。 |
代码实现:
class MyThread implements Runnable{
@Override
public void run() {
//利用currentThread()和getName()返回当前线程的名字
//返回当前正在执行的线程的对象的引用
String str = Thread.currentThread().getName();
System.out.println(str);
for (int i = 0; i < 50; i++) {
//通过sleep()方法,设置睡的时间,单位毫秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class Demo12 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
//利用Thread的有参构造起名字
Thread thread = new Thread(myThread, "子线程1");
//通过setName()方法改名字
thread.setName("新改名的线程1");
thread.start();
//获得Thread的优先级
System.out.println(thread.getPriority());
//设置Thread的优先级
thread.setPriority(9);
System.out.println(thread.getPriority());
}
}
5.线程的同步和锁
为什么要进行线程的同步?
Java是允许多线程(多个线程),当多个线程操作同一个资源的时候,会导致得到或者打印的数据不准确。从而发生冲突,这时需要加同步锁。
案列:三个线程卖50张票,一张票只能买一次。
不加锁的代码:
class Tecket implements Runnable{
int tecket = 50;
@Override
public void run() {
while (true){
if (tecket>0){
System.out.println(Thread.currentThread().getName()+"卖出第:"+tecket+"张");
tecket--;
} else {
System.out.println("卖完了!");
break;
}
}
}
}
public class Demo13 {
public static void main(String[] args) {
Tecket tecket = new Tecket();
new Thread(tecket,"线程1").start();
new Thread(tecket,"线程2").start();
new Thread(tecket,"线程3").start();
}
}
运行结果:
结果出现了线程1和线程2和线程3都卖了第50张票,显然是不合理的。所以要加锁。
加锁:
while (true){
//给当前线程对象加锁
synchronized (this) {
if (tecket > 0) {
System.out.println(Thread.currentThread().getName() + "卖出第:" + tecket + "张");
tecket--;
} else {
System.out.println("卖完了!");
break;
}
}
}
只需在上述位置加锁,便不会再出现都卖一张的情况了。
6.守护线程
守护线程是用来守护非守护线程的。
非守护线程:就是平常写的线程
一个应用程序中必须至少一个非守护线程
非守护线程一旦结束,守护线程就会自动消亡
main 主函数是非守护线程
class MyThread1 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("子线程正在执行" + i);
}
}
}
public class Demo1 {
public static void main(String[] args) {//在非守护线程中执行的
//启动线程
MyThread1 myThread1 = new MyThread1();
Thread thread = new Thread(myThread1);
//可以将myThread1设置为守护线程。
//就会在非守护线程结束De一瞬间,守护线程也会停止执行
thread.setDaemon(true);//变成了守护线程了
thread.start();
for (int i = 0; i < 200; i++) {
System.out.println("主线程正在执行" + i);
}
//在jvm中 main主线程, 还有一个线程 垃圾回收线程
//等到main主线程和垃圾回收线程 都结束De时候才守护线程才消亡!!!
}
}
7.死锁
死锁是指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁线程。
class DeadLock1 implements Runnable{
private boolean flag;
private Object obj1;
private Object obj2;
public DeadLock1(boolean flag, Object obj1, Object obj2) {
this.flag = flag;
this.obj1 = obj1;
this.obj2 = obj2;
}
@Override
public void run() {
if (flag) {
synchronized (obj1) {
System.out.println(Thread.currentThread().getName() + "拿到了锁1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("等待锁2释放....");
synchronized (obj2) {
System.out.println(Thread.currentThread().getName() + "拿到了锁2");
}
}
}
if (!flag){
synchronized (obj2) {
System.out.println(Thread.currentThread().getName() + "拿到了锁2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("等待锁1释放....");
synchronized (obj1) {
System.out.println(Thread.currentThread().getName() + "拿到了锁1");
}
}
}
}
}
public class Demo14 {
public static void main(String[] args) {
Object obj1 = new Object();
Object obj2 = new Object();
DeadLock1 deadLock1 = new DeadLock1(true,obj1,obj2);
new Thread(deadLock1,"锁1").start();
DeadLock1 deadLock11 = new DeadLock1(false, obj1, obj2);
new Thread(deadLock11,"锁2").start();
}
}
8.线程生命周期
1)新建:当一个Thread类或其子类的对象被声明并创建时。新生的线程对象属于新建状态。
(2)就绪(可运行状态):处于新建状态的线程执行start()方法后,进入线程队列等待CPU时间片,该状态具备了运行的状态,只是没有分配到CPU资源。
(3)运行:当就绪的线程分配到CPU资源便进入运行状态,run()方法定义了线程的操作。
(4)阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时终止自己的的执行,进入阻塞状态。
(5)死亡:当线程执行完自己的操作或提前被强制性的终止或出现异常导致结束,会进入死亡状态
9.和线程相关的Object类下面的方法
Object类下面的方法:
void | notify() 唤醒正在等待对象监视器的单个线程。 |
---|---|
void | notifyAll() 唤醒正在等待对象监视器的所有线程。 |
导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法。 换句话说,这个方法的行为就好像简单地执行呼叫wait(0)
。
总结:至少两个线程,其中一个线程中使用对象.wait() 那么这个线程就会阻塞,代码不会往下执行了。如何想让这个线程往下执行呢?再开另外一个线程,使用对象.notify()去唤醒另外那个等待线程。
10.生产者消费者模式
案例:
package com.qfedu.d_procus;
//为啥要新建这类Goods?
//还记得上午讲的Message类
//因为两个线程要通信。这个Goods就是通信的桥梁!!!
//goods.wait() 消费者等待
//goods.notoify() 生产者唤醒消费者
class Goods {
private String name;//商品名字
private double price;//商品价格
private boolean isProduct;//
//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 {
//为啥要定义这个goods变量? 因为两个线程要共享同一个资源!!!
private Goods goods;
public Customer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
//写业务逻辑,业务比较麻烦
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 Producter implements Runnable {
//为啥要定义这个goods变量?
private Goods goods;
public Producter(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
int count = 0;
//生产者,业务逻辑比较麻烦
while (true) {//你一直消费,我一直生产
synchronized (goods) {
if (goods.isProduct()) {//true 需要生产的!!
//造车,就是赋值 对goods对象进行赋值
//奇数造一种车, 偶数造另外一种车
if (count % 2 == 0) {//偶数
goods.setName("奥迪A8");
goods.setPrice(78.9);
} else {//奇数
goods.setName("五菱大光");
goods.setPrice(12.9);
}
//生产一辆车,一定要记得有车了
//标记就改为 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 Demo1 {
public static void main(String[] args) {
Goods goods = new Goods("东风41", 67.8, false);
Producter producter = new Producter(goods);
new Thread(producter).start();
Customer customer = new Customer(goods);
new Thread(customer).start();
}
}