多线程

【java多线程编程】三种多线程的实现方式 文章目录前言进程与线程继承Thread类,实现多线程FAQ 为什么多线程的启动不直接使用run()方法而必须使用Thread类中start()方法呢?基于Runnable接口实现多线程Thread 与 Runnable 的关系Callable实现多线程线程运行状态 前言       在java语言最大的特点是支持多线程的开发(也是为数不多... 阅读详情

多线程

线程简介

​ 线程:目前正在做的事情,独立执行的路径

​ 多线程:目前有多个事情同时进行

​ 进程:操作系统运行的程序。eg:播放器、聊天工具…

​ 一个进程可以拥有多个 线程。 eg:视频同时播放音乐、图形…

​

线程创建

三种创建方式

	Thread (class)
    	=> class xx extends Thread
	Runnable (Interface)
		=> class xx implements Runnable
	Callable (Interface)
		=> class xx implements Callable
	

Thread

​ 开发步骤:

​ 1.自定义线程类继承Thread类

​ 2.重写run()方法,编写线程执行体

​ 3.创建线程对象,调用start()方法启动线程

​ 注: 线程不一定立即执行、由 CPU 安排调度

​ 故可能会交替控制,因为同时只做一件事情

//1.
class MyThread extends Thread{
    //2.

    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("这是run方法--"+i);
        }
    }
}

public class Test  {
    //main线程 主线程
    public static void main(String[] args) {

        //3.
        MyThread myThread = new MyThread();
        myThread.start();



        for (int i = 0; i < 400; i++) {
            System.out.println("这是main "+i);
        }

        /*
         中间部分会出现这种 情况
            这是run方法--8
            这是run方法--9
            这是main 120
            这是run方法--10
            这是main 121
            这是run方法--11
            这是main 122
            这是run方法--12
            这是main 123
            这是run方法--13
            这是main 124

            
         */
    }
}

Runnable

​ 【推荐该方法、因为Java是单继承】

​ 开发步骤:

​ 1.定义xxxx类实现Runnable接口

​ 2.实现run()类

​ 3.创建线程对象,调用start()方法启动线程

​ 注:

​ 相对于Thread 方式、Runnable需要 new 一个Thread类进行代理

//1.
class MyRunnable implements Runnable{
    //2.

    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("这是Runnable-run方法--"+i);
        }
    }
}

public class Test  {
    //main线程 主线程
    public static void main(String[] args) {

        //3.
        MyRunnable myRunnable = new MyRunnable();

        //创建线程对象,通过线程对象来开启线程 ->代理
        new Thread(myRunnable).start();
    }
}

Callable

​ 【了解即可】

​ 开发步骤:

​ 1.实现Callable接口,需要返回值类型

​ 2.重写call方法,需要抛出异常

​ 3.创建目标对象

​ 4.创建执行服务

​ 5.提交执行

​ 6.获取结果

​ 7.关闭服务

案例:龟兔赛跑

class Race implements Runnable{

    //胜利方
    private static String winner;

    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {

            //设置某一对象 休眠
            if (Thread.currentThread().getName().equals("兔子") && i%10 == 0){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            //作为结束的条件
            boolean flag = gameOver(i);
            if (flag) break;

            System.out.println(Thread.currentThread().getName()+"==>跑了 "+i +" 步");
        }
    }

    //判断谁是胜利方
    private boolean gameOver(int step){
        if (winner != null){
            return true;
        }
        if (step >= 100){
            winner = Thread.currentThread().getName();
            System.out.println("winner is " + winner);
            return true;
        }
        return false;

    }
}

public class Test  {
    public static void main(String[] args) {
        Race race =new Race();

        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
}



Lambda 表达式

​ 避免 匿名内部类 定义 过多

​ 增加可读性

​ 保留核心逻辑

​ 实质:函数式编程

(params)->expression[表达式]
(params)->statement[语句]
(params)->{statements}

函数式接口

​ 只包含唯一一个抽象方法

public interface xxx{
	public abstract void xxx();
}

​ 对于此类,我们可以通过lambda表达式 来创建该接口对象

举例

原始版本
//1.定义函数式接口
interface MyLambda{
    void run();
}



public class Test  {



    public static void main(String[] args) {
        //2.创建对象
        MyLambda lambda;

        //3.实现匿名内部类 Lambda 表达式
        lambda = () ->{
            System.out.println("Lambda 表达式");
        };

        lambda.run();


    }
}
简化一

如果 抽象方法有参数,还可以将 形参类型 省略掉

//1.定义函数式接口
interface MyLambda{
    void run(int nums);
}



public class Test  {
    public static void main(String[] args) {
        //2.创建对象
        MyLambda lambda;

        //3.实现内部类 Lambda 表达式
//        lambda = (int nums) ->{
//            System.out.println("我跑了 "+nums + "公里");
//        };
        lambda = (nums) ->{
            System.out.println("我跑了"+nums + " 公里");
        };

        lambda.run(10); //我跑了10 公里
    }
}

​

简化二

​ 简化括号 + 简化 花括号

​ 注:

​ 只有单个(一个参数、一行代码)的情况式才能简化掉、否则必须使用。

​ 必须是函数式接口( 类型为接口、 且仅有一个抽象方法 )

public static void main(String[] args) {
        MyLambda lambda;

        lambda = nums -> System.out.println("我跑了 "+nums + " 公里");;

        lambda.run(12); //我跑了 12 公里
 }

线程状态

1.创建状态
	--->就绪状态 【启动线程】
2.就绪状态
	--->运行状态 【获取CPU资源】
3.阻塞状态
	--->就绪状态 【阻塞解除】
4.运行状态
	--->阻塞状态 【等待用户输入线程休眠等】
	--->就绪状态 【释放CPU资源】
	--->死亡状态  【线程自然执行完毕 || 外部干涉终止线程】
5.死亡状态

线程方法

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

停止线程

​ 不推荐jdk使用的stop()、destroy()

​ 推荐线程自己停下来

​ 建议使用一个标志位进行终止变量,当 flag=false,则终止线程运行

//1.线程正常停止 ==>利用次数、不采用死循环
//2.标志位 ==> flag
//3.不用stop()、destroy()
class MyThreadStop implements Runnable{

    //标志位
    private boolean flag = true;

    @Override
    public void run() {
        int i = 0;

        while (flag){
            System.out.println("Run...." + i++);
        }
    }

    //自己定义的stop方法、对外提供方法改变标志位
    public  void stop(){
        this.flag=false;
    }
}


public class Test  {



    public static void main(String[] args) {

            MyThreadStop stop = new MyThreadStop();

            new Thread(stop).start();

        for (int i = 0; i < 500; i++) {
            System.out.println("main..."+i);

            if (i == 400){
                stop.stop();
                System.out .println("线程该停止了");
            }

        }

    }
}

线程休眠

​ 语法形式:

xxx.sleep(millis)

​ xxx线程休眠 x 毫秒

注:

​ sleep 指定当前线程阻塞多少毫秒

​ sleep存在 InterruptedException 异常

​ sleep时间达到后进入 就绪状态

​ 可以模拟网络延时,倒计时

​ 每一个对象都有一个锁,sleep不会释放锁

​ 案例:模拟倒计时

public class Test  {
    public static void main(String[] args) {
        try {
            numsDown(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //模拟倒计时
    public static void numsDown(int nums) throws InterruptedException {
        while (true){
            Thread.sleep(1000);
            System.out.println(nums--);
            if (nums<=0) break;
        }
    }
}

线程礼让

​ 让当前线程暂定、但不造成阻塞

​ =>将线程从运行状态转为就绪状态

​

​ 注:

​ 由于 是由CPU重新调度,所以 不一定礼让成功

class MyYield implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 线程开始执行");
        Thread.yield(); // 礼让
        System.out.println(Thread.currentThread().getName()+"线程停止执行");
    }
}



public class Test  {
    public static void main(String[] args) {
        new Thread(new MyYield(),"Tom").start();
        new Thread(new MyYield(),"Jerry").start();

        /*
            礼让成功:
                Tom 线程开始执行
                Jerry 线程开始执行
                Tom线程停止执行
                Jerry线程停止执行

        */
    }


}

线程强制执行

​ Join 合并线程,待此线程执行完成后,再执行其他线程,其他线程会阻塞

class ThreadJoin implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("Join最强 => "+i);
        }
    }
}



public class Test  {
    public static void main(String[] args) throws InterruptedException {
        ThreadJoin join=new ThreadJoin();

        Thread thread = new Thread(join);
        thread.start();

        for (int i = 0; i < 10; i++) {
            if (i==5){
                thread.join();
            }
            System.out.println("main => " + i);
        }
        /*
            main => 0
            main => 1
            main => 2
            main => 3
            main => 4
            Join最强 => 0
            Join最强 => 1
            Join最强 => 2
            Join最强 => 3
            Join最强 => 4
            Join最强 => 5
            Join最强 => 6
            Join最强 => 7
            Join最强 => 8
            Join最强 => 9
            Join最强 => 10
            Join最强 => 11
            Join最强 => 12
            Join最强 => 13
            Join最强 => 14
            Join最强 => 15
            Join最强 => 16
            Join最强 => 17
            Join最强 => 18
            Join最强 => 19
            main => 5
            main => 6
            main => 7
            main => 8
            main => 9
         */
    }
}

线程状态观测

Thread.State
	New 
		尚未启动的线程
	Runnable
		在java虚拟机执行的线程
     Blocked
     	被阻塞等待监视器锁定的线程
     Waiting
     	正在等待另一个线程执行特定动作的线程
     Timed_Waiing
     	正在等待另一个线程执行动作到达指定等待时间的线程
     Terminated
     	已退出的线程
public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread.State state = thread.getState();
        System.out.println(state); //NEW

        thread.start();
        state=thread.getState();
        System.out.println(state); //RUNNABLE

        while (state != Thread.State.TERMINATED){
            Thread.sleep(100);
            state = thread.getState();
            System.out.println(state);
            /*
                TIMED_WAITING
                TIMED_WAITING  =>因为在run时 Thread.sleep(300);
                TERMINATED
             */

        }
    }

线程优先级

​ java提供 线程调度器 来监控 就绪状态的 所有线程

​

​ 优先级用数字表示:1~10

​ Thread.MIN_PRIORITY = 1;

​ Thread.MAX_PRIORITY = 10;

​ Thread.NORM_PRIORITY = 15;

​ 语法形式

getPriority.setPriority(int xxx)

注:

​ 优先级越高,启动概率越高

​ 一定要先设定优先级、再启动

public static void main(String[] args) throws InterruptedException {
        //主线程优先级
        System.out.println( Thread.currentThread().getName()+"==>"+Thread.currentThread().getPriority());

        ThreadPriority priority =new ThreadPriority();

        Thread t1 = new Thread(priority);
        Thread t2 = new Thread(priority);
        Thread t3 = new Thread(priority);
        Thread t4 = new Thread(priority);
        Thread t5 = new Thread(priority);

        //设置优先级
        t1.start();

        t2.setPriority(1);
        t2.start();

        t3.setPriority(Thread.MAX_PRIORITY);
        t3.start();

        /*
            main==>5
            Thread-2==>10
            Thread-0==>5
            Thread-1==>1
         */


    }

守护线程 daemon

​ 线程分为 用户线程 和 守护线程

​ 虚拟机 必须确保用户现场执行完毕、但不用等待守护线程执行完毕

xxx.setDaemon(true); //默认是fasle,即用户线程

线程同步

​ 多个线程控制一个资源

​

​ 处理多线程时,多个线程访问同一个对象,并且某些线程想修改对象,此时则需要线程同步。

​ 线程同步:一种等待机制,多个需要同时访问此对象的线程 进入 该对象 的 等待池 形成队列,等待前面线程使用完毕,下一个线程再使用。

队列和锁

​ 形成 线程同步 就需要 队列 + 锁

​ synchronized,当一个对象获得对象的排他锁,独占资源,其他线程就必须等待。

释放锁,会导致:

​ 一个线程持有锁会导致其他所有需要此锁的线程挂起

​ 在多线程竞争时,加锁,释放锁会引发性能问题

​ 优先级高 等待 优先级低 的 锁,会导致 优先级倒置,引发性能问题

​

不安全案例

​ 火车站买票案例

//不安全的买票
class BuyTicket implements Runnable{


    //票
    int ticketNums = 10;
    boolean flag = true;

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

    private void buy() throws InterruptedException {
        if (ticketNums <= 0){
            flag = false;
            return;
        }

        //模拟延时
        Thread.sleep(100);

        System.out.println(Thread.currentThread().getName()+" 买到 "+ticketNums--);
    }
}


public class Test  {
    public static void main(String[] args) throws InterruptedException {

        BuyTicket buyTicket = new BuyTicket();

        new Thread(buyTicket,"Tom").start();
        new Thread(buyTicket,"Jerry").start();
        new Thread(buyTicket,"Jack").start();
    }


}

产生的问题:

​ 数据混乱

​ 产生拿到 -1

​ 不同线程拿到 同一张

​ 产生原因:每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。

银行取钱问题

class Account{
    int money;
    String name;

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

class Drawing extends Thread{

    private Account account;
    private int drawingMoney;
    private int nowMoney;

     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(this.getName()+" 余额不足");
            return;
        }


        //sleep可以放大问题的可能性
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(account.name+" 余额: "+account.money);

        //账户的资金变动
        account.money = account.money -drawingMoney;
        //用户拿到的钱数
        nowMoney = nowMoney +drawingMoney;



        System.out.println(this.getName()+"手里的钱"+nowMoney);
    }
}

public class Test  {
    public static void main(String[] args) throws InterruptedException {
        Account account = new Account(100,"零花钱");


        Drawing drawing = new Drawing(account,50,"Tom");
        Drawing drawing1 = new Drawing(account,100,"Jerry");

        drawing.start();

        drawing1.start();

        /*
            零花钱 余额: 100
            Tom手里的钱50
            零花钱 余额: 100
            Jerry手里的钱100
         */


    }
}

产生问题:

​ 两者都看到同样的余额,取出来的钱总数会大于 额数。

synchronized机制

​ 在此处通过锁机制 synchronized 来保证数据在方法中被访问时的正确性。

​

synchronized 包括两种用法:

同步方法
public synchronized void method(int args){}

​ synchronized方法控制对 “对象”的访问,每个对象都对应着 一把锁,每个方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得该锁,继续执行。

​ 缺点: 若将一个大的方法申明为synchronized 将会影响效率

eg:火车票案例改进

class BuyTicket implements Runnable{


    //票
    int ticketNums = 10;
    boolean flag = true;

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

    //锁的是this
    private synchronized void buy() throws InterruptedException {
        if (ticketNums <= 0){
            flag = false;
            return;
        }

        //模拟延时
        Thread.sleep(100);

        System.out.println(Thread.currentThread().getName()+" 买到 "+ticketNums--);
    }
}


public class Test  {
    public static void main(String[] args) throws InterruptedException {

        BuyTicket buyTicket = new BuyTicket();

        new Thread(buyTicket,"Tom").start();
        new Thread(buyTicket,"Jerry").start();
        new Thread(buyTicket,"Jack").start();

        /*
            Tom 买到 10
            Tom 买到 9
            Jack 买到 8
            Jerry 买到 7
            Jerry 买到 6
            Jack 买到 5
            Jack 买到 4
            Tom 买到 3
            Jack 买到 2
            Jerry 买到 1
         */
    }


}
同步块
synchronized(Obj){}

​ Obj:同步监视器

​ 可以是任意对象,但推荐使用共享资源作为同步监视器

​ 同步方法无需指定监视器,因为同步方法的同步监视器就是 this(对象本身)

​

eg: 银行取钱的问题

package javaEE;


import java.io.BufferedWriter;
import java.util.concurrent.Callable;


class Account{
    int money;
    String name;

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

class Drawing extends Thread{

    private Account account;
    private int drawingMoney;
    private int nowMoney;

    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(this.getName()+" 余额不足");
                return;
            }

            //sleep可以放大问题的可能性
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(account.name+" 余额: "+account.money);

            //账户的资金变动
            account.money = account.money -drawingMoney;
            //用户拿到的钱数
            nowMoney = nowMoney +drawingMoney;



            System.out.println(this.getName()+"手里的钱"+nowMoney);
        }


    }
}

public class Test  {
    public static void main(String[] args) throws InterruptedException {
        Account account = new Account(100,"零花钱");


        Drawing tomDrawing = new Drawing(account,50,"Tom");
        Drawing jerryDrawing = new Drawing(account,100,"Jerry");

        tomDrawing.start();

        jerryDrawing.start();

        /*
            零花钱 余额: 100
            Tom手里的钱50
            零花钱 余额: 100
            Jerry手里的钱100
         */


    }
}

死锁

​ 多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才运行,而导致多个线程都停止,互相等待释放。

​ 即某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题

​ 死锁产生的必要条件:

​ 1.互斥条件:一个资源每次只能被一个进程使用

​ 2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放

​ 3.不剥夺条件:进程已获得的资源,在未完成前,不得强行

​ 4.循环等待条件:若干进程之间形成一宗种头尾相接的循环等待资源关系。

​ 避免死锁,只需要避免以上条件中的一条或多条即可

Lock锁

​ 通过显式定义同步锁对对象来实现同步。同步锁使用Lock对象充当。

Lock接口及相关实现类

interface Lock //Lock接口
	class ReentrantLock //拥有与synchronized相同的并发性和内存语句
	class ReentrantLock //可以显示加锁、释放锁

eg:

class LockDemo implements Runnable{

    //票
    int ticketNums = 10;

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

    @Override
    public void run() {
        while (true){
            try {
                lock.lock();//加锁

                //购买
                if (ticketNums > 0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(ticketNums--);
                }else {
                    break;
                }
            }finally {
                lock.unlock(); //解锁
            }
            }



    }
}


public class Test  {
    public static void main(String[] args) throws InterruptedException {
        LockDemo lockDemo = new LockDemo();

        new Thread(lockDemo).start();
        new Thread(lockDemo).start();
        new Thread(lockDemo).start();
    }
}

注:

​ Lock是显式锁(需手动 开启、关闭锁) synchronized 是隐式锁(出了作用域便释放了)

​ Lock只有代码块锁

​ 使用Lock锁,JVM可更快的调度线程

​ 推荐使用顺序:

​ Lock > 同步代码块 > 同步方法

线程通信

​ 主要应用的是:

Thread.wait(); //等待被唤醒
Thread.notifyAll(); //唤醒线程

​ 应用场景:生产者和消费者问题

​ 1.比如 仓库只存放一件产品,生产者负责放入,消费者负责取出

​ 2.若仓库没有产品,生产者放入,然后停止生产,直到仓库的产品被取出

​ 3.若仓库有产品,消费者取出,然后停止消费,直到仓库的产品被生产者放入

​

管程法

​ 通过缓冲区

//生产者
class Producer extends Thread{
    Warehouse warehouse;

    public Producer(Warehouse warehouse){
        this.warehouse=warehouse;
    }

    //生产

    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println("生产了 "+ i + " 个产品");
            warehouse.push(new Product(i));
        }

    }
}

//消费者
class Consumer extends Thread{
    Warehouse warehouse;

    public Consumer(Warehouse warehouse){
        this.warehouse=warehouse;
    }

    //消费
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println("消费了 "+ warehouse.pop().id+ " 个产品");

        }
    }
}

//产品
class Product{
    int id;

    public Product(int id) {
        this.id = id;
    }
}

//仓库(缓冲区)
class Warehouse{

    //能容纳多少
    Product[] products = new Product[10];
    //当前产品有多少
    int count = 0;

    //生产者投入产品
    public synchronized void push(Product product){
        //如果仓库满了,则需要等待消费者消费
        if (count == products.length){
            //通知消费者消费
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

        //如果没有满,则需要生产者放入
        products[count] = product;
        count++;

        //通知消费者消费
        this.notifyAll();
    }

    //消费者消费
    public synchronized Product pop(){
        //判断能否消费
        if (count==0){
            //等待生产者生产
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //消费
        count--;
        Product product = products[count];


        this.notifyAll();
        return product;

    }
}



public class Test  {
    public static void main(String[] args) throws InterruptedException {
        Warehouse warehouse = new Warehouse();

        Producer producer = new Producer(warehouse);
        Consumer consumer = new Consumer(warehouse);

        producer.start();
        consumer.start();

    }
}

信号灯法

​ 通过标志位

//生产者
class Producer extends Thread{
    Product product;

    public Producer(Product product){
        this.product=product;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (i%2==0){
                this.product.push("苹果手机");
            }else {
                this.product.push("谷歌手机");
            }
        }
    }
}

//消费者
class Consumer extends Thread{
    Product product;

    public Consumer(Product product){
        this.product=product;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            this.product.pop();
        }
    }
}

//产品
class Product{

    String name;

    //生产者生产、消费者等待 T
    //消费者取出、生产者生产 F
    boolean flag = true;

    //生产者 放入
    public synchronized void push(String name){
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("生产者生产了 "+name);

        //通知消费者消费
        this.notifyAll();
        this.name=name;
        this.flag = !this.flag;
    }

    //消费者 取出
    public synchronized void pop(){
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("消费者购买了"+this.name);

        //通知生产者生产
        this.notifyAll();
        this.flag = !this.flag;
    }
}




public class Test  {
    public static void main(String[] args) throws InterruptedException {
        Product product = new Product();

        new Producer(product).start();
        new Consumer(product).start();

    }
}

线程池

​ 出现的背景:

​ 经常创建或销毁、使用量特别大的资源时,对性能的影响较大

​ 思路:

​ 提前创建线程,放入线程池,需要时取出,不需要时放回。可以避免频繁创建销毁、实现重复利用。

​ 好处:

​ 提高了响应速度

​ 降低资源消耗

​ 便于线程管理

​ corePoolSize: 线程池的大小

​ maximumPoolSize:最大线程数

​ keepAliveTime:线程没有任务时最多保持多长时间后会终止

相关线程池类

ExecutorService -> interface
	void execute(Runnable command):执行命令,没有返回值,一般用于执行Runnable
	<T> Future<T> submit(Callable<T> task):执行任务,有返回值。一般用来执行Callable
	void shutdown():关闭线程池
	
Executors -> class
	用于创建并返回不同类型的线程池

eg:

class ThreadGo implements Runnable{

    @Override
    public void run() {
            System.out.println(Thread.currentThread().getName());
    }
}



public class Test  {
    public static void main(String[] args) throws InterruptedException {
        //1.创建服务,创建线程池
        ExecutorService service = Executors.newFixedThreadPool(10);

        //线程池创建线程
        service.execute(new ThreadGo());
        service.execute(new ThreadGo());
        service.execute(new ThreadGo());
        service.execute(new ThreadGo());
        service.execute(new ThreadGo());

        //关闭连接
        service.shutdown();


    }
}
Ruby 多线程 多线程是一种允许单个程序同时执行多个任务的技术。在传统的单线程程序中,任务通常是顺序执行的,这意味着如果一个任务需要较长时间才能完成,它会阻塞后续任务的执行。而在多线程环境中,程序可以同时运行多个线程,每个线程执行一个独立的任务,从而提高了程序的效率和响应速度。 阅读详情

相关推荐

多线程基础入门:从单线程到多线程的魔法之旅

说到多线程,大家第一印象可能就是“并发”、“并行”等词,但这些词汇的背后究竟藏着怎样的魔法?对于 Java 初学者来说,多线程就像一个复杂的迷宫,如果没有明确的方向和清晰的思路,容易被概念和代码搞得头晕眼花。这篇文章将带你逐步解锁多线程的基础,配合示例代码,力求让你在这条学习之路上不再迷茫,真正地从概念到实践掌握多线程的精髓。简单来说,多线程是一种让程序能在同一时间执行多个任务的技术。它模拟了我们人类的多任务能力,比如边听音乐边写代码。多线程编程不仅是技术的挑战,更是思维的突破。

喵手的博客 1352

Arduino的多线程

前言 本章节是如何使用Arduino 的多线程技术。截止目前,Arduino 的性能很棒,但是如果我们想在不同的时间间隔中,完成两件、多件的任务呢? 此时,我们就需要使用一种类多线程技术来处理项目。 Arduino 的多线程 \ 类多线程? 为了正确理解多线程技术和Arduino 之间的关系,我们需要理解为什么它不是真正的多线程。 在计算机CPU上,多线程应用程序...

TEST_PROGRAM_NONE 3万+

微型纵向加密在新能源场站中的实战部署与运维避坑指南

本文详细解析了微型纵向加密在新能源场站中的实战部署与运维技巧。通过国密算法SM4实现数据加密,确保新能源场站通信安全,同时提供非侵入式部署方案和运维避坑指南,帮助提升网络稳定性和运维效率。

weixin_30505225的博客 597

多线程一,什么是多线程,创建多线程的几种方式

1 多线程相关概念 并发(Concurrent):在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。 同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。 并行(Parallel):当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,这种方式我们称之为

世上哪有什么岁月静好,不过是有人替你负重前行 2万+

椭圆曲线点加的推导公式(一)

椭圆曲线点加推导过程

qq_32100199的博客 1517

SECS/GSM 测试工具 SecsExpress 的配置与实战应用

本文详细介绍了半导体设备通讯测试工具SecsExpress的配置与实战应用。作为专业的SECS/GSM协议调试工具,它能模拟Host与设备端进行双向测试,帮助工程师快速部署、验证通讯链路,并高效排查与RMS系统或真实设备间的联调问题,是提升半导体工厂设备集成与维护效率的利器。

yhn456789的博客 298

多线程详解

​​学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。各位小伙伴,如果您:想系统/深入学习某技术知识点…一个人摸索学习很难坚持,想组团高效学习…想写博客但无从下手,急需写作干货注入能量…热爱写作,愿意让自己成为更好的人…...

m0_65144570的博客 2062

详解在linux上安装vpn客户端

详解在linux上安装vpn客户端

FuncPlotCalc 8427

Qt多线程

Qt 提供了多种方式来处理多线程编程,它不仅使多线程操作更易于管理,还与 Qt 的事件驱动架构紧密集成。下面是 Qt 多线程编程的详细介绍,包括关键类、模式以及常见的使用场景。

強雲的博客 2319

用逻辑分析仪实测EM4095:125kHz RFID耳标解码的电压与脉宽玄学

本文通过逻辑分析仪实测EM4095芯片,深入解析125kHz RFID耳标解码中的电压与脉宽关联现象。揭示了供电电压对曼彻斯特编码脉宽的影响规律,并提供了一套可落地的调试方法论,包括信号链路解析、逻辑分析仪配置技巧及系统级稳定性提升方案,帮助开发者解决解码不稳定问题。

weixin_30629653的博客 329

NVIDIA Isaac Sim 入门教程(二)

Omniverse Isaac Sim 的工作空间非常容易自定义。调整用户界面的大小、停靠、取消停靠、添加和移除面板都非常容易。进入顶部菜单栏,单击下拉菜单最下方的窗口 > 视口 > 视口 2(现在工作区中应该会出现一个名为 "视口 2 "的新视口。如果您想创建更多的视口,请按照相机检查器扩展进行操作。单击并拖动视口 2 的顶部标题以激活停靠部件。拖放到原始视口的右侧,将两个视口并排放置。单击并向左拖动视口 2 的左侧,使视口大小相等。

机器人控制与仿真应用 6545

什么是多线程

什么是多线程? 多线程是一种同时执行多个线程的技术,它不允许进程或操作系统生成计算机中运行的程序的多个副本,而是在不创建程序多个副本的情况下,同时管理多个用户或同一用户的多个请求。 线程是轻量级进程,是处理的最小单元,同时运行多个线程称为多线程。因此,基本上,这是一种由多个处理器使用单个代码集的方法。 了解多线程 1.为了理解多线程,需要理解两个术语。 (1)线程:线程是进程的独立或基本单元。 (2)进程:正在执行的程序称为进程,一个进程中存在多个线程。 2.多线程中的执行是并发的和并行的。 (1)并发执行

qf2019的博客 1万+

计算机组成原理(第三版)唐朔飞-第十章控制单元的设计-课后习题

计算机组成原理(第三版)唐朔飞-第十章控制单元的设计-课后习题

weixin_45735391的博客 3万+

一文讲清多线程和多线程同步

一个进程内多个线程并发执行的情况就叫多线程,每个线程是一个独立的执行流,多线程是一种编程模型,它与处理器无关、跟设计有关。并行计算:充分利用多核,提升整体吞吐,加快执行速度。后台任务处理:将后台线程和主线程分离,在特定场景它是不可或缺的,如:响应式用户界面、实时系统等。我们用2个例子作说明。同一进程内的多个线程会共享数据,对共享数据的并发访问会出现(竞争状况)。协调多个线程对共享数据的访问,避免出现数据不一致的情况。

excellent_fish_c的博客 1875

利用恒源云云端GPU高效训练YOLOv11模型全流程:从环境搭建到模型部署,让你摆脱硬件束缚

记住,云端GPU的核心优势是“弹性算力+一键环境”,只要把握好这一工具特性,再结合YOLOv11的高性能模型架构,你也能在计算机视觉领域快速落地项目。接下来,我们从工具准备到模型部署,一步步带你实现高效训练,真正帮你解决资源与效率的核心痛点。凭借其在目标检测、实例分割任务上的卓越性能,成为众多开发者的首选。据统计,使用云端GPU训练YOLOv11,在处理百万级数据集时,训练效率较本地普通GPU提升。在安防监控中,YOLOv11可实时检测人员、车辆异常行为,某园区采用该方案后,异常事件识别准确率达。

LXY_LOVE36的博客 213

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

C++多线程-多线程调试

软件调试是我们软件开发过程中的重要一课。在前面,我们也讨论过程序调试,比如说这里。今天,我们还可以就软件调试多讲一些内容。比如说条件断点,数据断点,多线程断点等等。 #include <stdio.h> int value = 0; void test() { int total; int index; total = 0; ...

逍遥云恋 2346

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

php 怎么实现多线程,php 实现多线程

通过php的Socket方式实现php程序的多线程。php本身是不支持多线程的,那么如何在php中实现多线程呢?可以想一下,WEB服务器本身都是支持多线程的。每一个访问者,当访问WEB页面的时候,都将调用新的线程,通过这一点我们可以利用WEB服务器自身的线程来解决PHP不支持多线程的问题。下面给出通过 fsockopen() 建立socket连接,然后用 用fputs() 发送消息,来实现的PHP...

weixin_28756427的博客 1万+

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

【Java多线程】Java多线程技能

到 1. 进程和多线程的概念及线程的优点 提到多线程这个技术就不得不提及“进程”这个概念,在“百度百科”中对进程的解释如下: 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。 进程是受操作系统管理的基本运行单元。 那什么是线程呢? 线程可以理解成是在

一定要站在自己热爱的生活里闪闪发光 4万+

C++多线程

多线程(Multithreading)是指从软件或者硬件上实现多个线程并发执行的技术。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。

yegu001的博客 1379

Python 多线程

多线程是指程序中可以独立于其他部分并发执行的一系列指令。可以将它们视为进程中的不同单元,这些单元在被调度时可以独立完成任务。当线程需要等待缓慢的外部操作(如网络请求或磁盘访问)完成时,它们会暂时休眠,从而使调度器可以将时间分配给其他线程执行。进程是计算机程序的一个可执行实例。通常,一个进程是在单个控制流序列中执行的。进程是操作系统分配资源的基本单位,每个进程包含一个或多个线程。进程之间是隔离的,每个进程拥有自己的内存空间和系统资源。在这篇文章中,我们探讨了 Python 多线程的基本概念。

疯一样的码农 1428

C#多线程

C#中的多线程编程是开发高效并发应用程序的关键技术之一,它允许程序同时执行多个任务,从而提升应用程序的响应速度和性能。为了更好地理解C#中的多线程使用和定义,我们可以从以下几个方面来探讨:线程的基本概念、创建线程的方法、线程的状态管理以及线程同步机制。

C6666888的博客 1570

多线程爬虫

多线程简单描述就是指在一个进程中运行多个线程,以此来加快程序的运行效率。我们在写python代码时,那种按部就班,一步一步写完并执行的就是单线程;而在python中,想要实现多线程需要用到类Thread。

qq_75229677的博客 1797

Python多线程

想象你在厨房做饭:一边煮着汤,一边切着菜,偶尔还要看看烤箱里的蛋糕。这种同时处理多个任务的能力,就是多线程的生动体现。在计算机中,线程是程序执行的最小单位。多线程允许一个程序同时运行多个任务,特别适合I/O密集型操作(如网络请求、文件读写等)。适用场景I/O密集型任务(网络请求、文件操作等)需要保持响应性的GUI应用注意事项避免过度使用线程(线程也有开销)注意线程安全问题(使用锁、队列等)考虑使用线程池而非频繁创建线程替代方案CPU密集型任务:考虑multiprocessing。

陆小森_红齐飘飘的博客 2302

多线程的理解

多线程的理解 什么是多线程? ​ 多线程的简单理解,即:同时处理多个事项,宏观方面可以理解为计算机在cpu的调度下,同时进行多项工作;类比可以看做,武侠高手左手画圆,右手画三角形,同时进行;但是,实际上微观理解,单核cpu同一时间只能处理一件事情,所以是将各个线程分为细小的时间片来交替进行,但是由于相对于人类感官速度极快,产生统一时间俩件事情同时进行了。打个比方,现在用一台单核cpu计算机,同时打开俩个视频,只看视频播放画面,其实是将视频切割为一个个细小的时间片交替cpu调度执行,同一时间点,只有一个在被c

qq_36098674的博客 3164

python多线程编程

1、多线程的发展背景 随着计算机的发展,无论是硬件还是软件都在快速的发展。 在最开始的时候,计算机都是只有一个cpu来进行指令控制和运算,程序执行的时候都是一个进程一个进程的运行,也就是顺序执行的方式,所有的进程都是排成一个队列,然后cpu取出其中的一个进程,然后运行。 在硬件发展的时候,慢慢发展为几颗cpu,并且发展出来了几核cpu,从而在一般的服务器上都是四核的,并且至少是两颗,从而在每...

KEL 6223
上一篇: 异常
下一篇: Swagger
code_Floyed
博客等级 码龄7年 3粉丝 56原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值