JavaSE_22_线程(二)

本文介绍了Java中的线程同步问题,如并发访问导致的结果不一致,并通过同步方法、同步代码块和重入锁等方式解决。此外,还讨论了Java锁的概念,包括对象锁和类锁,以及可能出现的死锁现象。最后,文章探讨了线程池的优势和使用,如单一线程池、固定线程池、缓存线程池和定时线程池的示例及其应用场景。
摘要由CSDN通过智能技术生成

《线程(二)》

目录

  • 线程同步(掌握)
  • Java锁(了解)
  • 线程池(掌握)

一、线程同步

并发访问问题

在处理多任务时,多线程无疑是高效的,但由于默认情况下线程之间是异步的,就会产生并发访问问题。

请添加图片描述

案例一(存钱业务模拟)

  • 账户类
package com.hpr.test;

public class Account {

    private int money = 0;

    public void saveMoney(int save) {
        money = money + save;
    }

    public int getMoney() {
        return money;
    }
}
  • 测试类
package com.hpr.test;

public class Test {

    static class SaveThread extends Thread {

        private Account account;

        public SaveThread(Account account) {
            this.account = account;
        }

        @Override
        public void run() {
            //每个线程存款5次,每次存100
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(10);
                    account.saveMoney(100);
                    System.out.println(Thread.currentThread().getName() + "存钱成功,当前余额:" + account.getMoney());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        //1.创建同步资源:Account账户
        Account account = new Account();
        //2.创建三个线程
        SaveThread th1 = new SaveThread(account);
        th1.setName("th1");
        SaveThread th2 = new SaveThread(account);
        th2.setName("th2");
        SaveThread th3 = new SaveThread(account);
        th3.setName("th3");

        //3.启动线程
        th1.start();
        th2.start();
        th3.start();

        //4.加入主线程,优先执行
        try {
            th1.join();
            th2.join();
            th3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //5.主线程调用方法查看账户余额
        System.out.println("账户余额:" + account.getMoney());
    }
}

执行结果

变量及方法是线程共享的,即每个线程都可以访问,但不会排队访问,因此大概率下会造成结果不一致情况。

请添加图片描述

同步处理(三种方式)

  1. 同步方法(synchronized):同一时刻只能有一个线程进入该方法,其他线程需"排队"访问。
...
public class Account {
    ...
    public synchronized void saveMoney(int save) {
        money = money + save;
    }
    ...
}
  1. 同步代码块(给对象上锁,即同一时刻只能有一个线程访问该对象该部分代码块)
...
public class Account {
    ...
    public void saveMoney(int save) {
        synchronized (this) {
            money = money + save;
        }
    }
    ...
}
  1. 重入锁(重入锁是以对象形式使用的,要保证一个对象一个锁,可将其声明为全局变量)
...
public class Account {
    ..
    private Lock lock = new ReentrantLock();

    public void saveMoney(int save) {
        //上锁
        lock.lock();
        //执行业务
        money = money + save;
        //解锁
        lock.unlock();
    }
    ...
}

执行结果

请添加图片描述

二、Java锁

Java中每一个对象都有一个lock,当访问一个对象某个synchronized方法的时候,该对象就会被上锁,同一时刻只能有一个线程访问该对象synchronized资源,其他线程需排队等待。

注意:被上锁的是对象,而不是方法,非synchronized资源不受影响。

请添加图片描述

同步锁(对象锁)

对象中有多个synchronized方法,当多线程访问其中任意一个,保证有且仅有一个线程在同一时刻能够访问到synchronized资源,剩余线程进行排队等待。

public synchronized void saveMoney(int save) {
    money = money + save;
}

静态锁(类锁)

在静态方法中使用同步锁(static synchronized),该类将被上锁。即同步锁针对一个对象,而静态锁针对该类的所有对象。

public static synchronized void saveMoney(int save) {
    money = money + save;
}

死锁

当两个线程循环依赖于一对同步对象时将发生死锁,例如:

请添加图片描述

三、线程池

当线程较少时我们可手动创建,但当所需线程非常多的时候,使用线程池将会事半功倍,Java通过Executors提供了四种线程池,并具有以下优势:

  1. 提高资源利用率
    线程池可以重复利用已经创建了的线程;
  2. 提高响应速度
    因为当线程池中的线程没有超过线程池的最大上限时,有的线程处于等待分配任务状态,当任务到来时,无需创建线程就能被执行;
  3. 具有可管理性
    线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。

案例二(单一线程池、固定线程池、缓存线程池)

//单一线程池(只有一个线程)
ExecutorService es = Executors.newSingleThreadExecutor();

//固定线程池(固定数量线程)
//ExecutorService es = Executors.newFixedThreadPool(2);

//缓存线程池(不定数量线程)
//ExecutorService es = Executors.newCachedThreadPool();

//线程任务
Runnable runnable = () -> {
    for (int i = 0; i < 3; i++) {
        System.out.println(Thread.currentThread().getName() + ":" + i);
    }
};
//提交任务至线程池处理
es.submit(runnable);
es.submit(runnable);
es.submit(runnable);
//关闭线程池
es.shutdown();
  • 单一线程池执行结果

请添加图片描述

  • 固定线程池执行结果

请添加图片描述

  • 缓存线程池执行结果

请添加图片描述

定时线程池

//Runtime.getRuntime().availableProcessors() 获取JVM的可用的处理器数量
ScheduledExecutorService ses = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());

Runnable task = () -> System.out.println("----------------------");

//scheduleAtFixedRate(定时任务, 延迟时长, 间隔时长, 时长单位);
ses.scheduleAtFixedRate(task, 3, 1, TimeUnit.SECONDS);

执行结果

延迟三秒之后,每隔一秒执行一次打印。

请添加图片描述

总结

重点

  1. 线程同步实现手段;
  2. Java锁机制相关概念;
  3. Java四种线程池的特点及使用方式。

难点

  1. 线程同步实现手段;
  2. Java锁机制相关概念。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值