美文网首页
多线程的简单实现

多线程的简单实现

作者: coder_hang | 来源:发表于2018-10-31 18:12 被阅读0次

进程和线程的区别

每一个计算机应用至少有一个进程,并对应一个进程号。

image

线程是比进程更加细粒度,一个进程可以有多个线程。可以并发执行多个任务。

并行和并发的区别:

并行:多个CPU实例或者多个机器同时执行一段处理逻辑。真正的同时。

并发:通过CPU调度算法,让用户看上去同时执行一段处理逻辑,并不是真正的同时。

我们通过一个简单的实例了解一下多线程的威力。

image

比如。我们想看一下局域网内有哪些主机是可达的。我们可以使用cmd

命令行工具,使用ping命令来看是否接通,可以看到接通的会有TTL关键字,我们就可以根据这个关键字来判断是否可达。

先用单线程实现一下。

<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(43, 43, 43); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; color: rgb(169, 183, 198); font-family: "Source Code Pro"; font-size: 10pt;">package com.xuhang.springbootdemo.thread; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /**

  • @author:xuhang
  • @description:
  • @date:2018/10/31 15:07
    / public class PingHost { public static boolean checkHost(String ip) throws IOException {
    long begin = System.currentTimeMillis();
    Process pro = Runtime.getRuntime().exec("ping " + ip);
    //判断返回信息是否包含TTL关键字
    String line;
    BufferedReader buf = new BufferedReader(new InputStreamReader(pro.getInputStream())); while ((line = buf.readLine()) != null) {
    if (line.contains("TTL")) {
    long end1 = System.currentTimeMillis();
    long l1 = end1 - begin;
    System.out.println("主机" + ip + "可到达 耗费时间:"+l1+"ms" );
    return true;
    }
    } long end2 = System.currentTimeMillis();
    long l2 = end2 - begin;
    System.out.println("主机" + ip + "不可到达 耗费时间:"+l2+"ms");
    return false; } /
    *
  • @param args
  • @throws IOException
    */ public static void main(String[] args) throws IOException {
    PingHost ph = new PingHost();
    for (int i = 147; i < 300; i++) {
    ph.checkHost("10.40.96." + i);
    } } } </pre>

看一下运行结果。

image

可以看到是按照顺序进行测试的。而且耗费时间特别多,如果自己操作可以不用等到结束就中断,因为太费时间了。总之单线程的缺点是:所有的操作顺序,时间长,效率低。

那么我们就来看一下多线程是什么样的。实现多线程的方式有多种。今天我们来了解继承Thread类并重写run方法和实现Runnable接口并重写run方法两种方法实现。启动一个线程是调用线程的start方法。面试中经常问到。

<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(43, 43, 43); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; color: rgb(169, 183, 198); font-family: "Source Code Pro"; font-size: 10pt;">package com.xuhang.springbootdemo.thread; import java.io.IOException; /**

  • @author:xuhang
  • @description:通过继承Thread类实现多线程。
  • @date:2018/10/31 15:25
    */ public class MultiThreadByThread extends Thread {
    private String ip;
    public MultiThreadByThread(String ip) {
    super();
    this.ip = ip;
    }
    @Override
    public void run() {
    try {
    PingHost.checkHost(ip);
    } catch (IOException e) {
    e.printStackTrace();
    }
    } }</pre>

这种方法使用主函数测试时需要变一下;如下所示:

<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(43, 43, 43); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; color: rgb(169, 183, 198); font-family: "Source Code Pro"; font-size: 10pt;">//测试Thread方法
for (int i = 147; i < 300; i++) {
MultiThreadByThread btt =new MultiThreadByThread("10.40.96." + i);
btt.start(); }</pre>

如果自己操作,通过控制台就可以很直观的看到质的飞跃。很快就可以执行完。

image

可以看出:操作没有按照顺序,并且耗费时间大大缩减。这就是多个线程同时进行操作,不用等待前一个线程结束就可以直接执行。

另一种方式,实现Runnable接口:

<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(43, 43, 43); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; color: rgb(169, 183, 198); font-family: "Source Code Pro"; font-size: 10pt;">package com.xuhang.springbootdemo.thread; import java.io.IOException; /**

  • @author:xuhang
  • @description:通过实现Runnable接口并重写run方法实现多线程
  • @date:2018/10/31 15:53
    */ public class MultiThreadByRunnable implements Runnable {
    private String ip;
    public MultiThreadByRunnable(String ip) {
    super();
    this.ip = ip;
    } @Override
    public void run() {
    try {
    PingHost.checkHost(ip);
    } catch (IOException e) {
    e.printStackTrace();
    } } }</pre>

再测试一下Runnable接口的方式,主函数修改为:

<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(43, 43, 43); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; color: rgb(169, 183, 198); font-family: "Source Code Pro"; font-size: 10pt;">//测试runnable方式 for (int i =147; i < 300; i++) {
MultiThreadByRunnable mtb =new MultiThreadByRunnable("10.40.96." + i);
new Thread(mtb).start(); }</pre>

通过控制台看出。效果和继承Thread类似;

image

同样是无序的,在短时间内就完成了。

但是 Thread和Runnable的区别有哪些呢?

我们通过一个火车站卖火车票的实例来了解一下。

如果使用thread方式,火车票资源不共享,各卖各。

<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(43, 43, 43); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; color: rgb(169, 183, 198); font-family: "Source Code Pro"; font-size: 10pt;">package com.xuhang.springbootdemo.thread; /**

  • @author:xuhang
  • @description:
  • @date:2018/10/31 16:02
    */ public class ThreadTicket extends Thread {
    private int ticketnum=5;
    private String name;
    public ThreadTicket(String name) {
    super();
    this.name = name;
    };
    @Override
    public void run() {
    for (int i = 1; i <=5; i++) {
    System.out.println(name+"卖票"+ticketnum--);
    }
    } public static void main(String[] args) {
    //使用金庸武侠里的美女角色怀念一下金庸大侠
    ThreadTicket zhaomin = new ThreadTicket("赵敏");
    ThreadTicket xiaozhao = new ThreadTicket("小昭");
    //启动两个线程
    zhaomin.start();
    xiaozhao.start(); } }</pre>

控制台输出:

image

可以发现出现了各卖各的的情况。

接下来使用实现Runnable接口的方式:

<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(43, 43, 43); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; color: rgb(169, 183, 198); font-family: "Source Code Pro"; font-size: 10pt;">package com.xuhang.springbootdemo.thread; /**

  • @author:xuhang
  • @description:
  • @date:2018/10/31 16:09
    */ public class RunnableTicket implements Runnable {
    private int ticketnum=5;
    @Override
    public void run() {
    for (int i = 1; i <=5; i++) {
    if(ticketnum>0){
    //Thread.currentThread().getName()获取当前进程名称
    System.out.println(Thread.currentThread().getName()+"卖票"+ticketnum--);
    }
    } } public static void main(String[] args) {
    RunnableTicket rt = new RunnableTicket();
    new Thread(rt,"售票员赵敏").start();
    new Thread(rt,"售票员小昭").start(); } }</pre>

使用runnable方式,可以实现资源共享,但是出现重复卖的情况

image

如何避免这些情况呢?使用关键字synchronized同步锁解决

<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(43, 43, 43); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; color: rgb(169, 183, 198); font-family: "Source Code Pro"; font-size: 10pt;">package com.xuhang.springbootdemo.thread; /**

  • @author:xuhang
  • @description:
  • @date:2018/10/31 16:09
    */ public class RunnableTicket implements Runnable {
    private int ticketnum=100;
    @Override
    public void run() {
    for (int i = 1; i <=ticketnum; i++) {
    synchronized (this) {
    if(ticketnum>0){ // try { // Thread.sleep(1000); // } catch (InterruptedException e) { // e.printStackTrace(); // }
    //Thread.currentThread().getName()获取当前进程名称
    System.out.println(Thread.currentThread().getName()+"卖票"+ticketnum--);
    }
    } } } public static void main(String[] args) {
    RunnableTicket rt = new RunnableTicket();
    new Thread(rt,"售票员赵敏").start();
    new Thread(rt,"售票员小昭").start(); } } </pre>

注释掉的是为力让看着比较直观,让线程睡了一秒。不然唰一下子就把票卖完啦, 此时两个线程是被cpu随机分配的。可能性能好的话一直都会是一个线程在执行。增加票数,去掉睡眠一秒钟。可以观察到。所有的票不会被多卖也不会被重复卖。

image

前面说到了同步锁的概念。那么什么是锁呢,锁就是当一个线程进入时会将当前状态锁定,直到一个线程结束后才会释放,让第二个线程使用。下面看两个不同的代码了解一下锁以及sleep和wait的区别。

<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(43, 43, 43); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; color: rgb(169, 183, 198); font-family: "Source Code Pro"; font-size: 10pt;">package com.xuhang.springbootdemo.thread; /**

  • @author:xuhang
  • @description:
  • @date:2018/10/31 16:46
    */ public class LockTest {
    public static void main(String[] args) {
    final Object lock = new Object();
    //创建两个线程
    Runnable t1= new Runnable() {
    @Override
    public void run() {
    synchronized (lock) {
    System.out.println(1);
    try {
    Thread.sleep(3000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println(2);
    }
    } };
    Runnable t2= new Runnable() {
    @Override
    public void run() {
    synchronized (lock) {
    System.out.println(3);
    try {
    Thread.sleep(3000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println(4);
    }
    } };
    new Thread(t1).start();
    new Thread(t2).start(); } } </pre>

打印结果:

image

如果使用wait,则需要使用notify或者notifyAll才能唤醒。而且唤醒后的线程会去竞争cpu执行。

<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(43, 43, 43); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; color: rgb(169, 183, 198); font-family: "Source Code Pro"; font-size: 10pt;">package com.xuhang.springbootdemo.thread; /**

  • @author:xuhang
  • @description:
  • @date:2018/10/31 16:46
    */ public class LockTest {
    public static void main(String[] args) {
    final Object lock = new Object();
    //创建两个线程
    Runnable t1= new Runnable() {
    @Override
    public void run() {
    synchronized (lock) {
    System.out.println(1);
    try {
    lock.wait();//一旦线程通过wait进入等待,不会自己醒
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println(2);
    }
    } };
    Runnable t2= new Runnable() {
    @Override
    public void run() {
    synchronized (lock) {
    System.out.println(3);
    try {
    Thread.sleep(3000);
    lock.notify();//唤醒lock
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println(4);
    }
    } };
    new Thread(t1).start();
    new Thread(t2).start(); } } </pre>

打印结果:

image

sleep和wait的区别

1,sleep是thread类的方法,wait是object的方法。

2,wait不会自己醒,sleep睡完能够自己唤醒,sleep必须指定睡眠时间。

3,线程执行到sleep不会释放锁,但是wait会释放锁

线程死锁

新建一个father类

<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(43, 43, 43); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; color: rgb(169, 183, 198); font-family: "Source Code Pro"; font-size: 10pt;">package com.xuhang.springbootdemo.thread; /**

  • @author:xuhang
  • @description:
  • @date:2018/10/31 16:53
    */ public class Father { public void say(){
    System.out.println("爸爸说:你做作业我就给你糖吃!");
    }
    public void get(){
    System.out.println("爸爸拿到了孩子的作业!");
    } } </pre>

再建一个child类

<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(43, 43, 43); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; color: rgb(169, 183, 198); font-family: "Source Code Pro"; font-size: 10pt;">package com.xuhang.springbootdemo.thread; /**

  • @author:xuhang
  • @description:
  • @date:2018/10/31 16:53
    */ public class Child {
    public void say(){
    System.out.println("孩子说:你给我糖吃我就做作业!");
    }
    public void get(){
    System.out.println("孩子拿到了糖");
    } } </pre>

写一个死锁。

<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(43, 43, 43); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; color: rgb(169, 183, 198); font-family: "Source Code Pro"; font-size: 10pt;">package com.xuhang.springbootdemo.thread; /**

  • @author:xuhang
  • @description:
  • @date:2018/10/31 16:51
    */ public class DeadLockTest implements Runnable { private static Father father = new Father();
    private static Child child = new Child();
    private boolean flag = false; @Override
    public void run() {
    if(flag){
    //父亲
    synchronized (father) {
    father.say();
    synchronized (child) {
    father.get();
    }
    } }else
    {
    //孩子
    synchronized (child) {
    child.say();
    synchronized (father) {
    child.get();
    }
    } } } public static void main(String[] args) {
    DeadLockTest d1= new DeadLockTest();
    DeadLockTest d2= new DeadLockTest();
    d1.flag=true;
    d2.flag=false;
    new Thread(d1).start();
    new Thread(d2).start();
    } } </pre>

运行

image

线程不会停止。相互阻塞。

我们可以使用jdk自带的Jconsole图形化界面来检测死锁。

image image
    目前,我自己学习了解到的多线程以及线程锁的知识只到这里。更加深入的需要我们继续深入学习。希望有条件的可以来自己操作试一下。面试中差不多的公司都会问到多线程的问题。

相关文章

  • IOS多线程二 NSThread简约而不简单

    IOS多线程二NSThread简约而不简单 今天就来着手教大家在IOS中简单的实现多线程。IOS实现多线程的方式有...

  • 多进程和多线程的应用场景

    其实,使用多线程编程还是使用多进程编程,有一个简单的原则,如果能使用多线程实现的,就用多线程,不能使用多线程实现的...

  • 面向加薪重学Java多线程(三种创建方式,线程安全,生产者消费者

    实现多线程 简单了解多线程【理解】 是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件...

  • 多线程的简单实现

    进程和线程的区别 每一个计算机应用至少有一个进程,并对应一个进程号。 线程是比进程更加细粒度,一个进程可以有多个线...

  • iOS多线程的简单用法

    实现多线程的方式一般有三种:NSThread、GCD、NSOperation 1、NSThread:适用简单,简单...

  • python多线程的使用

    python多线程的使用 后续还会更新其他方法实现的多线程。 Queue实现多线程 通过队列实现多线程 启动一定数...

  • iOS 多线程

    参考链接 iOS多线程iOS 多线程:『GCD』详尽总结iOS简单优雅的实现复杂情况下的串行需求(各种锁、GCD ...

  • OC语法_多线程

    1. 多线程实现原理; 2. 多线程实现的方案; 3. 线程同步技术; 1. 多线程实现原理; - 进程:...

  • Linux并发服务器模型五 -- epoll

    前言多进程和多线程模型在实现中相对简单, 但其开销和CPU高度比较大, 一般不用多线程和多进程来实现服务器多路模型...

  • Java多线程1

    创建多线程的3种方式 1、继承Thread类实现多线程 2、实现Runnable接口方式实现多线程 定时器 线程的...

网友评论

      本文标题:多线程的简单实现

      本文链接:https://www.haomeiwen.com/subject/jxqbwftx.html