栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java多线程学习笔记

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java多线程学习笔记

多线程,就是说一个应用程序有多条执行路径,每当我们打开一个应用程序的时候,就相当于打开了一个进程,而进程中执行的操作,就是线程。

以优酷为例,打开优酷就相当于打开一个进程,下载视频的操作就是线程,多线程就是同时下载多个视频。

在Java中有两种方式实现多线程分别是继承Thread类和实现Runnable接口。

一、继承Thread类

Thread类中有一个run方法,但是这个run方法需要我们重写,故我们需要自定义一个类来继承Thread类,然后在自定义的类中重写这个run方法,重写完了就可以创建一个对象了,用该对象去调用这个方法,但是由于这是多线程,如果是直接调用run方法,无法实现多线程,然后就有了一个新的的方法start方法供我们使用,此方法会自动调用run方法。

程序代码如下:

public class MyThread extends Thread {

    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + ":" + i);
        }
    }

    public static void main(String[] args) {

        //创建多线程对象
        MyThread myThread1 = new MyThread();
        MyThread myThread2 = new MyThread();
        MyThread myThread3 = new MyThread();

        //设置每个对象的名字
        myThread1.setName("刘");
        myThread2.setName("亦");
        myThread3.setName("菲");

        //调用start()方法,其内部调用了run()方法,实现了多线程
        //在这里直接调用run()方法,不能实现多线程
        myThread1.start();
        myThread2.start();
        myThread3.start();
    }
}

运行结果如下:

由结果可以看出,在跑这个循环的时候,并不是myThread1 跑完再跑myThread2,而是抢着跑的,这就是多线程的体现,也就是抢

二、实现Runnable接口

为了解决单继承的局限性,因为如果某个类已经有父亲了,那么它就不能再继承Thread来实现多线程了,所以就有了实现接口的多线程,我们大部分使用的方法也是用接口来实现多线程。接口方式实现多线程我们需要自定义一个类去实现Runnable接口,然后在自定义的类中重写run方法,再然后去创建一个自定义类的对象,最后创建一个Thread类的对象,并且把刚刚创建的自定义对象作为参数传递。

注意:其实Thread源码中,Thread类也实现了Runnable接口

程序代码:

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            //因为Runnable中没有getName方法,故用Thread中方法获取当前线程对象来实现
            System.out.println(Thread.currentThread().getName() + ":" + x);
        }
    }


    public static void main(String[] args) {

        //在这里只需创造一个接口子类对象,后面直接用mr就行了
        MyRunnable mr = new MyRunnable();

//		Thread t1 = new Thread(mr);
//		Thread t2 = new Thread(mr);
//		t1.setName("胡");
//		t2.setName("歌");

        //上面这种方法不是很常用,可以用下面这个
        Thread t1 = new Thread(mr, "胡");
        Thread t2 = new Thread(mr, "歌");
        t1.start();
        t2.start();

    }
}

运行结果如下:

三、继承Thread类和实现Runnable接口的区别
  • 继承Thread类不适合资源的共享,而实现Runnable接口很容易实现资源的共享
  • 实现Runnable接口适合多个相同的程序代码的线程去处理同一个资源
  • 避免了Java中的单继承限制
  • 增强了程序的健壮性,代码可以被多个线程共享
四、线程中断interrupt()

程序代码如下:

public class MyThread extends Thread {

    public void run() {
        System.out.println("线程开始");

        try {
            // 在这里是线程休息2秒,如果线程在2秒内被中断,则执行catch
            Thread.sleep(2000);
        } catch (InterruptedException e) {
//           e.printStackTrace();
            System.out.println("线程中断");
        }
        System.out.println("线程结束");
    }

    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();
        try {
            Thread.sleep(1000);
            mt.interrupt();// 这里的意思是线程执行如果超过1秒就被中断
        } catch (InterruptedException e) {
            // e.printStackTrace();
            System.out.println("中断线程");
        }
    }
}

运行结果:

五、线程同步synchronized()

多线程中,线程同步问题,为了讲解此问题,举一个火车卖票的例子,假如有3个窗口卖100张票,然后消费者去买票,当然了是三个窗口同时卖票,模拟一下此过程:

public class SynThread implements Runnable{
    private int t = 100;

    @Override
    public void run() {
        // 为了演示多线程的同步问题,在多线程同步中,
        // 可能出现1号窗口还正在卖,而还没卖出去的时候,二号窗口刚好开始卖,导致一张票卖了两次
        // 所以在这里我加上在卖票时候的一个延迟
        while (t > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 这里调用的是当前线程的名字
            System.out.println(Thread.currentThread().getName() + ":" + t--);
        }
    }

    public static void main(String[] args) {

        // 创建一个MyRunnable类的对象,由于此对象没有继承Thread类,所以下面还需创建Thread对象
        SynThread st = new SynThread();

        // 用此对象去创建Thread对象,并且给每个对象赋值,也就是起个名子
        Thread t1 = new Thread(st, "刘");
        Thread t2 = new Thread(st, "亦");
        Thread t3 = new Thread(st, "菲");

        // 这里是启动线程的意思
        t1.start();
        t2.start();
        t3.start();
    }
}

运行结果:

从上面的结果很容易看出,第100张票卖了3次,这肯定是不符合实际的,所以我们需要解决此问题,就是在某个窗口卖该张票的时候,其他窗口不能卖,也就是对该票进行加锁,即用synchronized()方法,其中有两种方式实现同步,分别是同步代码块和同步方法

(1)同步对象
public class SynThread implements Runnable {
    private int t = 100;

    // 方法一同步代码块
    public void run() {
        while (t > 0) {
            // 同步对象的锁是this
            synchronized (this) {
                if (t > 0) {// 在这里可能出现在t--之前进来消费者,故加个判断
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // 这里调用的是当前线程的名字
                    System.out.println(Thread.currentThread().getName() + ":" + t--);
                }
            }
        }
    }


    public static void main(String[] args) {

        // 创建一个MyRunnable类的对象,由于此对象没有继承Thread类,所以下面还需创建Thread对象
        SynThread st = new SynThread();

        // 用此对象去创建Thread对象,并且给每个对象赋值,也就是起个名子
        Thread t1 = new Thread(st, "刘");
        Thread t2 = new Thread(st, "亦");
        Thread t3 = new Thread(st, "菲");

        // 这里是启动线程的意思
        t1.start();
        t2.start();
        t3.start();
    }
}
(2)同步方法
public class SynThread implements Runnable {
    private int t = 100;

    // 方法二同步方法
    public void run() {
        while (t > 0) {
            sellt();
        }
    }

    // 此方法为同步方法
    private synchronized void sellt() {
        if (t > 0) {// 在这里可能出现在t--之前进来消费者,故加个判断
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 这里调用的是当前线程的名字
            System.out.println(Thread.currentThread().getName() + ":" + t--);
        }
    }


    public static void main(String[] args) {

        // 创建一个MyRunnable类的对象,由于此对象没有继承Thread类,所以下面还需创建Thread对象
        SynThread st = new SynThread();

        // 用此对象去创建Thread对象,并且给每个对象赋值,也就是起个名子
        Thread t1 = new Thread(st, "刘");
        Thread t2 = new Thread(st, "亦");
        Thread t3 = new Thread(st, "菲");

        // 这里是启动线程的意思
        t1.start();
        t2.start();
        t3.start();
    }
}

两种方式运行结果:

六、实现Callable接口实现多线程 1、Callable接口介绍

Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。

2.实现Callable接口,重写call()方法
 public class MyCallable implements Callable {

    //V call()  由于该方法的返回值是个泛型V,所以具体的返回类型可以自己定义,下面返回值设置为了String
    @Override
    public String call() throws Exception {
        return "Callable接口实现多线程";
    }
}
3.使用工厂类Executors的静态方法newFixedThreadPool()方法创建线程池,返回ExecutorService接口的实现类对象,然后调用submit()方法提交线程任务
public static void main(String[] args) throws Exception {

        //参数表示此线程池里有多少可用的线程数量
        ExecutorService es = Executors.newFixedThreadPool(3);
        //提交线程任务,返回一个Future接口,参数为Callable实现类对象
        Future future = es.submit(new MyCallable());
        //获取返回值
        String s = future.get();
        System.out.println(s);
    }

运行结果:

实现Callable接口的完整程序代码:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class MyCallable implements Callable {

    //V call()  由于该方法的返回值是个泛型V,所以具体的返回类型可以自己定义,下面返回值设置为了String
    @Override
    public String call() throws Exception {
        return "Callable接口实现多线程";
    }

    public static void main(String[] args) throws Exception {

        //参数表示此线程池里有多少可用的线程数量
        ExecutorService es = Executors.newFixedThreadPool(3);
        //提交线程任务,返回一个Future接口,参数为Callable实现类对象
        Future future = es.submit(new MyCallable());
        //获取返回值
        String s = future.get();
        System.out.println(s);
    }
}
七、进程与线程
  1. 进程是指运行中的应用程序,每一个进程都有自己独立的内存空间。比如:打开优酷,就是打开一个进程
  2. 线程是指进程中的一个执行流程,有时也称为执行情景。比如:打开优酷后下载一个视频,就是一个线程
  3. 一个进程可以由多个线程组成,即在一个进程中可以同时运行多个不同的线程,它们分别执行不同的任务。比如:在优酷中同时下载多个视频,就是多线程
  4. 当进程内的多个线程同时运行,这种运行方式称为并发运行。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/891657.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号