多线程的操作方法

多线程的操作方法基本上都在Thread类之中定义的,所以研究方法也就是一个Thread类的翻译,那么下面主要看三组方法。

1.线程的命名和取得

在整个多线程的操作之中,名称是最重要的,因为多线程的运行状态是不确定的,那么系统只能根据名字来判断出是那一个线程,所以在为线程命名的时候不要有重复,更不要修改。
  在Thread类里面提供了如下的几个方法进行名称操作:。

  • 构造方法:public Thread(Runnable target, String name);·
  • 设置名字:public final void setName(String name);·
  • 取得名字:public final String getName()。

问题是线程的运行状态不确定,所以要想取得线程的名字,只能够取得当前正在执行的线程。那么在这种情况下,如果要想取得当前的线程对象,就必须依靠Thread类的方法:public static Thread currentThread()。

范例:取得线程名字

package cn.mldn.demo;class MyThread implements Runnable{public void run(){   //线程的主方法System.out.println(Thread.currentThread().getName());}
}
public class TextDemo {public static void main(String[] args){MyThread mt = new MyThread();new Thread(mt).start() ;new Thread(mt).start() ;new Thread(mt,"带名字").start() ;}
}

发现此时如果设置了线程名称,那么就会返回设置的数据。如果没有设置线程名字会自动的进行命名,以保证每一个线程一定有个名字。

范例:观察如下代码

package cn.mldn.demo;class MyThread implements Runnable{public void run(){   //线程的主方法System.out.println(Thread.currentThread().getName());}
}
public class TextDemo {public static void main(String[] args){MyThread mt = new MyThread();new Thread(mt,"自定义线程").start() ;mt.run();   //主方法之中进行调用}
}

现在发现通过“mt.run()”执行的时候取得的线程名称是main,所以可知:主方法也是一个线程,而新的问题就出现了,一直在讨论线程,但线程依附于进程,进程也在那里?

每当用户使用Java命令解释一个类的时候,对操作系统而言,都会默认启动一个Java的进程,而主方法只是这进程之中的一个线程而已。每一个JVM运行时至少要启动两个线程:主线程、GC垃圾回收线程。

2.线程休眠,线程的休眠指的是让程序暂缓执行,休眠方法:public static void sleep(long millis)throws InterruptedException;

范例:观察休眠

package cn.mldn.demo;class MyThread implements Runnable{public void run(){   //线程的主方法for(int x = 0; x<100; x++){try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}System.out.println(Thread.currentThread().getName());}}
}
public class TextDemo {public static void main(String[] args){MyThread mt = new MyThread();new Thread(mt,"线程A").start() ;new Thread(mt,"线程B").start() ;new Thread(mt,"线程C").start() ;new Thread(mt,"线程D").start() ;new Thread(mt,"线程E").start() ;}
}

由于线程资源的间隔很短,那么可以简单地理解为,多个线程有可能会同时进入到方法执行。

3.线程的优先级

从理论上讲,线程的优先级越高,越有可能最先执行。但这也只是可能,如果想进行线程的优先级操作,可以使用如下的两个方法:

  • 设置优先级:public final void setPriority(int newPriority);
  • 取得优先级:public final int getPriority();

对于优先级在Thread类之中定义了三个常量:

  • 最高优先级:public static final int MAX_PRIORITY, 10;
  • 中等优先级:public static final int NORM_PRIORITY, 5;
  • 最低优先级:public static final int MIN PRIORITY, 1。

范例:观察优先级改变

package cn.mldn.demo;class MyThread implements Runnable{public void run(){   //线程的主方法for(int x = 0; x<20; x++){try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}System.out.println(Thread.currentThread().getName());}}
}
public class TextDemo {public static void main(String[] args){MyThread mt = new MyThread();Thread mt1 = new Thread(mt,"线程A");Thread mt2 = new Thread(mt,"线程B");Thread mt3 = new Thread(mt,"线程C");mt1.setPriority(Thread.MIN_PRIORITY);mt3.setPriority(Thread.MAX_PRIORITY);mt1.start();mt2.start();mt3.start();}
}

问题:那么主线程的优先级是多少呢

public class TextDemo {public static void main(String[] args){System.out.println(Thread.currentThread().getPriority());}
}

主线程只是一个中等优先级。

线程的同步与死锁

1.线程的同步

当多个线程访问同一资源的时候一定需要考虑到同步问题,那么下面首先经过一个简单的买票程序,来观察同步问题的情况。

范例:观察问题

package cn.mldn.demo;class MyThread implements Runnable{private int ticket = 8 ;public void run(){   //线程的主法for(int x = 0 ; x < 20 ; x ++ ){if(this.ticket > 0){System.out.println(Thread.currentThread().getName() + ",ticket = " + this.ticket--);}}}
}
public class TextDemo {public static void main(String[] args){MyThread mt = new MyThread();new Thread(mt,"票贩子A").start();new Thread(mt,"票贩子B").start();new Thread(mt,"票贩子C").start();new Thread(mt,"票贩子D").start();new Thread(mt,"票贩子E").start();}
}

那么在这个时候就发生了不同步的情况,而发生的原因也很简单。对于整个卖票过程实际上是两步完成的

  • 第一步:使用if语句判断是否有票;
  • 第二步:进行票数的修改。

但是此时有可能会出现这样一种情况:如果说现在只有最后一张票了,可以满足if条件判断,所以线程可以通过if语句拦截,可是在修改票之前出现了一个延迟操作,那么有可能在这个延迟的过程之中,又出现了其它线程进入到方法中,由于此时没有修改票数,那么这个线程也可以满足if条件判断,后面的线程依次类推,所以当休眠时间已过,进行票数修改的时候,都将在已有的票数上修改,自然就有可能出现负数。
  那么既然已经清楚了问题的产生原因,下面就必须进行问题的解决,问题的解决关键是需要一把锁。如果要想上好这把“锁”,则可以采用两种方式完成:同步代码块、同步方法。 I
范例:使用同步代码块
  同步代码块是使用synchronized关键字定义的代码块,但是在进行同步的时候一定要设置好一个同步对象,所以这个同步对象一般使用当前对象this表示。

package cn.mldn.demo;class MyThread implements Runnable{private int ticket = 8 ;public void run(){   //线程的主法for(int x = 0 ; x < 20 ; x ++ ){if(this.ticket > 0){try{Thread.sleep(10000);}catch(InterruptedException e){e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ",ticket = " + this.ticket--);}}}
}
public class TextDemo {public static void main(String[] args){MyThread mt = new MyThread();new Thread(mt,"票贩子A").start();new Thread(mt,"票贩子B").start();new Thread(mt,"票贩子C").start();new Thread(mt,"票贩子D").start();new Thread(mt,"票贩子E").start();}
}

此时的程序执行速度变得明显缓慢了,因为需要一个个排队进行,就好比你们上网和去银行取钱。一定是上网快。虽然性能变慢了,但是数据的安全性提高了,也就是说异步处理(不加synchronized)和同步处理(synchronized)的使用区别也在于此。
范例:同步方法实现

如果在一个方法的声明上使用synchronized关键字,则表示此方法是一个同步方法。

package cn.mldn.demo;class MyThread implements Runnable{private int ticket = 8 ;public void run(){for(int x = 0 ; x < 20 ; x ++ ){this.sale();}}public synchronized void sale(){   //线程的主法if(this.ticket > 0){try{Thread.sleep(10000);}catch(InterruptedException e){e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ",ticket = " + this.ticket--);}}
}
public class TextDemo {public static void main(String[] args){MyThread mt = new MyThread();new Thread(mt,"票贩子A").start();new Thread(mt,"票贩子B").start();new Thread(mt,"票贩子C").start();new Thread(mt,"票贩子D").start();new Thread(mt,"票贩子E").start();}
}

同步的核心意义:一个线程要等另一个线程执行完毕

2.死锁

死锁实际上是在项目运行过程之中产生的一种问题,简单说就是多个线程访问同一个资源的时候一定要考虑同步,但是同步会影响程序的性能,同时会提升数据的安全性,过的同步会出现死锁

线程通讯的经典案例--生产者和消费者

本程序的核心结构如下:首先定义两个类,一个是生产者线程类,另一个是消费者线程类生产者每年生产完一个数据之后,消费者要取走这些数据,那么假设现在的数据有两种:

title=小动物,content= 草泥马;

title=小金子,content=不是好孩子。

范例

package cn.mldn.demo;class Message{private String title;private String content;public void setTitle(String title){this.title = title;}public void setContent(String content){this.content= content;}public String getContent(){return content;}public String getTitle(){return title;}
}
class Productor implements Runnable{private Message msg;public Productor(Message msg){this.msg=msg;}public void run(){for(int x = 0 ; x < 100 ; x++){if(x % 2 ==0){this.msg.setTitle("小动物");try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}this.msg.setContent("草泥马");}else{this.msg.setTitle("小金子");try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}this.msg.setContent("不是好孩子");}}}
}
class Custome implements Runnable{private Message msg;public Custome(Message msg){this.msg=msg;}public void run(){for(int x = 0 ; x < 100 ; x++){try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}System.out.println(this.msg.getTitle() + "," + this.msg.getContent());}}}
public class TextDemo {public static void main(String[] args){Message msg = new Message ();new Thread(new Productor(msg)).start();new Thread(new Custome(msg)).start();}
}

遗憾的是,这个时候出现了两个问题,

  • 数据错乱
  • 数据重复取出与重复设置

1.解决数据错位问题

想要解决数据错位问题,使用同步处理即可,代码如下

package cn.mldn.demo;class Message{private String title;private String content;public synchronized void set(String title , String content){this.title=title;try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}this.content=content;}public synchronized void get(){try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}System.out.println(this.title + "," + this.content);}
}
class Productor implements Runnable{private Message msg;public Productor(Message msg){this.msg=msg;}public void run(){for(int x = 0 ; x < 100 ; x++){if(x % 2 ==0){this.msg.set("小动物","草泥马");}else{this.msg.set("小金子","不是好孩子");}}}
}
class Custome implements Runnable{private Message msg;public Custome(Message msg){this.msg=msg;}public void run(){for(int x = 0 ; x < 100 ; x++){this.msg.get();}}}
public class TextDemo {public static void main(String[] args){Message msg = new Message ();new Thread(new Productor(msg)).start();new Thread(new Custome(msg)).start();}
}

现在数据没有错乱,但是重复操作更严重了

2.解决数据重复操作

如果要想解决重复的操作必须加入等待与唤醒机制。而这样的处理机制是由Object类提供的方法支持,在Object类之中有如下的方法可以使用:

  • 等待:public final void wait throws InterruptedException;
  • 唤醒第一个:public final void notify();
  • 唤全部:public final void notifyAll(),谁的优先级高就先执行。

范例:解决问题

package cn.mldn.demo;class Message{private String title;private String content;private boolean flag = true;//flag = true:表示可以生产,但是不能够取走//flag = false:表示可以取走,但不能生产public synchronized void set(String title , String content){if(this.flag == false){try{super.wait();}catch(InterruptedException e){e.printStackTrace();}}this.title=title;try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}this.content=content;this.flag = flag;super.notify();}public synchronized void get(){if(this.flag == true){try{super.wait();}catch(InterruptedException e){e.printStackTrace();}}try{Thread.sleep(10);}catch(InterruptedException e){e.printStackTrace();}System.out.println(this.title + "," + this.content);this.flag = true;super.notify();}
}
class Productor implements Runnable{private Message msg;public Productor(Message msg){this.msg=msg;}public void run(){for(int x = 0 ; x < 100 ; x++){if(x % 2 ==0){this.msg.set("小动物","草泥马");}else{this.msg.set("小金子","不是好孩子");}}}
}
class Custome implements Runnable{private Message msg;public Custome(Message msg){this.msg=msg;}public void run(){for(int x = 0 ; x < 100 ; x++){this.msg.get();}}}
public class TextDemo {public static void main(String[] args){Message msg = new Message ();new Thread(new Productor(msg)).start();new Thread(new Custome(msg)).start();}
}

解释sleep()和wait()的区别

  • sleep是Thread类定义的方法,在休眠之后可以自动唤醒;。
  • wait()是Object类定义的方法,等待之后必须使用notify()或notifyAll()手工唤醒。

2022.0727学习Java笔记之多线程编程(二)相关推荐

  1. java8 函数式编程_您必须学习Java 8的函数式编程吗?

    java8 函数式编程 我最近一直在研究Java 8,并掌握了Manning出版的" Java 8 In Action" . 让我印象深刻的第一件事是Java 8独特的销售主张是函 ...

  2. 您必须学习Java 8的函数式编程吗?

    我最近一直在研究Java 8,并掌握了Manning出版的" Java 8 In Action" . 让我印象深刻的第一件事是Java 8的独特销售主张是函数式编程. 函数现在是一 ...

  3. Java中的多线程编程(超详细总结)

    文章目录 Java中的多线程编程(超详细总结) 一.线程与多线程的概念 二.线程与进程之间的关系 三.一个线程的生命周期 四.多线程的目的和意义 五.线程的实现的方式 Java中的多线程编程(超详细总 ...

  4. 对比Java和.NET多线程编程

    这篇文章以对比的方式总结Java和.NET多线程编程. 基本概念 多线程:很多开发语言都提供多线程编程支持,比如Java,C#. 并发(concurrent):即使对于单核CPU,我们也会采用多线程等 ...

  5. 【java笔记】网络编程概述

    [java笔记]TCP通信程序_m0_52043808的博客-CSDN博客 [java笔记]网络编程:文件上传案例_m0_52043808的博客-CSDN博客 网络编程: 在一定协议下,实现两台计算机 ...

  6. 草根方式学习java中的多线程

    草根方式学习java中的多线程 下面有具体的代码和截图 源码点这里 多线程即在同一时间,可以做多件事情(说白了,就是齐头并进) 单线程就是按部就班 创建多线程有2种方式,分别是继承线程Thread类, ...

  7. 【 反向传播算法 Back-Propagation 数学推导以及源码详解 深度学习 Pytorch笔记 B站刘二大人(3/10)】

    反向传播算法 Back-Propagation 数学推导以及源码详解 深度学习 Pytorch笔记 B站刘二大人(3/10) 数学推导 BP算法 BP神经网络可以说机器学习的最基础网络.对于普通的简单 ...

  8. 【 卷积神经网络CNN 数学原理分析与源码详解 深度学习 Pytorch笔记 B站刘二大人(9/10)】

    卷积神经网络CNN 数学原理分析与源码详解 深度学习 Pytorch笔记 B站刘二大人(9/10) 本章主要进行卷积神经网络的相关数学原理和pytorch的对应模块进行推导分析 代码也是通过demo实 ...

  9. 【 线性回归 Linear-Regression torch模块实现与源码详解 深度学习 Pytorch笔记 B站刘二大人(4/10)】

    torch模块实现与源码详解 深度学习 Pytorch笔记 B站刘二大人 深度学习 Pytorch笔记 B站刘二大人(4/10) 介绍 至此开始,深度学习模型构建的预备知识已经完全准备完毕. 从本章开 ...

最新文章

  1. 转载 - sql分页优化
  2. 谷歌和 Facebook 是如何给工程师定职级和薪水的?
  3. 自动化测试===unittest配套的HTMLTestRunner.py生成html报告源码
  4. SAP Fiori Elements - how to set breakpoint to get converted xml view parsed by f
  5. 十四、PHP框架Laravel学习笔记——构造器的排序分组、子查询
  6. C++ STL 中提供的算法
  7. mysql开启url重写_开启URL伪静态的方法
  8. 牛客 赛码网 编程题JavaScript的输入输出
  9. 递归下降算法语法分析c语言
  10. Mac安装wget的两种方法
  11. 配置maven的settings文件
  12. 读债务危机0814-08年9月崩溃
  13. 【洛谷P1606】白银莲花池【最短路】
  14. vuepress 插件 markdown 拓展
  15. centos8之systemd管理服务开机自启动
  16. 推荐5种不错的开源虚拟化技术软件
  17. 数字信号处理专业术语翻译
  18. 2022年美赛C题-交易策略-完整解题论文和代码
  19. 魔力宝贝高清单机计划(一) 图库提取
  20. 三十八载,Oracle伴我同行—记我的职业成长之路

热门文章

  1. 堡垒机介绍和跳板机的简单实现
  2. mysql跳板机怎么搭建_Jumpserver跳板机的搭建和部署
  3. “钱三篇”后续之复利现值
  4. K3 WISE 开发插件《K3 WISE常用数据表整理》
  5. 54.Python的def语句自定义函数
  6. 互联网时代,VR全景营销的意义是什么?
  7. 一键打造自己的Gilde图片加载控件
  8. iOS开发-canOpenURL: failed for URL: mqq:// - error: This app is not allowed to query for scheme mqq
  9. windows环境下MySQL8.0.30的下载安装
  10. Swift WKWebView