Java多线程(一)

简介: 线程(thread)是一个程序内部的一条执行流程。如果只有一条执行流程,那么这个程序就是单线程的程序,例如只有main方法的一个程序。

线程(thread)是一个程序内部的一条执行流程。如果只有一条执行流程,那么这个程序就是单线程的程序,例如只有main方法的一个程序。

多线程是从软硬件上实现多条执行流程的技术,在各种通信、购物等系统上都有多线程计算的应用。

  1. 多线程的创建

通常来说我们创建多线程有三大方法:继承Thread类、实现Runnable接口、实现Callable接口

  1. 继承Thread类

我们可以写一个子类MyThread继承线程类Thread,重写run()方法。然后创建MyThread类的对象,调用线程对象的start()方法启动线程。

这种方法实现简单,但是线程类已经继承Thread,无法继承其他类,不利于扩展。

class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 500; i++) {
            System.out.println("子线程 "+i);
        }
    }
}
        Thread t=new MyThread();//子线程对象
        t.start();//启动线程
        //主线程的内容写后面
  1. 实现Runnable接口

我们写一个线程任务类MyRunnable实现Runnable接口,重写run()方法。

class myRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println("子线程 "+i);
        }
    }
}

然后创建MyRunnable任务对象,把MyRunnable任务对象交给Thread处理。调用线程对象的start()方法启动线程。这里出现了Thread的构造器,Thread(Runnable target)、

public Thread(Runnable target ,String name )指定线程名称

Runnable work= new myRunnable();//创建MyRunnable任务对象
        Thread t=new Thread(work);
        t.start();
//主线程的内容写后面

线程任务类只是实现接口,可以继续继承类和实现接口,但是多一层对象包装,线程执行结果不可以直接返回。

//简化: 
       new Thread(() -> {
            for (int i = 0; i < 200; i++) {
                System.out.println("Lambda 子线程 "+i);
            }
        }).start();
  1. 实现Callable接口  ——可以得到线程执行的结果

前2种线程创建方式都存在一个问题,重写的run方法均不能直接返回结果。

步骤1、得到任务对象

定义类实现Callable接口,重写call方法,封装要做的事情。

class myCallable implements Callable<String>{
    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++) {
            System.out.println("任务进行中: "+i+"/"+n);
            sum+=i;
        }
        System.out.println("任务完成: "+n);
        return "子线程计算1+...+n的值:"+sum;
    }
}

用FutureTask把Callable对象封装成线程任务对象。(FutureTask实现了Runnable的对象,可以给Thread,可以使用get方法获得返回值

Callable<String> call=new myCallable(190);
        FutureTask<String> futureTask=new FutureTask<>(call);
//2
        Thread t=new Thread(futureTask);
        t.start();
//3
        try {//如果线程任务没有完成会在这里等待
            System.out.println(futureTask.get());
        } catch (Exception e) {
            e.printStackTrace();
        }

步骤2、把线程任务对象交给Thread处理,调用Thread的start方法启动线程

步骤3、FutureTask的get方法可以获取任务执行的结果。

  1. Thread常用API

获取线程名称getName()、设置名称setName()、获取当前线程对象currentThread()。

Thread类的线程休眠方法 public static void sleep(long time):Thread.sleep(5);//毫秒

  1. 线程安全

多个线程同时操作同一个共享资源的时候可能会出现安全问题。

多线程同时访问并修改共享资源:例如,有一个公共的账户有1万元。如果A和B同学同时来取1万元,由于AB是两个线程操作,可能存在都取出情况。

public static void main(String[] args) {
        account acc=new account("happy",10000);
        new getMoneyThread(acc,"张三").start();
        new getMoneyThread(acc,"李四").start();
    }
//账户类
class account{
    private String ID;
    private double money;
    public account(){}
    public account(String id,double money){
        this.ID=id;
        this.money=money;
    }
    public void getMoney(double money){
        String name=Thread.currentThread().getName();
        //取钱
        if(this.money>=money){
            System.out.println(name+"取出"+money);
            this.money-=money;
            System.out.println(ID+"剩余"+this.money);
        }
        else
            System.out.println(name+"取钱"+money+" 失败"+"剩余"+this.money);
    }
    public String getID() {
        return ID;
    }
    public double getMoney() {
        return money;
    }
}
//取钱的线程类
class getMoneyThread extends Thread{
    private account a;
    public getMoneyThread(account a,String name){
        super(name);
        this.a=a;
    }
    @Override
    public void run() {
        a.getMoney(10000);
    }
}

因为在取钱的时候,首先判断是否有足够的余额,两个线程同时进来,就可能导致判断都通过,继而后面的操作都通过,使得两个人都取到了钱。

  1. 线程同步

上面的例子中,多个线程同时执行,发现账户都是可以取钱的,而实际上不可能发生这样的事情。这就是为了解决线程安全问题,采用了线程同步,让多个线程实现先后依次访问共享资源。

线程同步的核心思想:加锁,把共享资源进行上锁,每次只能一个线程进入访问完毕以后解锁,然后其他线程才能进来。(操作系统中的进程互斥,对某个系统资源,一个进程正在使用它,另外一个想用它的进程就必须等待,而不能同时使用 。

方法一:同步代码块,对出现问题的核心代码使用synchronized进行加锁,每次只能一个线程占锁进入访问。

方法二:同步方法,把出现线程安全问题的核心方法给上锁。

方法三:Lock锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。Lock是接口不能直接实例化,采用它的实现类ReentrantLock来构建Lock锁对象。lock()、unlock()分别获得锁和释放锁。

相关文章
|
3月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
219 1
|
3月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
239 1
|
4月前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
Java 数据库 Spring
193 0
|
4月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
319 16
|
5月前
|
缓存 并行计算 安全
关于Java多线程详解
本文深入讲解Java多线程编程,涵盖基础概念、线程创建与管理、同步机制、并发工具类、线程池、线程安全集合、实战案例及常见问题解决方案,助你掌握高性能并发编程技巧,应对多线程开发中的挑战。
|
5月前
|
数据采集 存储 前端开发
Java爬虫性能优化:多线程抓取JSP动态数据实践
Java爬虫性能优化:多线程抓取JSP动态数据实践
|
6月前
|
Java API 调度
从阻塞到畅通:Java虚拟线程开启并发新纪元
从阻塞到畅通:Java虚拟线程开启并发新纪元
385 83
|
6月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
288 0
|
6月前
|
存储 Java 调度
Java虚拟线程:轻量级并发的革命性突破
Java虚拟线程:轻量级并发的革命性突破
377 83