首页 > 编程语言 >Java多线程基础用法

Java多线程基础用法

时间:2024-03-11 16:44:05浏览次数:13  
标签:Java Thread int void 用法 线程 new 多线程 public

线程创建

线程创建的三种方式:

Thread(继承Thread类)

  1. 自定义线程类继承Thread类
  2. 重写run()方法。编写线程执行体
  3. 创建线程对象,调用start()方法启动线程
package com.lily.demo01;

public class TestThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println("子线程在看代码..."+Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) {

        TestThread testThread = new TestThread();
        testThread.start(); //主线程和子线程交替执行

        for (int i = 0; i < 200; i++) {
            System.out.println("主线程在看代码..."+Thread.currentThread().getName());
        }
    }
}

当调用子线程对象.run()方法其实是主线程和子线程交替执行。线程不一定立刻执行,二是由CPU根据调度算法选择调度。

Runnable(实现Runnable接口)

  1. 自定义MyRunnable实现Runnable接口
  2. 重写run方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程
package com.lily.demo01;

public class TestThread3{

    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    System.out.println("子线程正在写代码"+Thread.currentThread().getName());
                }
            }
        });
        thread.start();

        for (int i = 0; i < 20; i++) {
            System.out.println("主线程正在写代码"+Thread.currentThread().getName());
        }
    }
}

Callable(实现Callable接口)

  1. 实现Callable接口,需要返回值类型
  2. 重写call方法,需要抛出异常
  3. 创建目标对象
  4. 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
  5. 提交执行:Future result = ser.submit()
  6. 获取结果:boolean r = result.get()
  7. 关闭服务:ser.shutdownNow()
package com.lily.demo01;

import java.util.concurrent.*;

public class TestThread4 implements Callable<String> {

    @Override
    public String call() throws Exception {
        for (int i = 0; i < 20; i++) {
            System.out.println("子线程正在写代码"+Thread.currentThread().getName());
        }
        return "子线程执行完毕";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建一个线程
        TestThread4 testThread4 = new TestThread4();
        //创建一个线程池,里有3个线程
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        //提交执行
        Future<String> future = executorService.submit(testThread4);
        //获取结果
        String result = future.get();
        //关闭服务
        executorService.shutdown();
    }
}

关于线程常见的方法

方法 说明
setPriority(int newPriority) 更改线程的优先级
static void sleep(long millis) 在指定的毫秒级内让当前正在执行的线程休眠
void join() 等待线程终止
static void yield() 暂停当前正在执行的线程对象,并执行其它线程
void interrupt() 中断线程,别用这个方法
boolean isAlive() 测试线程是否处于活动状态

关于停止线程

  • 不推荐使用JDK提供的stop()、destroy()方法【弃用】
  • 推荐线程自己停止下来
  • 推荐使用一个标志位进行终止变量,当flag=false时,则终止线程运行
package com.lily.demo01;

public class TestStop {
    private static boolean flag = true;

    public static void main(String[] args) {

        int i=0;
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (flag) {
                    if(i>100){
                        System.out.println("子线程停止运行...");
                        stop();
                        t.stop();
                    }
                    System.out.println("子线程正在运行" + Thread.currentThread().getName());
                }
            }
        });
        t.start();

        for (int j = 0; j < 20; j++) {
            System.out.println("主线程正在运行"+Thread.currentThread().getName());
        }
    }

    //设置一个公开的方法转换标志位
    public static void stop() {
        flag = false;
    }
}

线程休眠sleep

关于sleep()方法,在执行sleep()方法时,sleep不会释放锁,也就是说不会释放临界资源。

线程礼让yield

  • 线程礼让,让当前正在执行的线程暂停,但不阻塞,从运行态转为就绪态
  • 让CPU重新调度,但礼让不一定成功,看CPU心情
package com.lily.demo01;

public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();
        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();
    }
}

class MyYield implements Runnable {
    @Override
    public void run() {
        System.out.println("子线程开始执行" + Thread.currentThread().getName());
        Thread.yield(); //线程礼让
        System.out.println("子线程结束执行" + Thread.currentThread().getName());
    }
}
子线程开始执行a
子线程开始执行b
子线程结束执行a
子线程结束执行b

Process finished with exit code 0

线程强制执行join

  • join合并线程,待此线程执行完毕后,再执行其它线程,其它线程阻塞
  • 可以想象成排队插队
package com.lily.demo01;

public class TestJoin implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("线程vip来了" + i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //启动子线程
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();

        for (int i = 0; i < 1000; i++) {
            if (i == 200) {
                //子线程插队
                thread.join();
            }
            System.out.println("main" + i);
        }
    }
}

观测线程状态Thread.State

在Java中,线程通常有如下状态:

  1. NEW:就绪态
  2. RUNNABLE:运行态
  3. BLOCKED:阻塞态
  4. WRITING:正在等待另一个线程执行特定动作的线程处于此状态
  5. TIMED_WRITING:正在等待另一个线程执行动作达到指定时间的线程处于此状态
  6. TERMINATED:终止态

线程优先级

Java提供了一个线程调度器监控程序中启动后进入就绪状态的所有进程,线程调度器按照优先级应该调度哪个线程获取CPU资源。

现成的额优先级用数字表示,范围从1-10

  • Thread.MIN_PRIORITY = 1
  • Thread.MAX_PRIORITY=10
  • Thread.NORM_PRIORITY = 5

使用如下方法改变或获取优先级

  • getPriority()
  • setPriority(int xxx)

线程同步问题

多个线程操作同一个资源。由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁时,独占资源,其他线程必须等待,使用后释放锁即可,但存在以下问题:

  1. 一个线程持有锁会降低性能,因为别的线程需要该资源必须阻塞等待。
  2. 在多线程竞争下,加锁、释放锁会引起线程上下文切换和调度,会导致额外的资源浪费。
  3. 如果一个优先级高的线程等待一个优先级低的进程释放锁,会导致优先级倒置,引起性能问题。

两个不安全的实例

  1. 不安全的买票
package com.lily.syn;

//不安全的买票
public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket station = new BuyTicket();
        new Thread(station,"苦逼的我").start();
        new Thread(station,"牛逼的你们").start();
        new Thread(station,"可恶的黄牛党").start();
    }
}

class BuyTicket implements Runnable {

    //票
    private int ticketNumber = 10;
	//标志位
    private boolean flag = true;

    @Override
    public void run() {
        //买票
        while (flag) {
            try {
                buy();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private void buy() throws InterruptedException {
        //判断是否有票
        if (ticketNumber <= 0) {
            flag = false;
            return;
        }
        //模拟延时
        Thread.sleep(100);
        //买票
        System.out.println(Thread.currentThread().getName() + "买到了" + ticketNumber--);
    }
}
  1. 不安全的银行取钱
package com.lily.syn;

//两个人去银行取钱
public class UnsafeBank {
    public static void main(String[] args) {
        Account account = new Account(100,"基金");
        Drawing you = new Drawing(account,50,"你");
        Drawing girlFriend = new Drawing(account,100,"girlFriend");
        you.start();
        girlFriend.start();
    }
}

//账户
class Account {
    int money;  //余额
    String name;    //卡名

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

//银行:模拟取款
class Drawing extends Thread {
    Account account;      //账户
    int drawingMoney;     //取了多少钱
    int nowMoney;        //手里的钱

    public Drawing(Account account, int drawingMoney, String name) {
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    //取钱
    @Override
    public void run() {
        //判断有没有钱
        if (account.money - drawingMoney <= 0) {
            //钱不够
            System.out.println(Thread.currentThread().getName() + "钱不够,取不了");
            return;
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        //取钱
        account.money -= drawingMoney;
        nowMoney += drawingMoney;
        System.out.println(account.name + "余额为:" + account.money);
        System.out.println(this.getName() + "手里的钱" + nowMoney);
    }
}
  1. 不安全的ArrayList
package com.lily.syn;

import java.util.ArrayList;
import java.util.List;

public class UnsafeList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
        System.out.println(list.size());
    }
}

线程同步方法

  1. 对不安全的买票实例进行修改:
class BuyTicket implements Runnable {

    //票
    private int ticketNumber = 10;

    private boolean flag = true;

    @Override
    public void run() {
        //买票
        while (flag) {
            try {
                buy();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private synchronized void buy() throws InterruptedException {
        //判断是否有票
        if (ticketNumber <= 0) {
            flag = false;
            return;
        }
        //买票
        System.out.println(Thread.currentThread().getName() + "买到了" + ticketNumber--);
    }
}
  1. 对不安全的银行取钱示例进行修改
package com.lily.syn;

//两个人去银行取钱
public class UnsafeBank {
    public static void main(String[] args) {
        Account account = new Account(100, "基金");
        Drawing you = new Drawing(account, 50, "你");
        Drawing girlFriend = new Drawing(account, 100, "girlFriend");
        you.start();
        girlFriend.start();
    }
}

//账户
class Account {
    int money;  //余额
    String name;    //卡名

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

//银行:模拟取款
class Drawing extends Thread {
    Account account;      //账户
    int drawingMoney;     //取了多少钱
    int nowMoney;        //手里的钱

    public Drawing(Account account, int drawingMoney, String name) {
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    //取钱
    @Override
    public void run() {

        synchronized (account) {
            //判断有没有钱
            if (account.money - drawingMoney <= 0) {
                //钱不够
                System.out.println(Thread.currentThread().getName() + "钱不够,取不了");
                return;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //取钱
            account.money -= drawingMoney;
            nowMoney += drawingMoney;
            System.out.println(account.name + "余额为:" + account.money);
            System.out.println(this.getName() + "手里的钱" + nowMoney);
        }
    }
}
  1. 对不安全的集合进行修改
package com.lily.syn;

import java.util.ArrayList;
import java.util.List;

public class UnsafeList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (list) {
                        list.add(Thread.currentThread().getName());
                    }
                }
            }).start();
        }
        System.out.println(list.size());
    }
}

Lock锁

  • 从JDK5.O开始,Java提供了更强大的线程同步机制-通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。
  • 锁提供了对共享资源的独占访问每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获取锁。
  • ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。
package com.lily.syn;

import java.util.concurrent.locks.ReentrantLock;

//测试Lock锁
public class TestLock {
    public static void main(String[] args) {
        TestLock2 testLock2 = new TestLock2();
        new Thread(testLock2, "SubThread1").start();
        new Thread(testLock2, "SubThread2").start();
        new Thread(testLock2, "SubThread3").start();
    }
}

class TestLock2 implements Runnable {

    //定义lock锁
    private final ReentrantLock lock = new ReentrantLock();
    private int tickerNumer = 10;

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();    //加锁
                if (tickerNumer > 0) {
                    System.out.println("当前" + Thread.currentThread().getName() + "抢到了票,还剩票数:" + tickerNumer--);
                } else {
                    break;
                }
            } finally {
                lock.unlock();  //解锁,释放资源
            }
        }
    }
}

关于Lock和synchronized比较

  • Lock锁是显式锁(手动开启和关闭),synchronized是隐式锁,出了作用域自动释放。
  • Lock只有代码块锁,synchronized有代码块锁和方法锁。
  • 使用Lock锁,JVM将花费少量的时间调度线程,性能更好,并且具有更好的扩展性。
  • 优先使用顺序:Lock > 同步代码块 > 同步方法。

线程池

由于经常创建和消耗线程,会造成比较大的资源浪费,对性能有较大影响,因此提出线程池的概念。

提前创建好多个线程,放入线程池中,使用时直接获取,用完之后放回线程池中,可以避免的创建、销毁线程。实现重复利用。

好处:

  • 提高响应速度(减少了创建线程的时间)
  • 降低资源消耗
  • 便于管理
    • corePoolSize:核心池大小
    • maximumPoolSize:最大线程数
    • keepAliveTime:线程没有任务时最多保持多长时间后会终止

JDK5.0起提供了两种线程池相关API:ExecutorServiceExecutors

ExecutorService:真正的线程池接口。常见的子类ThreadPoolExecutor

  • void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
  • Future submit(Callable task):执行任务,有返回值,返回值为T,一般用来执行Callable

Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

package com.lily.syn;

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

public class TestPool {
    public static void main(String[] args) {
        //创建线程池,里面有10个线程(进程池大小)
        ExecutorService service = Executors.newFixedThreadPool(10);
        service.execute(new MyThrad());
        service.execute(new MyThrad());
        service.execute(new MyThrad());
        service.execute(new MyThrad());
        service.execute(new MyThrad());
        //关闭连接
        service.shutdown();
    }
}

class MyThrad implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
pool-1-thread-2
pool-1-thread-5
pool-1-thread-1
pool-1-thread-3
pool-1-thread-4

Process finished with exit code 0

标签:Java,Thread,int,void,用法,线程,new,多线程,public
From: https://www.cnblogs.com/lilyflower/p/18066488

相关文章

  • Java登陆第三十四天——使用Vite创建工程化的Vue3项目
    VueVue是基于标准HTML、CSS和JavaScript构建的前端框架,可以更高效地开发前端页面。ViteVite是Vue团队开发的项目管理工具。Maven的主要功能引入依赖项目管理使用Maven可以工程化的管理后端代码。npm的主要功能:引入依赖vite的主要功能:项目管理使用npm+vit......
  • 一次解决Docker内java变量原因导致执行Kafka查询消费报错经历
    引言企业内对某设备小集群进行状态巡检(包括内存、磁盘、CPU、集群状态、集群Docker内接口状态、服务状态、Kafka消费情况监控)。由于需要将状态的结果通过命令展示在命令行中,且查询命令较多,于是打算脚本解决。在写脚本时,查询内容包括了宿主机和docker内的服务都需......
  • Java 遍历文件夹内每个文件夹的文件
    在Java中,你可以使用java.nio.file包中的Files和DirectoryStream类来遍历文件夹内的所有文件,包括子文件夹中的文件。以下是一个示例代码,展示了如何实现这个功能:importjava.nio.file.DirectoryStream;importjava.nio.file.Files;importjava.nio.file.Path;importjava.nio.f......
  • JavaScript逆向之有道翻译加解密全过程解析
    本篇文章用于解析有道翻译中的加解密全过程url:https://fanyi.youdao.com/index.html#/加密访问网址,输入框中随便输入一个英文单词,查看触发流量包,只看Fetch/XHR类型的。这里主要关注webtranslate的这条,请求参数和响应数据都是有加密的,主要了解其的加解密逻辑。根据url定位......
  • QT 多线程
     第一种:静态函数1voidprint()2{3for(inti=0;i<5;i++)4qInfo()<<"helloglobalprint";5}6classMainWindow:publicQWidget7{8Q_OBJECT9public:10MainWindow(QWidget*parent=nullptr):QWidget(parent)......
  • Java诊断工具Arthas:开篇之watch实战
    Arthas是阿里开源的线上监控诊断产品,用于问题的排查和诊断。它的出现大大提高线上排查问题的效率,这篇只讲它一个非常牛逼的功能,其它功能往后篇章会在展开详细说。一、Arthas能为你做什么?1、遇到问题无法在线上debug,难道只能通过加日志再重新发布吗?2、我改的代码为什么没有执......
  • Java基础输入输出
    好久不写普通Java,一上手发现简单的部分都忘记怎么写了……趁着这次练习,赶紧记一下packageorg.example;importjava.util.Scanner;publicclassMain{publicstaticvoidmain(String[]args){//System.out.println("Helloworld!");Scannerscanner=ne......
  • ubuntu20.04-通过docker安装jenkins并自动化发布java
    前言jenkins需要git、maven和node,其中maven和node手动添加,git和git环境是jenkins镜像自带了的。如果删除了docker对应的jenkins容器后(删除前一定备份jenkins_home整个文件夹,否则怕前功尽弃:tar-czvfjenkins_home.tar.gz/var/jenkins_home)如果删除了容器,需要重新安装并配置m......
  • java: 程序包com.sun.org.slf4j.internal不存在
    java:程序包com.sun.org.slf4j.internal不存在事件之由来问题之分析处理之方案收工事件之由来拉完别人的项目后,启动不了了,报错提示:java:程序包com.sun.org.slf4j.internal不存在1问题之分析就是别人用lombok了同时使用slf4j和lombok的时候会出现这个问题原因是slf4j和lombok自......
  • Java Http Get Post 请求工具类
    importcom.alibaba.fastjson.JSONObject;importorg.apache.http.NameValuePair;importorg.apache.http.client.config.RequestConfig;importorg.apache.http.client.entity.UrlEncodedFormEntity;importorg.apache.http.client.methods.CloseableHttpResponse;importorg......