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

Java多线程基础

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

Java多线程基础

一、线程概述 1.1 什么是线程

线程(thread)是一个程序内部的一条执行路径。main方法的执行是一条单独的执行路径。程序中如果只有一条执行路径,那么这个程序就是单线程的程序。 1.2 什么是多线程

多线程是指从软硬件上实现多条执行流程的技术。 二、多线程的创建 2.1 继承Thread类

Java是通过java.lang.Thread类来代表线程的。按照面向对象的思想,Thread类提供了实现多线程的方式。过程:

    定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法创建MyThread类的对象调用线程对象的start()方法启动线程(启动后还是执行run方法)
优点:编码简单缺点:线程类已经继承Thread类,无法继承其他类,不利于扩展为什么不调用run方法,而是调用start?

直接调用run方法会当成普通方法执行,此时相当于还是单线程执行。只有调用start方法才是启动一个新的线程执行。 主线程任务应该放在子线程之后。

public class ThreadDemo1 {
    public static void main(String[] args) {
        //3.new一个新线程对象
        Thread t = new MyThread();
        //4.调用start方法启动线程(执行run)
        t.start();
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程输出:" + i);
        }
    }
}
//1.定义一个线程类继承Thread类
class MyThread extends Thread {
    //2.重写run方法,定义线程功能
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程输出:" + i);
        }
    }
}
2.2 实现Runnable接口

过程:

    定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法。创建MyRunnable任务对象。把MyRunnable任务对象交给Thread处理。调用线程对象的start()方法启动线程。
优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。缺点:编程多一层对象包装,如果线程有执行结果是不可以直接返回的。
public class ThreadDemo2 {
    public static void main(String[] args) {
        //3.创建一个任务对象
        MyRunnable task = new MyRunnable();
        //4.将任务对象交给线程对象Thread处理
        Thread t = new Thread(task);
        //5.启动线程
        t.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程输出:" + i);
        }
    }
}
//1.定义一个线程类,实现Runnable接口
class MyRunnable implements Runnable {
    //2.重写run方法,定义线程任务
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("子线程输出:" + i);
        }
    }
}
2.3 实现Callable接口

前2种线程创建方式都存在一个问题:

他们重写的run方法均不能直接返回结果不适合需要返回线程执行结果的业务场景 过程

    得到任务对象
      定义类实现Callable接口,重写call方法,封装要做的事情用FutureTask把Callable对象封装成线程任务对象
    把线程任务对象交给Thread处理调用Thread的start方法启动线程,执行任务线程执行完毕后,通过FutureTask的get方法去获取任务执行的结果
FutureTask

public FutureTask<>(Callable call):把Callable对象封装成FutureTask对象public V get():获取线程执行call方法返回结果 优点:

线程任务类只是实现接口,可以继续继承类的实现接口,扩展性强可以在线程执行完毕后去获取线程执行的结果 缺点:编码稍微复杂

public class threadDemo3 {
    public static void main(String[] args) {
        //3.创建Callable任务对象
        Callable call1 = new MyCallable(100);
        //4.把Callable任务对象,交给FutureTask对象(实现了Runnable接口,可以调用get方法得到线程执行结果)
        FutureTask f1 = new FutureTask(call1);
        //5.交给线程执行
        Thread t1 = new Thread(f1);
        //6.启动线程
        t1.start();
        //3.创建Callable任务对象
        Callable call2 = new MyCallable(200);
        //4.把Callable任务对象,交给FutureTask对象(实现了Runnable接口,可以调用get方法得到线程执行结果)
        FutureTask f2 = new FutureTask(call2);
        //5.交给线程执行
        Thread t2 = new Thread(f2);
        //6.启动线程
        t2.start();
        try {
            String rs1 = f1.get();
            System.out.println("第一个线程执行结果: " + rs1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        try {
            String rs2 = f2.get();
            System.out.println("第二个线程执行结果: " + rs2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
//1.定义一个任务类,实现Callable接口(声明线程任务执行完毕后返回结果的数据类型)
class MyCallable implements Callable{
    //2.重写call方法(任务方法)
    private int n;
    public MyCallable(int n) {
        this.n = n;
    }
    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum += i;
        } 
        return "子线程的执行结果是: " + sum;
    }
}

三、Thread类的常用方法 3.1 设置名称

获取线程名称:getName()设置线程名称:setName()获取当前线程对象:currentThread() 3.2 线程休眠方法

public static void sleep(long time):让当前线程休眠指定的时间后再继续执行,单位为毫秒。

public class ThreadDemo2 {
    public static void main(String[] args) throws InterruptedException {
        for (int i = 1; i <= 5; i++) {
            System.out.println("输出: " + i);
            if(i == 3){
                //让当前线程进入休眠
                Thread.sleep(3000);
            }
        }
    }
}
四、线程安全 4.1 线程安全问题

多个线程同时操作同一个共享资源的时候可能会出现业务安全问题,成为线程安全问题线程安全问题出现的原因

存在多线程并发同时访问共享资源存在修改共享资源 4.2 线程安全问题模拟

ThreadDemo.java

public class ThreadDemo {
    public static void main(String[] args) {
        //1.定义线程类,创建一个共享的账户对象
        Account acc = new Account("share",100000);
        //2.创建两个线程对象,代表小明和小红同时进入
        new DrawThread(acc,"小明").start();
        new DrawThread(acc,"小红").start();
    }
}

Account.java

public class Account {
    private String cardID;
    private double money;
    public Account() {
    }
    public Account(String cardID, double money) {
        this.cardID = cardID;
        this.money = money;
    }
    public String getCardID() {
        return cardID;
    }
    public void setCardID(String cardID) {
        this.cardID = cardID;
    }
    public double getMoney() {
        return money;
    }
    public void setMoney(double money) {
        this.money = money;
    }
    public void drawMoney(double money) {
        String name  = Thread.currentThread().getName();
        if (this.money >= money){
            System.out.println(name + "来取钱成功,吐出: " + money);
            this.money -= money;
            System.out.println(name + "取钱后剩余:" + this.money);
        }else{
            System.out.println(name + "取钱失败");
        }
    }
}

DrawThread.java

public class DrawThread extends Thread{
    private Account acc;
    public DrawThread(Account acc,String name) {
        super(name);
        this.acc = acc;
    }
    @Override
    public void run() {
        //小明,小红取钱
        acc.drawMoney(100000);
    }
}

执行结果

小明来取钱成功,吐出: 100000.0
小红来取钱成功,吐出: 100000.0
小明取钱后剩余:0.0
小红取钱后剩余:-100000.0
五、线程同步 5.1 线程同步概述

为了解决线程安全问题线程同步的核心思想:加锁,把共享资源进行上锁,每次只能一个线程进入访问,访问完毕以后解锁,然后其他线程才能进来。 5.2 方式一:同步代码块

作用:把出现线程安全问题的核心代码给上锁原理:每次只能一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行锁对象的规范要求

规范:建议使用共享资源作为锁对象对于实例方法建议使用this作为锁对象对于静态方法建议使用字节码(类名.class)对象作为锁对象

synchronized(同步锁对象) {
    操作共享资源的代码
}
5.2 方式二:同步方法

作用:把出现线程安全问题的核心方法给上锁原理:每次只能一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行底层原理

同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码实例方法:默认用this作为所的对象静态方法:默认用类名.class作为锁的对象

修饰符 synchronized 返回值类型 方法名称(形参列表) {
    操作共享资源的代码
}
5.3 方式三:Lock锁

为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock,更加灵活、方便。Lock实现可以获得更广泛的锁定操作Lock锁接口不能直接实例化,这里采用它的实现类ReentrantLock来构建锁对象获得锁对象:public ReentrantLock()获得锁:void lock()释放锁:void unlock()

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/763055.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

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

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