这次是深夜2点还在肝文章的我,学习使我快乐,学习是世界上最快乐的事。

正文分割线

一、死锁

1、定义:多个线程之间,由于针对多个对象进行上锁,导致的多个线程之间,需要相互等待对方释放对象的过程。你可以把线程想象为上厕所,当所有茅坑被占了,其他线程就必须在厕所门口等着。如果这个线程在厕所里看电影,那么就得一直等着。

2、死锁产生的条件:

互斥使用:针对同一个资源,提供了互斥锁。就是这个资源只能同时被一个线程访问,当两个线程同时访问这个线程时,其中一个线程必须等待。

不可剥夺:当某个线程占用一个对象锁以后,这个对象就不能被其他线程进行剥夺。

相互等待:A线程需要的资源被B线程占用,B线程需要的资源被A线程占用。双方都在等待对方释放资源。

二、线程之间的通讯:

1、线程通讯:线程之间的通讯可以使用某个对象进行传输。但是在传输过程中,我们得保证这个对象的唯一性。就像你说给别人许下的承诺最好还是实现比较好,传输过程中,我们得保证这个对象由一个线程一个线程的调用,这样才能保证传输的正确性。

2、涉及线程通讯的方法:

wait方法:让线程处于等待式阻塞状态

notify方法:让线程唤醒处于等待式的线程

notifyAll:可以让线程去唤醒所有在等待中的线程

上述的所有方法都需要配合synchronized关键词使用

三、线程池

1、线程池的定义:包含很多个线程的集合。当我们使用Runable接口和Thread类时,每一次调用都会创建新的线程,而使用结束后该线程会被销毁。这样的频繁地创建和销毁线程比较耗费系统资源。所以我们定义了一个包含多个线程的集合,任务会被分配给线程池中的线程。当任务执行完毕以后,线程又会回到线程中等待下一个任务的来临。

2、Executors ThreadPoolExecutor的区别

这2个类,都可以产生线程池,区别在于:Executors 线程量不高的,需要快捷使用线程的;ThreadPoolExecutor 线程量很高的。

3、Runable和Callable的区别:

两个都是定义任务的接口,但是后者可以返回任务的结果。而且后者需要配合线程池使用。后者在执行的时候是有序的,前者是无序的。

4、面试题(经典)

主线程-包含10根子线程,问你?一般来讲线程的执行顺序是无序的,而且是获取不到返回结果,问你:你如何保证 主线程 在所有的子线程执行完毕之后,方可继续执行,而且要输出子线程的结果

答案:使用Callable定义任务,使用Future 接收任务的结果。

5、sleep() 和 wait() 的区别(面试题)

1、sleep() 是Thread类的静态方法,代表是让当前线程休眠

wait() 是Object类提供的,代表是让当前线程出于等待

2、sleep()休眠一定时间,会自动唤醒,并且让线程出于Rannable就绪状态,

而wait()必须需要其他线程来唤醒(notify() notifyAll())

3、sleep()在休眠期间,不会释放线程所持有的内存资源,但是会释放CPU的执行权,wait() 在等待期间,会释放线程所持有的内存资源,同样也会释放CPU的执行权

代码举例

死锁发生的代码

package com.woniuxy.test;

/*** 死锁的代码* @author 84980**/

public class ThreadStudy {

//启动线程public static void main(String[] args) {

Thread t1 = new Thread(new Task(1));

Thread t2 = new Thread(new Task(2));

//开启线程1和线程2t1.start();

t2.start();

}

}

//1、实现runable接口的的Task方法class Task implements Runnable{

//给一个flag,根据flag的取值(1或者)去执行不同的变量private int flag;

private static Object o1=new Object();

private static Object o2=new Object();

//flag的输入public Task(int flag) {

super();

this.flag = flag;

}

@Override

public void run() {

if(flag==1) {

//如果flag是1,就锁住o2这个代码块synchronized (o1) {

//Thread.currentThread是指当前执行的线程System.out.println(Thread.currentThread().getName()+"锁住了o1");

//休眠1stry {

Thread.sleep(1000);

} catch (InterruptedException e) {

// TODO 自动生成的 catch 块e.printStackTrace();

}

//试图去锁住o2synchronized (o2) {

System.out.println(Thread.currentThread().getName()+"锁住了o2");

}

}

}else {

//如果flag是1,就锁住o2这个代码块synchronized (o2) {

System.out.println(Thread.currentThread().getName()+"锁住了o2");

//休眠1stry {

Thread.sleep(1000);

} catch (InterruptedException e) {

// TODO 自动生成的 catch 块e.printStackTrace();

}

//试图去锁住o2synchronized (o1) {

System.out.println(Thread.currentThread().getName()+"锁住了o1");

}

}

}

}

}

线程之间的通讯

package com.woniuxy.test;

import java.io.Serializable;

import java.util.UUID;

/*** 使用一个生产者和消费者的问题来演示线程之间的通讯* 只有当生产者生发送了消息,消费者才能收到消息。* 也就是说本程序有两个线程,一个生产者线程,另一个是消费者线程* 只有当生产者发送了消息,消费者线程才会执行* @author 84980**/

public class Improtant {

public static void main(String[] args) {

//实例化消息类InformationBean msg = new InformationBean();

//开启线程Thread t1=new Thread(new ProducerTask(msg));

Thread t2=new Thread(new ConsumerTask(msg));

//启动线程t1.start();

t2.start();

}

}

/*** 产生消息的类* @author 84980**/

class InformationBean implements Serializable{

/**当你实现了序列化接口的时候* eclipse编译器自动生成的序列化id*/

private static final long serialVersionUID = 1L;

//消息private String message;

//无参构造器public InformationBean() {

super();

}

//有参构造器public InformationBean(String message) {

super();

this.message = message;

}

public String getMessage() {

return message;

}

public void setMessage(String message) {

this.message = message;

}

}

/*** 消息产生类* @author 84980**/

class ProducerTask implements Runnable{

//消息变量private InformationBean msg;

//有参构造器public ProducerTask(InformationBean msg) {

super();

this.msg = msg;

}

@Override

public void run() {

// TODO 自动生成的方法存根while(true) {

//间隔2S产生一次信息try {

Thread.sleep(2000);

} catch (InterruptedException e) {

// TODO 自动生成的 catch 块e.printStackTrace();

}

//产生一段不重复的36位随机字符串String str = UUID.randomUUID().toString();

//设置消息msg.setMessage(str);

System.out.println(Thread.currentThread().getName()+"产生的消息是:"+str);

}

}

}

/*** 消息的接受类* @author 84980**/

class ConsumerTask implements Runnable{

//同样设置一个消息类private InformationBean msg;

public ConsumerTask(InformationBean msg) {

super();

this.msg = msg;

}

@Override

public void run() {

//获取消息while(true) {

System.out.println(Thread.currentThread().getName()+"接受到的消息是:"+msg.getMessage());

}

}

}

执行结果:线程之间无法判断谁在之前执行,谁在之后执行。所以就会造成通讯问题,两个线程互不干涉,接受信息的线程并不会等待发送消息的线程。

下面我修改代码中的一些地方来实现有序地发送和接受信息(主要是修改了其中的get和set方法)

package com.woniuxy.test;

import java.io.Serializable;

import java.util.UUID;

/*** 使用一个生产者和消费者的问题来演示线程之间的通讯* 只有当生产者生发送了消息,消费者才能收到消息。* 也就是说本程序有两个线程,一个生产者线程,另一个是消费者线程* 只有当生产者发送了消息,消费者线程才会执行* @author 84980**/

public class Improtant {

public static void main(String[] args) {

//实例化消息类InformationBean msg = new InformationBean();

//开启线程Thread t1=new Thread(new ProducerTask(msg));

Thread t2=new Thread(new ConsumerTask(msg));

//启动线程t1.start();

t2.start();

}

}

/*** 产生消息的类* @author 84980**/

class InformationBean implements Serializable{

/**当你实现了序列化接口的时候* eclipse编译器自动生成的序列化id*/

private static final long serialVersionUID = 1L;

//消息private String message;

//无参构造器public InformationBean() {

super();

}

//有参构造器public InformationBean(String message) {

super();

this.message = message;

}

//添加锁public synchronized String getMessage() {

//接受消息的时候等待try {

wait();

} catch (InterruptedException e) {

// TODO 自动生成的 catch 块e.printStackTrace();

}

return message;

}

//添加锁public synchronized void setMessage(String message) {

this.message = message;

//发送消息以后唤醒线程notifyAll();

}

}

/*** 消息产生类* @author 84980**/

class ProducerTask implements Runnable{

//消息变量private InformationBean msg;

//有参构造器public ProducerTask(InformationBean msg) {

super();

this.msg = msg;

}

@Override

public void run() {

// TODO 自动生成的方法存根while(true) {

//间隔2S产生一次信息try {

Thread.sleep(2000);

} catch (InterruptedException e) {

// TODO 自动生成的 catch 块e.printStackTrace();

}

//产生一段不重复的36位随机字符串String str = UUID.randomUUID().toString();

//设置消息msg.setMessage(str);

System.out.println(Thread.currentThread().getName()+"产生的消息是:"+str);

}

}

}

/*** 消息的接受类* @author 84980**/

class ConsumerTask implements Runnable{

//同样设置一个消息类private InformationBean msg;

public ConsumerTask(InformationBean msg) {

super();

this.msg = msg;

}

@Override

public void run() {

//获取消息while(true) {

System.out.println(Thread.currentThread().getName()+"接受到的消息是:"+msg.getMessage());

}

}

}

队列

下面是队列的基本用法,你也可以在多线程程序中使用队列,发送消息的那一方把消息放入队列,接受消息的从队列中拿出数据。

package com.woniuxy.java25.study.queue;

import java.util.UUID;

import java.util.concurrent.ArrayBlockingQueue;

public class QueueStudy {

public static void main(String[] args) {

//ArrayBlockingQueue 是线程安全的 //rabbitmq == queue队列 //创建一个队列,并指定队列的大小 500 ArrayBlockingQueue queue = new ArrayBlockingQueue(500);

for(int i = 0; i < 500; i ++) {

queue.add(UUID.randomUUID().toString());

}

//遍历(1) queue.forEach(e -> System.out.println(e));

System.out.println(queue.size());

//遍历(2) //迭代器 //检索(不删除) //从队列的头部开始,获取第一个元素 System.out.println(queue.peek());

System.out.println(queue.size());

//检索(删除) //从队列的头部开始,获取第一个元素 System.out.println(queue.poll());

System.out.println(queue.size());

}

}

线程池

package com.woniuxy.test;

import java.io.Serializable;

import java.util.UUID;

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

/*** 线程池* @author 84980**/

public class Improtant {

public static void main(String[] args) {

//产生一个内置10线程的线程池//ExecutorService es = Executors.newFixedThreadPool(10);//产生一个不限量的线程的线程池//ExecutorService es = Executors.newCachedThreadPool();//产生一个内置一根线程的线程池ExecutorService es = Executors.newSingleThreadExecutor();

//通过future对象来调用线程池中的线程,在这里调用实现了Runable接口的实现类Future future = es.submit(new SayTask());

try {

System.out.println(future.get());

} catch (InterruptedException e) {

// TODO 自动生成的 catch 块e.printStackTrace();

} catch (ExecutionException e) {

// TODO 自动生成的 catch 块e.printStackTrace();

}

//关闭线程池es.shutdown();

}

}

/*** 定义任务* 如果线程,不需要返回一个结果,那么就使用Runable接口* @author 84980**/

class SayTask implements Runnable{

@Override

public void run() {

//定义任务for (int i = 0; i < 10; i++) {

System.out.println("我喜欢你");

}

}

}

/*** 定义任务* 如果线程需要返回一个结果,那么就使用Callable接口* @author 84980**/

class ConsumerTask implements Callable{

@Override

public Integer call() throws Exception {

//这是Callable的线程任务方法for (int i = 0; i < 10; i++) {

System.out.println("我喜欢你");

}

//可以返回一个值return 10;

}

}

Runnable 和Callable的区别

package com.woniuxy.test;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

/*** Runable和Callable的区别* @author 84980**/

public class ThreadStudy {

public static void main(String[] args) {

//主线程开启System.out.println("主线程开始");

//这个是使用Calable的方法//study();//这是使用Runable的方法//study01();System.out.println("主线程关闭");

}

/*** Runable演示方法*/

private static void study01() {

// 产生一个内置10线程的线程池ExecutorService es = Executors.newFixedThreadPool(10);

Future future = es.submit(new Task1());

try {

System.out.println(future.get());

} catch (InterruptedException e) {

// TODO 自动生成的 catch 块e.printStackTrace();

} catch (ExecutionException e) {

// TODO 自动生成的 catch 块e.printStackTrace();

}

es.shutdown();

}

/*** Callable演示方法*/

private static void study() {

// 产生一个内置10线程的线程池ExecutorService es = Executors.newFixedThreadPool(10);

//使用拉姆达表达式,因为Callable中只有一个方法,所以可以简写成这样//Integer指得是返回值的类型Future re01 = es.submit(()->{

return 10;

});

Future re02 = es.submit(()->{

return 20;

});

Future re03 = es.submit(()->{

return 12;

});

Future re04 = es.submit(()->{

return 23;

});

try {

System.out.println(re01.get());

System.out.println(re02.get());

System.out.println(re03.get());

System.out.println(re04.get());

} catch (InterruptedException e) {

// TODO 自动生成的 catch 块e.printStackTrace();

} catch (ExecutionException e) {

// TODO 自动生成的 catch 块e.printStackTrace();

}

//关闭线程池es.shutdown();

}

}

class Task1 implements Runnable{

@Override

public void run() {

//定义任务for (int i = 0; i < 10; i++) {

System.out.println("我喜欢你");

}

}

}

poll和死锁_Java从入门到入土day20 死锁 多线程通讯 线程池相关推荐

  1. java同步锁售票_Java基础学习笔记: 多线程,线程池,同步锁(Lock,synchronized )(Thread类,ExecutorService ,Future类)(卖火车票案例)...

    学习多线程之前,我们先要了解几个关于多线程有关的概念. 进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线程:线程是 ...

  2. java main生命周期_Java从入门到入土(62)线程的生命周期

    线程是程序内部的一个顺序控制流,他具有一个特定的生命周期.在一个线程的生命周期中,他总是处于某一种状态中.线程的状态表示了线程正在进行的活动以及在这段时间内线程能完成的任务. 线程的生命周期包括五个状 ...

  3. java写方法用来调用_Java从入门到入土(79)lambda表达式和方法引用

    lambda表达式是Java8引入的新功能.lambda表达式以字面量的形式把少量代码直接写在程序中,从而让 Java 编程更符合函数式风格(Java 实质上是面向对象语言.不过,引入lambda 表 ...

  4. java从入门到入土_Java从入门到入土100天,第四天

    第四天,String的常用方法 首先,先说明一个重点,在大部分语言中,String 都是不可变的,改变的都是建一个新的字符串然后将修改结束的字符串存到新的字符串中. 然后再提一个区别:String.S ...

  5. java jdk安装_Java从入门到入土第一课—JDK安装及环境配置

    安装步骤如下: 下载jdk 在浏览器输入:https://www.oracle.com/technetwork/java/javase/downloads 选择想要下载的jdk版本,以Java SE ...

  6. java从基础到入门_Java从入门到入土(30)继承基础

    面向对象的优势在于代码的复用,继承是实现代码复用的重要手段.Java的继承采用单继承机制,即每个子类只能继承一个父类.被继承的类称为父类,而实现继承的类称为子类.通过继承,子类继承父类的非私有属性与方 ...

  7. java从入门到入土图_Java从入门到入土day08

    好家伙,这个作者竟然妄图一天就把面向对象的两个特征说了.他可真是自不量力啊,作者心中OS:学习真累,赶快写完去打游戏真香. 正文分割线 一.抽象 1.抽象的定义:抽象,字面意思.很抽象,很多人都不懂的 ...

  8. java包装类转换_Java从入门到入土(38)包装类转换

    Java是一种面向对象的编程语言,他提供了8种基本数据类型.但是,这8种基本数据类型并不符合面向对象的编程机制,因为基本数据类型并不具有对象所具有的属性,方法,构造方法等特征.Java之所以提供8中基 ...

  9. 浅析java中的死锁_Java学习笔记五十五(死锁问题)

    多线程死锁问题. 我们知道,多线程可以改善系统的资源利用率,并且可以提高程序的运行效率.但是,多线程也带来了新的问题,即:死锁问题. 1.死锁的概念 死锁可以理解为多个线程为了争夺同一个资源,而出现互 ...

最新文章

  1. linux遭入侵挖矿进程被隐藏案例分析
  2. 测试岗位 mysql 面试题_一套实用的渗透测试岗位面试题,你会吗?
  3. 苹果浏览器移动端click事件延迟300ms的原因以及解决办法
  4. fastweixin v1.3.0 发布,极速微信公众号开发框架
  5. 《影响力》6个使人顺从的武器之一互惠原理深入剖析
  6. usb转ttl模块与matlab,图文详解USB转TTL设备与电路板的连接
  7. JS中URL中的特殊字符问题:escape,encodeURI,encodeURIComponent(转)
  8. ReentrantLock 公平锁和非公平锁加锁和解锁源码分析(简述)
  9. PHP5.4 Apache Mysql搭配与多站点配置
  10. 互联网上最可怕的女人
  11. CentOS7安装dnf
  12. 二工大计算机专业,两电一邮与哈工大:计算机专业哪所实力最强?看完就知道...
  13. Error: ImageIO: PNG invalid PNG file: iDOT doesn't point to valid IDAT chunk
  14. Java基础 -> 为什么⽤线程池?线程池的创建?
  15. 路由器NAT 类型检测实现
  16. 天猫精灵 python 控制_树莓派4B小爱同学、天猫精灵、智能音箱、百度语音极速版、百度语音标准版、语音识别、语音合成终极方案...
  17. 手机服务器 微信QQ,玩家天价买服务器语聊开黑 小白没想明白:微信QQ难道不行?...
  18. 开关电源的 PCB 布线设计,电容并联,环路面积
  19. 88.计算500以内最大的10个能被13或17整除的自然数之和。
  20. 九江大桥的设计和质量均没有问题

热门文章

  1. access2007 定义了过多字段_定义了过多的字段错误 | Microsoft Docs
  2. python 算法找出list列表里的最小值_python 列表
  3. 2022-9-18把Trie搞出来来,开心开心开心!!!!
  4. 创建 360 压缩bat文件
  5. java程序员的大数据之路(3):用maven构建Hadoop项目
  6. 【重磅】Deepmind出品-自动学习并生成图像
  7. 微信jsapi支付获取code_【微信支付】JSAPI支付开发者文档
  8. 基于RFID的仓储管理系统的设计与实现-毕业论文
  9. 华为成立四大军团,字节开启今年第2次回购,1年半回报率近300%
  10. 基于LMI的车辆主动悬架控制