带着新人看java虚拟机07(多线程篇)

时间:2021-11-08 03:18:12

  这一篇说一下比较枯燥的东西,为什么说枯燥呢,因为我写这都感觉很无聊,无非就是几个阻塞线程的方法和唤醒线程的方法。。。

1.线程中断  

  首先我们说一说怎么使得一个正在运行中的线程进入阻塞状态,这也叫做线程中断,最常见的就是Thread.sleep(1000)这种方式的,我们直接看一个简单粗暴的图:

带着新人看java虚拟机07(多线程篇)

  此图应该列举了所有中断,我们选择几个比较重要的说说,其中有几个方法已经被废弃了,原因要么就是不安全,要么就是容易产生死锁等等,图中已经划去了!

 2.等待通知和收到通知(wait、notify、notifyAll)

    由于这wait/notify这种不怎么好理解,我们就详细说说,其他的随便看看就好。。。

    大家不知道有木有发现,java中几乎所有的数据类型都重写了Object的wait()、notify()、notifyAll()这三个方法,那这三个方法到底是干什么的呢?

    前提:要用这三个方法必须要放在synchronized里面,这是规则!!!至于调用这三个方法必须是锁定(也就是我前面说的锁芯)才能调用,还有,锁定几乎可以是任何类型的!

  ·   举例下面两种用法:

带着新人看java虚拟机07(多线程篇)

带着新人看java虚拟机07(多线程篇)

    介绍一个东西,名字叫做“wait set”,这是个什么东西呢?你就把这个看作是一个存阻塞线程的集合(大白话来说就是线程的休息室,把线程用wait阻塞了就相当于让线程去休息室休息,并且线程很有自觉,去休息了之后就会释放锁),而且每个锁定(也就是我前面说的锁芯)都有一个。比如一个线程调用obj.wait()之后,那么这个线程就被阻塞了,这个阻塞的线程就被放在了obj的“wait set”中了,我们可以用一个图来表示:

带着新人看java虚拟机07(多线程篇)

    

  当线程A阻塞之后就被丢进了obj的wait set中之后,线程A就会释放当前的锁,此时线程B就可以访问这个方法或相同锁定的方法;但是假如在B中调用了notify()方法,那么就是从obje的wait set中唤醒A线程,然后直到B线程结束后释放锁,A线程才变成准备就绪状态,可以随时被CPU调度再次获得这个锁;

带着新人看java虚拟机07(多线程篇)

  注意:必须等B线程执行完之后释放锁,线程A才能变成准备就绪状态(代码是从wait方法后面的代码开始执行,不是重新开始)  

  根据上面两个图随意举个小例子:

package com.wyq.thread;

public class Bank {
Object obj = new Object();//我们随便创建一个锁定 public void tomoney(Integer money){
//在转账方法的锁中调用wait方法,此时执行这个方法的线程会中断,保存在obj的wait set中,并且该线程会释放锁其他线程可以访问相同锁定的锁
synchronized(obj){
try {
obj.wait();
System.out.println("转账:"+money+"元");
} catch (InterruptedException e) {
e.printStackTrace();
} } }
public void save(Integer money){
//在存钱方法的锁中我们调用notify从obj的wait set中唤醒存在其中的某一个线程,那个被唤醒的线程不会马上变成准备就绪状态,
  //必须要等本存钱方法的线程执行完毕释放锁,才会进入准备就绪状态
synchronized(obj){
obj.notify();
System.out.println("存钱:"+money+"元");
} } public static void main(String[] args) {
Bank bank = new Bank();
//我们可以多次运行这两个线程,总是先执行存钱方法,然后才是转账方法(其实转账线程可以利用for循环创建几十个,这样效果更明显)
new Thread(new Runnable() { @Override
public void run() {
bank.tomoney(100);
}
}).start(); new Thread(new Runnable() { @Override
public void run() {
bank.save(100);
}
}).start(); }
}

    运行结果如下:

带着新人看java虚拟机07(多线程篇)

    

  notify()是唤醒wait set集合中随意一个线程;而那个notifyAll()方法可以唤醒wait set集合中所有的线程,用法和notify一样,就不举例子了;那么我们平常应该用哪一个呢?用notify的刷唤醒的线程比较少效率高一点,但是缺点就是到底唤醒哪一个线程的实现可能有点难度,一个处理不好程序就可能挂掉了;但是用notifyAll的话效率比较低一点,但是却比较可靠稳定;

  所以啊,如果我们对于程序代码都理解得十分透彻,那就用notify比较好,否则还是用稳妥一点的notifyAll吧!

  顺便说一点,有的时候我们把一个线程阻塞之后放进wait set中之后,却忘记调用notify/notifyAll了,那么这些阻塞线程就会一直留在了wait set中,我们可以在wait()方法指定一个时间,在规定时间内如果没有被notify唤醒,那么放在wait set中的该线程就会自动唤醒!还有obj.wait()方法其实本质是调用obj.wait(0),wait(long timeout)是一个Native方法!比如obj.wait(3000)表示三秒之后会自动唤醒!这里就是随意提一下,一般很少去指定这个超时时间的

  补充wait set的定义:wait set是一个虚拟的概念,它既不是实例的字段,也不是可以获取在实例上wait中线程的列表的方法。

3.sleep和interrupt方法

  有没有觉得上面的这种用法比较麻烦,虽然在某些情况下比较适用,但是我们平常测试用的话这也太麻烦了,还有个什么锁定这种鬼东西,有没有比较简单的用法啊!

  于是我们有了sleep方法对应于wait方法,interrupt方法对应于notify方法;(注意,这里只是功能上面的对应,但是其中的原理是不相同的!)

  首先说说sleep方法,这个应该比较熟悉,直接就是Thread.sleep(xxx)这种方式来使得当前线程阻塞一定时间(注意sleep方法和wait方法最大的区别就是sleep方法不会释放锁),如果没有到达相应时间我们非要让阻塞状态的线程又重新变成准备就绪状态,就使用a.interrupt()方法,其中a指的是当前线程的实例;

  我们看一个最简单的例子:

package com.wyq.thread;

public class Bank {

    public void tomoney(Integer money){
try {
//将运行这个方法的线程停止一个星期
Thread.sleep(604800000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("转账:"+money+"元"); } public static void main(String[] args) {
Bank bank = new Bank(); Thread thread = new Thread(new Runnable() { @Override
public void run() {
bank.tomoney(100);
}
});
thread.start();
    //如果没有调用interrupt方法,那么thread这个线程就会暂停一个星期
thread.interrupt(); }
}

带着新人看java虚拟机07(多线程篇)

  会抛出一个异常这也是用interrupt方法的特色;

  随意一提:这个interrupt方法比较厉害,即使线程中断是由于wait(),join(),sleep()造成的,但是都可以用interrupt方法进行唤醒,比较厉害!

4.join()方法

  由于线程的执行是随机的,那么我们有没有设什么方法可以让线程以一定的顺序执行呢?虽然可能会有点影响性能,但这不是我们暂时关心的。

  join()方法可以让一个线程B在线程A之后才执行,我们继续看一个简单的例子;

package com.wyq.thread;

public class Bank {

    public void tomoney(String name,Integer money){

        System.out.println(name+"转账:"+money+"元");

    }

    public static void main(String[] args) {
Bank bank = new Bank(); Thread A = new Thread(new Runnable() { @Override
public void run() {
bank.tomoney("A",100);
}
}); Thread B = new Thread(new Runnable() { @Override
public void run() {
bank.tomoney("B",200);
}
}); A.start();
try {
//必要要等到A线程执行完毕才会执行其他的线程
A.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//这个时候查看一下B的线程是NEW,说明B线程还没调用start()方法,
//调用start方法之后就成Runnable状态了
System.out.println(B.getState());
B.start(); }
}

带着新人看java虚拟机07(多线程篇)

5.线程优先级和yield()方法

  其实线程优先级这这种东西比较坑,这是一个概率问题,优先级分为10级,1级最低,10级最高,我们一般创建的线程默认5级;但是优先级高的线程不一定先执行,这个优先级的意思就是线程获取CPU调用的概率比较大,所以这是一个概率问题,基本用法是Thread thread = new Thread(xxx);  thread.setPriority(1);   thread.start();

  那么yield方法是干什么的呢?yield的意思是放手,让步,投降,在多线程中是对一个正在运行的A线程用这个方法表示该线程放弃CPU的调度,重新回到准备就绪状态,然后让CPU去执行和A线程相同优先级的线程,而且有可能又会执行A线程;而且还有可能调用yield方法无效,emmmm......日了狗了!

  我表示最这个方法没有什么好感,感觉很难控制,这个方法是Thread的静态方法,直接用Thread.yield()直接用即可,我感觉我一辈子都不会用到这个....这个方法就不测试了,有兴趣的小伙伴自己查查别的资料吧!

6.总结

  不知道大家有没有觉得多线程这个东西不能随便用,用好了虽然说可以提高效率,但是用的不好很容易出现不可控制的问题,这让我有一种错觉就是引入了多线程之后,又要解决由多线程引起的更多更麻烦的问题,emmm。。。。

    

带着新人看java虚拟机07(多线程篇)的更多相关文章

  1. 带着新人看java虚拟机06(多线程篇)

    其实多线程还有很多的东西要说,我们慢慢来,可能会有一些东西没说到,那就没办法了,只能说尽量吧! 1.synchronized关键字 说到多线程肯定离不开这个关键字,为什么呢?因为多线程之间虽然有各自的 ...

  2. 带着新人看java虚拟机04(多线程篇)

    我记得最开始接触多进程,多线程这一块的时候我不是怎么理解,为什么要有多线程啊?多线程到底是个什么鬼啊?我一个程序好好的就可以运行为什么要用到多线程啊?反正我是十分费解,即使过了很长时间我还是不是很懂, ...

  3. 带着新人看java虚拟机03

    分享一篇博客:https://blog.csdn.net/yfqnihao/article/details/8289363,本篇有部分参考这篇博客!!! 还是继续说一下java虚拟机,为什么呢?因为我 ...

  4. 带着新人看java虚拟机02

    上一节是把大概的流程给过了一遍,但是还有很多地方没有说到,后续的慢慢会涉及到,敬请期待! 这次我们说说垃圾收集器,又名gc,顾名思义,就是收集垃圾的容器,那什么是垃圾呢?在我们这里指的就是堆中那些没人 ...

  5. 带着新人看java虚拟机01

    1.前言(基于JDK1.7) 最近想把一些java基础的东西整理一下,但是又不知道从哪里开始!想了好久,还是从最基本的jvm开始吧!这一节就简单过一遍基础知识,后面慢慢深入... 水平有限,我自己也是 ...

  6. 带着新人看java虚拟机05(多线程篇)

    上一篇我们主要是把一些基本概念给说了一下以及怎么简单的使用线程池,我们这一节就来看看线程池的实现: 1.线程池基本参数 以Executors.newFixedThreadPool()这种创建方式为例: ...

  7. java基础07 多线程

    在学习操作系统时,我们会学习进程和线程,那么进程和线程又是什么东西呢? 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程(thread) ...

  8. Java基础之多线程篇(线程创建与终止、互斥、通信、本地变量)

    线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public ...

  9. 深入理解java虚拟机系列初篇(一):为什么要学习JVM?

    前言 本来想着关于写JVM这个专栏,直接写知识点干货的,但是想着还是有必要开篇讲一下为什么要学习JVM,这样的话让一些学习者心里有点底的感觉比较好... 原因一:面试 不得不说,随着互联网门槛越来越高 ...

随机推荐

  1. winform设置文本框宽度 根据文字数量和字体返回宽度

    _LinkLabel.Width = TextRenderer.MeasureText(_LinkLabel.Text, _LinkLabel.Font).Width;

  2. php实现中文字符串截取各种问题

    用php截取中文字符串会出现各种问题,做一简单汇总,文中的问题暂时还未解决,有大神解决了问题欢迎指教 <?php header('Content-Type:text/html;charset=u ...

  3. php防注入

    引发 SQL 注入攻击的主要原因,是因为以下两点原因: 1. php 配置文件 php.ini 中的 magic_quotes_gpc选项没有打开,被置为 off 2. 开发者没有对数据类型进行检查和 ...

  4. 函数 buf&lowbar;chunk&lowbar;init

    http://www.tuicool.com/articles/3QbYfm http://www.360doc.com/content/13/1216/17/12904276_337644353.s ...

  5. 使用 vmstat 监测系统性能

    在linux/unix下,vmstat是常用的系统性能监测工具.常用用法如下 vmstat 1 10 表示以1秒为间隔,做相关参数的采样,一共10次.输出范例如下: procs ----------- ...

  6. js html标签select 中option 删除除了第一行外的其他行

    背景:共两个下拉框,第一个下拉框选择完之后,以第一个选定的值为条件返回第二个下拉框中的内容,用js中的createElement()创建,并利用appendChild()来添加进父标签.出现意外:每次 ...

  7. K 班前7次作业成绩汇总

    K 班前7次作业成绩汇总 得分榜 千帆竞发 详细 短学号 名 1 2 3 4 5 6 7 TOTAL 505 基智 4.55 1 -2 0 0 -10 4.37 -2.08 414 圳源 5.43 2 ...

  8. Struts2之ModelDriven和Preparable拦截器

    首先struts.xml文件配置如下 默认拦截器设置为paramsPrepareParamsStack <package name="default" namespace=& ...

  9. 【转】工作中使用Trepn Power Profiler的应用总结

    Trepn™ Profiler 工具的概述 Trepn™工具是高通开发的运行在使用高通骁龙芯片或者硬件开发设备等移动设备上 分析功耗和性能的一个应用. ## 特点 ## 1 2 3 Six fast- ...

  10. bzoj3802&colon; Vocabulary

    Description 给你三个字符串,这些字符串有些单词模糊不可认了,用"?"来代表. 现在你可以用任意英文小写字母来代表它们.要求是使得给定的三个字符串中 所有的"? ...