Java多线程学习总结(四)

简介: Java多线程学习总结(四)

Java多线程学习总结(四)


1.线程间的通信


wait()和notifyAll()的应用实例:


package com.Zhongger.Day05;
/**
 * @Author Zhongger
 * @Description 有两个线程,可以操作初始值为0的一个变量,实现一个线程对该变量加1,一个对该变量减1,实现交替,来10轮
 * @Date 2020.2.24 生产者消费者模式
 */
public class TheadWaitNotifyTest {
    public static void main(String[] args) {
        AirConditioner airConditioner = new AirConditioner();
        new Thread(()->{
            for (int i = 1; i <= 10; i++) {
                try {
                    airConditioner.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 1; i <= 10; i++) {
                try {
                    airConditioner.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}
class AirConditioner
{
    private int number=0;
    public synchronized void increment() throws InterruptedException {
        //1.判断
        if (number!=0){
            this.wait();
        }
        //2.干活
        number++;
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        //3.通知
        this.notifyAll();
    }
    public synchronized void decrement() throws InterruptedException {
        if (number==0){
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        this.notifyAll();
    }
}

运行结果为:


20200224091402691.png


在这其中,用到了生产者消费者模式

生产者消费者模式说明:


生产者只在仓库未满时进行生产,仓库满时生产者进程被阻塞;

消费者只在仓库非空时进行消费,仓库为空时消费者进程被阻塞;

当生产者发现仓库满时会通知消费者消费;


A相当于一个生产者,B相当于一个消费者。

现在,如果多加入两个线程,看看程序的运行结果会怎么样?


20200224092851859.png


可以发现,A,C作为生产者,生产的物品多于了1。如果在真正的生产环境中出现这样的问题,会造成难以挽回的后果。所以一定要避免这种情况。


即在多线程交互中,必须要防止多线程的虚假唤醒。解决方案是:判断的地方使用whlie循环


  whlie(number!=0){
        this.wait();
    }


另外,使用ReentrantLock的方式也是可以的,在前两天的博客中有写到。


2.多线程间按顺序调用


package com.Zhongger.Day05;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * @Author Zhongger
 * @Description 多线程间按顺序调用,实现A->B->C,A打印5次,B打印10次,C打印15次
 * @Date 2020.2.24
 */
public class ThreadOrderAccess {
    public static void main(String[] args) {
        ShareResource shareResource = new ShareResource();
        new Thread(()->{
            for (int i = 0; i < 3; i++) {
                shareResource.print5();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 3; i++) {
                shareResource.print10();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 3; i++) {
                shareResource.print15();
            }
        },"C").start();
    }
}
//资源类
class ShareResource{
    private int number=1;//1:A 2:B 3:C
    private Lock lock = new ReentrantLock();
    private Condition condition1=lock.newCondition();
    private Condition condition2=lock.newCondition();
    private Condition condition3=lock.newCondition();
    public void print5(){
        lock.lock();
        try {
            //判断
            while (number!=1){
                condition1.await();
            }
            //干活
            for (int i = 1; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName()+"\t"+i);
            }
            //通知
            number=2;
            condition2.signal();//唤醒了B线程
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void print10(){
        lock.lock();
        try {
            //判断
            while (number!=2){
                condition2.await();
            }
            //干活
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName()+"\t"+i);
            }
            //通知
            number=3;
            condition3.signal();//唤醒了线程C
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void print15(){
        lock.lock();
        try {
            //判断
            while (number!=3){
                condition3.await();
            }
            //干活
            for (int i = 1; i <= 15; i++) {
                System.out.println(Thread.currentThread().getName()+"\t"+i);
            }
            //通知
            number=1;
            condition1.signal();//唤醒了线程A
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

使用多个Condition和Signal来精准地通知下一个线程工作。


3.多线程八锁


1.如果一个对象里面有多个synchronized方法,某一时刻内,只要有一个线程去调用其中的一个synchronized方法了,其他线程都只能等待,换句话说,某一时刻内,只能有一个线程去访问这些synchronized方法,synchronized锁的是一个当前对象this,被锁定后,其他的线程都不能进入到当前对象的其他synchronized方法。

2.类中如果有普通方法,和同步锁无关,其他线程可以访问。

3.若有两个资源类对象,锁是不同的,所以不同的线程可以访问不同的资源类对象中的synchronized方法,互不干扰。

4.所有的非静态同步方法都是同一把锁——类的实例。

5.synchronized实现同步的基础:Java中的每一个对象都可以作为锁,具体表现为以下三种形式:


对于普通的同步方法,锁是当前实例对象

对于静态同步方法,锁是当前类的Class对象

对于同步方法块,锁是synchronized括号里配置的对象


6.当一个线程试图访问同步代码块时,它必须得到锁,退出或抛出异常时必须释放锁。

7.所有的静态同步方法都是同一把锁——类的Class对象

8.静态同步方法和普通同步方法锁的是不同的对象,两个方法之间是不会有竞争条件的,但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们是同一个类的实例对象。


好了,今天的多线程学习就到这里,明天继续咯~

相关文章
|
12天前
|
Java
Java基础学习day08-作业
本作业涵盖Java中Lambda表达式的应用,包括Runnable与Comparator接口的简化实现、自定义函数式接口NumberProcessor进行加减乘及最大值操作,以及通过IntProcessor处理整数数组,实现遍历、平方和奇偶判断等功能,强化函数式编程实践。
39 5
|
12天前
|
Java API 容器
Java基础学习day08-2
本节讲解Java方法引用与常用API,包括静态、实例、特定类型方法及构造器引用的格式与使用场景,并结合代码示例深入解析。同时介绍String和ArrayList的核心方法及其实际应用。
62 1
|
13天前
|
Java 程序员
Java基础学习day08
本节讲解Java中的代码块(静态与实例)及其作用,深入介绍内部类(成员、静态、局部及匿名)的定义与使用,并引入函数式编程思想,重点阐述Lambda表达式及其在简化匿名内部类中的应用。
92 5
|
13天前
|
Java
Java基础学习day07-作业
本作业包含六个Java编程案例:1)动物类继承与多态;2)加油卡支付系统;3)员工管理类设计;4)学生信息统计接口;5)USB设备控制;6)家电智能控制。综合运用抽象类、接口、继承、多态等面向对象技术,强化Java基础编程能力。
117 3
|
13天前
|
Java
Java基础学习day06-作业
本内容为Java基础学习作业,涵盖两个案例:一是通过Card类及其子类GoldenCard、SilverCard实现加油卡系统,体现封装与继承;二是通过Shape类及子类Circle、Rectangle演示多态与方法重写,强化面向对象编程理解。
39 1
|
13天前
|
设计模式 存储 Java
Java基础学习day07
本节讲解Java中的final关键字、单例设计模式、枚举类、抽象类与接口。涵盖常量定义、单例写法(饿汉式/懒汉式)、枚举特点及应用场景,以及抽象类与接口的使用与区别,助力掌握核心面向对象编程思想。
74 1
|
18天前
|
Java
Java基础学习day05-作业
本文为Java基础学习第五天作业,通过五个案例练习类与对象的定义、构造方法、set/get方法及成员方法的应用。涵盖女友、学生、教师、手机和电影等类的设计与测试,强化面向对象编程基础。
49 2
|
18天前
|
安全 Java 编译器
Java基础学习day06
本节学习Java继承与多态核心概念:继承通过extends实现,子类可复用父类非私有成员,支持单继承与多层继承,遵循就近原则并可用super调用父类成员;方法重写需满足权限与返回值约束,常用于重写toString等。多态基于继承,实现父类引用指向子类对象,提升扩展性但无法直接访问子类独有方法,可通过instanceof安全类型转换。
83 2
|
19天前
|
存储 Java
Java基础学习day05
本节介绍Java面向对象编程基础,涵盖类与对象的概念、构造器、this关键字、封装、Javabean及static修饰符的使用。重点讲解如何通过类设计对象、实现数据封装与方法暴露,并理解静态成员的特点与应用场景。
57 2
|
19天前
|
Java
Java基础学习day04-作业
本作业包含8个Java编程案例,涵盖数组的定义与遍历、求和、最值计算、去极值求平均、元素倍增、二维数组行和计算及查找指定元素等内容,旨在巩固Java基础语法与数组操作技能。
170 1

热门文章

最新文章