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

1.线程中断  

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

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

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

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

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

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

  ·   举例下面两种用法:

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

    

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

  注意:必须等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(); }
}

    运行结果如下:

    

  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(); }
}

  会抛出一个异常这也是用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(); }
}

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. mysql-入门

    创建数据库 Create database 数据库名字 [库选项]; 创建数据库 create database mydatas charset utf8; 查看数据库 show databases; ...

  2. Java多线程:线程间通信之volatile与sychronized

    由前文Java内存模型我们熟悉了Java的内存工作模式和线程间的交互规范,本篇从应用层面讲解Java线程间通信. Java为线程间通信提供了三个相关的关键字volatile, synchronized ...

  3. 高性能网络通信框架 HP-Socket

      HP-Socket 详细介绍 HP-Socket 是一套通用的高性能 TCP/UDP/HTTP 通信框架,包含服务端组件.客户端组件和Agent组件,广泛适用于各种不同应用场景的 TCP/UDP/ ...

  4. ResultSet只返回一行数据的原因

    写之前,先告戒一下自己......写代码一定要细心,自己写的即使是非常简单的地方也要细心,不能自我感觉太良好,那往往可能会有些bug在等着你...... 注意事项: 1.当你为了查看数据库中是否存在某 ...

  5. 解密TTY

    本文内容来自The TTY demystified ,讲述了*NIX系统中TTY的历史与工作原理,看完后解决了我很多疑惑,于是做此翻译,与大家分享. 译者:李秋豪 江家伟 审校: V1.0 Sun M ...

  6. 命令行备忘录 cli-memo

    前言 有时候想用一个简洁点儿的备忘录,发现没有简洁好用的,于是就想着开发一个,秉着简洁 的思想,所以连界面都没有,只能通过命令行来操作(尽可能的将命令简化).设计的时候 借鉴了git分支的思想,每个备 ...

  7. Python_heapq

    import heapq #导入heapq堆模块 import random data = random.sample(range(1000),10) print(data) heapq.heapif ...

  8. mysql导入数据中文乱码_ubuntu

    1.在ubuntu中mysql的部分编码格式不是utf-8,故在导文件的时候会出现中文乱码,Windows中编码格式为gbk,因此要修改mysql的编码方式为utf-8. 2.查看MySQL编码格式: ...

  9. Activity的状态保存

    这两个图其实说的是一个意思,具体onSaveInstanceState()这个函数什么时候会调用,在网络上搜了一下 这个第一种情况,我可以解释一下,说的是这个方法只在onResume和onPause之 ...

  10. 虚拟机搭建CentOS主机win10通过xshell连接

    目标:主机是win10系统,虚拟机搭建CentOS,在主机上通过XShell连接操作. 第一步 主机上安装虚拟机 第二步 下载CentOS 下载地址http://101.110.118.69/isor ...