1.守护线程
线中有守护线程,也有非守护线程
例如:main主函数,JVM垃圾回收站都是非守护线程
1.1功能
守护线程是用来守护非守护线程的,也就是说,当非守护线程结束时,守护线程也会随之消亡。
1.2守护线程的格式
thread.setDaemon(true);//如果为true,定义为守护线程
1.3案例
package com.qfedu.a_zht;
class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("ing........" + i + "%");
}
}
}
public class Demo1 {
public static void main(String[] args) {
Thread thread = new Thread(new MyThread());
//在线程启动之前调用设置守护线程
thread.setDaemon(true);
thread.start();
for (int i = 0; i < 300; i++) {
System.out.println("主线程正在执行" + i);
}
}
}
2.死锁
2.1简介
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
2.2产生条件
虽然进程在运行过程中,可能发生死锁,但死锁的发生也必须具备一定的条件,死锁的发生必须具备以下四个必要条件。
**1)互斥条件:**指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
**2)请求和保持条件:**指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
**3)不剥夺条件:**指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
**4)环路等待条件:**指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源
2.3案例
package com.qfedu.a_zht;
class MyThread1 implements Runnable{
private boolean flag;
private Object obj1;
private Object obj2;
public MyThread1(boolean flag, Object obj1, Object obj2) {
this.flag = flag;
this.obj1 = obj1;
this.obj2 = obj2;
}
@Override
public void run() {
if (flag) {//如果线程为true,则进入
synchronized (obj1) {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "fdyh" + i);
}
//锁1在此处出不去
System.out.println("锁2");
synchronized (obj2){//无法获取锁2
System.out.println(".,.....");
}
}
}
if (!flag) {
synchronized (obj2) {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "dfghj" + i);
}
System.out.println("锁1");
//锁2被锁住
synchronized (obj1) {//无法获取锁1
System.out.println("..,,.,..,");
}
}
}
}
}
public class Demo2 {
public static void main(String[] args) {
//死锁
Object obj1 = new Object();
Object obj2 = new Object();
//获取线程1
Thread thread = new Thread(new MyThread1(true, obj1, obj2),"线程1");
thread.start();
//获取线程2
Thread thread1 = new Thread(new MyThread1(false, obj1, obj2),"线程2");
thread1.start();
}
}
3.线程生命周期
线程作为一个功能,自然有自己的开始和结束,要经历一个循环。
(1)新建:当一个Thread类或其子类的对象被声明并创建时。新生的线程对象属于新建状态。
(2)就绪(可运行状态):处于新建状态的线程执行start()方法后,进入线程队列等待CPU时间片,该状态具备了运行的状态,只是没有分配到CPU资源。
(3)运行:当就绪的线程分配到CPU资源便进入运行状态,run()方法定义了线程的操作。
(4)阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时终止自己的的执行,进入阻塞状态。
(5)死亡:当线程执行完自己的操作或提前被强制性的终止或出现异常导致结束,会进入死亡状态。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D8uULsio-1660224768262)(C:\Users\张海涛\Desktop\5.png)]
4.和线程相关的Object方法
4.1wait:
让线程进入休眠状态,而且是一直休眠,除非被唤醒
4.2notify(),notifyAll()
notify:唤醒休眠的线程
notifyAll():唤醒所有的休眠的线程
注意:两个方法都需要使用进行加锁,以确保对象唯一性
package com.qfedu.c_zht;
class Message {
}
class Write implements Runnable {
private Message mgs;
public Write(Message mgs) {
this.mgs = mgs;
}
@Override
public void run() {
synchronized (mgs) {
for (int i = 0; i < 30; i++) {
System.out.println("二狗" + i);
}
System.out.println("等待时间" + System.currentTimeMillis());
//线程进入休眠
try {
mgs.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我睡醒了" + System.currentTimeMillis());
}
}
}
class Note implements Runnable{
private Message mgs;
public Note(Message mgs) {
this.mgs = mgs;
}
@Override
public void run() {
synchronized (mgs){
for (int i = 0; i < 10; i++) {
System.out.println("翠花");
}
//唤醒线程
mgs.notifyAll();
System.out.println("我要唤醒你" + System.currentTimeMillis());
}
}
}
public class Demo2 {
public static void main(String[] args) {
//Message是为了两个线程是同一个对象
Message message = new Message();
Thread thread = new Thread(new Write(message), "线程一");
thread.start();
new Thread(new Note(message),"线程二").start();
}
}
拓展join()
join()方法,因为join()方法底层是就是通过wait()方法实现的。
让"主线程"等待(WAITING状态),一直等到其他线程不再活动为止,然后"主线程"再执行
join在英语中是“加入”的意思,join()方法要做的事就是,当有新的线程加入时,主线程会进入等待状态,一直到调用join()方法的线程执行结束为止。
面试题:
如何先让所有的子线程执行,最后再执行主线程,咋解决?
join!
用于在当前线程A中添加别的线程B,这时线程A被阻塞,处于Blocked状态,线程B开始执行,当线程B执行完以后,线程A处于可运行状(Runnable),等待cpu的调度再执行。
其中,join()方法有两种调用方式:无参调用以及有参调用。
1.thread.join(); 不指定当前线程wait的时间,这会导致当前线程一直阻塞至thread执行完毕
2.thread.join(long time); 指定当前线程wait时间,这会导致当前线程一直阻塞time毫秒或者在time时间内,thread线程便已经执行完毕,那么只需要等待thread执行的执行时间即可。
package com.qfedu.c_zht;
class Father implements Runnable{
@Override
public void run() {
Thread thread = new Thread(new Son(),"线程一");
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
}
class Son implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
}
public class Demo1 {
public static void main(String[] args) {
Thread thread = new Thread(new Father(), "主线程");
thread.start();
}
}
5.生产者消费者模式【重点】
1.意义
生产者消费者模式 为信息传输开辟了一个崭新的概念,因为它的优先级最高,所以即使网络发生堵塞时它也会最先通过,最大程度的保证了设备的安全。也有缺点,就是在网络中的个数是有限制的。生产者消费者模式在设置时比较简单,使用方便安全,在将来的自动化行业必定会大大被人们所认同。
2.消费模式
生产者消费者模式 是Controlnet网络中特有的一种传输数据的模式。用于两个CPU之间传输数据,即使是不同类型同一厂家的CPU也可以通过设置来使用
3.传输原理
类似与点对点传送,又略有不同,一个生产者可以对应N个消费者,但是一个消费者只能对应一个生产者,每个生产者消费者对应一个地址,占一个网络节点,属于预定性数据,在网络中优先级最高。
由于此模式如果在网络中设置过多会影响网络传输速度,一般用在传输比较重要的信息上,比如设备的启动、停止、故障、急停等等,因为在Controlnet网络中节点数是有限制的,最高节点数为99。
如果两个控制器之前建立了多个生产者消费者的连接,只要一个失败,则所有的均失败。将数据整合到用户自定义结构或数组中 ,两个控制器中只保留一个连接。
4.案例
package com.qfedu.e_zht;
class Goods{
private String name;
private double price;
private boolean isProduct;
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() {
//业务逻辑
while (true){//消费无止境
synchronized (goods) {
//goods.isProduct() true 需要生产 false 不需要生产
if (!goods.isProduct()){
//不需要生产的,消费者直接购买
System.out.println("消费者购买了:" + goods.getName() + "价格为:" + goods.getPrice());
goods.setProduct(true);
goods.notify();
} else {
try {
goods.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
class Producter implements Runnable{
private Goods goods;
public Producter(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
int count = 0;//计数器
while (true) {
synchronized (goods) {
if (goods.isProduct()) {
if (count % 2 ==0) {
goods.setName("奥巴马A4");
goods.setPrice(9999999.0);
} else {
goods.setName("泵波赛特A6");
goods.setPrice(626262.0);
}
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("华丰74", 6632, false);
new Thread(new Producter(goods)).start();
new Thread(new Customer(goods)).start();
}
}
6.ThreadPoolExecutor【以后开发要用的】
package com.qfedu.e_zht;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Demo2 {
public static void main(String[] args) {
//corePoolSize :核心线程数
// maximumPoolSize 线程池最多可以运行线程数
//keepAliveTime:线程存活时间
//TimeUnit unit,
//LinkedBlockingDeque<>() 阻塞队列
// ThreadFactory
//RejectedExecutionHandler handler 异常打印
ThreadPoolExecutor tpe = new ThreadPoolExecutor(8, 100, 1000, TimeUnit.SECONDS, new LinkedBlockingDeque<>(8));
for (int i = 0; i < 100; i++) {//线程池决定能够执行的任务数
final int index = i;
tpe.execute(new Runnable() {
@Override
public void run() {
System.out.println(index + "被执行线程名:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
6.1七大创建方法
方法 含义
Executors.newFixedThreadPool() 创建一个大小固定的线程池,可控制并发的线程数,超出的线程会在队列中等待
Executors.newCachedThreadPool() 创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程
Executors.newSingleThreadExecutor() 创建单个线程的线程池,可以保证先进先出的执行顺序
Executors.newScheduledThreadPool() 创建一个可以执行延迟任务的线程池
Executors.newSingleThreadScheduledExecutor() 创建一个单线程的可以执行延迟任务的线程池
Executors.newWorkStealingPool() 创建一个抢占式执行的线程池
ThreadPoolExecutor() 手动创建线程池,可自定义相关参数
`
6.1七大创建方法
方法 含义
Executors.newFixedThreadPool() 创建一个大小固定的线程池,可控制并发的线程数,超出的线程会在队列中等待
Executors.newCachedThreadPool() 创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程
Executors.newSingleThreadExecutor() 创建单个线程的线程池,可以保证先进先出的执行顺序
Executors.newScheduledThreadPool() 创建一个可以执行延迟任务的线程池
Executors.newSingleThreadScheduledExecutor() 创建一个单线程的可以执行延迟任务的线程池
Executors.newWorkStealingPool() 创建一个抢占式执行的线程池
ThreadPoolExecutor() 手动创建线程池,可自定义相关参数