【Java】高级篇2:多线程

一、相关概念

注意:

1、不同进程之间不共享内存

2、进程之间的数据交换和通信成本很高

线程调度:

单核CPU与多核CPU:

并行与并发:

二、创建和启动线程

1、概述

2、方式

2.1 方式一:继承Thread类

2.2 方式二:实现Runnable接口

class CountNumber implements Runnable{
    for(int i=0;i<10;i++){
        print(i);
    }
}

class Test{
    public static void main(String args[]){
        CountNumber c = new CountNumber();
        Thread t = new Thread(c);
        t.start();
    }
}

2.3 方式三:Callable(JDK5.0后新增)

2.4 线程池

代码示例:

三、Thread类常用结构

1、构造器

2、常用方法

四、多线程的生命周期

JDK1.5之前:

JDK1.5之后:

五、线程安全问题

引入

public class Test3 {
    public static void main(String[] args) {
        saleTikect s =new saleTikect();
        Thread t1=new Thread(s);
        Thread t2=new Thread(s);
        Thread t3=new Thread(s);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}
//实现Runnable接口

class saleTikect implements Runnable{
    int tikect=100;

    @Override
    public void run(){
        while (true) {
            if (tikect>0) {
              System.out.println(Thread.currentThread().getName()+"售票,票号:"+tikect); 
              tikect--; 
            }else{
                break;
            }
        }
    }
}

出现问题:

解决方式:线程的同步机制

方式1:同步代码块

格式:

synchornized(同步监视器){

    //需要被同步的代码

}

代码:

public class Test3RunnableSafe {
    public static void main(String[] args) {
        saleTikect s =new saleTikect();
        Thread t1=new Thread(s);
        Thread t2=new Thread(s);
        Thread t3=new Thread(s);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}
class saleTikect implements Runnable{
    static int tikect=100;
    Object obj=new Object();

    @Override
    public void run(){
        while (true) {
            try {
                Thread.sleep(10);
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }

            synchronized(obj){
                //obj唯一

                if (tikect>0) {
                    try {
                        Thread.sleep(5);
                    } catch (Exception e) {
                        // TODO: handle exception
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"售票,票号:"+tikect); 
                    tikect--; 
                    
                }else{
                    break;
                }
            }

        }
    }
}

说明:

>需要被同步的代码,即为操作共享数据的代码

>共享数据,即为多个线程需要操作的数据

>需要被同步的代码,在被synchornized包裹以后,

就使得一个线程在操作这些代码的过程中,其他进必须等待

>同步监视器,俗称锁。哪个线程获取了锁,哪个线程就能执行需要被同步到代码

>同步监视器,可以使用任何一个类的对象充当。

>多个线程必须共用同一个同步监视器

注意:在实现Runnable接口方式中,同步监视器可以考虑用this

在jichengThread类方式中,同步监视器慎用this,可以可以考虑使用:当前类.class

方式2:同步方法

说明:

>如果操作共享数据的代码完整的声明在一个方法中,将此方法声明为同步方法即可。

>非静态同步方法,默认同步监视器是this;静态同步方法,默认同步监视器是当前类本身。

代码:

public class Test3 {
    public static void main(String[] args) {
        saleTikect s = new saleTikect();
        Thread t1 = new Thread(s);
        Thread t2 = new Thread(s);
        Thread t3 = new Thread(s);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

class saleTikect implements Runnable {
    int tikect = 100;
    boolean isFlag=true;

    @Override
    public void run() {
        while (isFlag) {
            show();
        }
    }

    /**
     * 关键代码完整声明在show()中
     */
    public synchronized void show() {
        if (tikect > 0) {
            try {
                Thread.sleep(10);
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "售票,票号:" + tikect);
            tikect--;
        }else{
            isFlag=false;
        }
    }
}

正确:

解决继承类出现的问题。

问题代码:

public class Test3two {
    public static void main(String[] args) {
        Window window1 = new Window();
        Window window2 = new Window();
        Window window3 = new Window();

        window1.setName("窗口1");
        window2.setName("窗口2");
        window3.setName("窗口3");

        window1.start();
        window2.start();
        window3.start();
    }
}

class Window extends Thread{
    static int tikect=100;
    @Override
    public void run(){
        while (true) {
            if (tikect>0) {
              System.out.println(Thread.currentThread().getName()+"售票,票号:"+tikect); 
              tikect--; 
            }else{
                break;
            }
        }
    }
}

出现重票:

解决代码:

public class Test3two {
    public static void main(String[] args) {
        Window window1 = new Window();
        Window window2 = new Window();
        Window window3 = new Window();

        window1.setName("窗口1");
        window2.setName("窗口2");
        window3.setName("窗口3");

        window1.start();
        window2.start();
        window3.start();
    }
}

class Window extends Thread{
    static int tikect=100;
    static boolean isFlag=true;
    @Override
    public void run(){
        while (isFlag) {
            show();
        }
    }

    /*public synchronized void show(){//此时同步监视器this:window1,window2,window3。
    */
        public static synchronized void show(){
            //isFlag也要加上static
            //此时同步监视器:当前类,即为window1.class,这是唯一的
        if (tikect>0) {
            System.out.println(Thread.currentThread().getName()+"售票,票号:"+tikect); 
            tikect--; 
          }else{
              isFlag=false;
          }
    }
}

运行结果:

六、懒汉式的线程安全问题、死锁、Lock的使用

1、懒汉式线程安全问题

案例代码:

public class Test4 {
    static Bank b1 = null;
    static Bank b2 = null;
    public static void main(String[] args) {
        Thread t1 = new Thread(){
            @Override
            public void run(){
                b1=Bank.getInstance();
            }
        };
        
        Thread t2 = new Thread(){
            @Override
            public void run(){
                b2=Bank.getInstance();
            }
        };

        t1.start();
        t2.start();

        try {
            t1.join();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        try {
            t1.join();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        System.out.println(b1);
        System.out.println(b2);
        System.out.println(b1==b2);
    }
}

class Bank{
    
    private Bank(){}
    private static Bank instance=null;

    public static Bank getInstance(){
        if(instance==null){

            try {
                Thread.sleep(100);
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
            instance=new Bank();
        }
        return instance;
    }
}

运行结果:

理想结果两个地址应该是一样的,但是出现了线程安全问题。

方式一:

public class Test4 {
    static Bank b1 = null;
    static Bank b2 = null;
    public static void main(String[] args) {
        Thread t1 = new Thread(){
            @Override
            public void run(){
                b1=Bank.getInstance();
            }
        };
        
        Thread t2 = new Thread(){
            @Override
            public void run(){
                b2=Bank.getInstance();
            }
        };

        t1.start();
        t2.start();

        try {
            t1.join();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        try {
            t1.join();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        System.out.println(b1);
        System.out.println(b2);
        System.out.println(b1==b2);
    }
}

class Bank{
    
    private Bank(){}
    private static Bank instance=null;

    //方式一:同步监视器
    public synchronized static Bank getInstance(){
        //同步监视器为当前类,是唯一的
        if(instance==null){

            try {
                Thread.sleep(100);
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
            instance=new Bank();
        }
        return instance;
    }
}

运行结果:

方式二:

//方式二:同步监视器(同步代码块)
    public static Bank getInstance(){
        synchronized(Bank.class){
            if(instance==null){

                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    // TODO: handle exception
                    e.printStackTrace();
                }
                instance=new Bank();
            }
        }
        return instance;
    }

方式三:

    //方式三:多套一层判断
    //当两个线程同时进入第一层判断instance为null时,
    //某一线程进入关键代码创建instance(synchronized只允许一个线程执行
    //另一线程进入等待,当某一线程完成创建,释放同步监视器
    //另一线程在第二层判断时instance!=null,return instance
    //此时两个线程得到的instance是相同的
    //若后续线程进入,在第一层判断时instance不为null,直接返回相同地址
//方式三相较于前两种效率更高,但是存在指令重排问题
//(还没有初始化完成就拿着对象出去了,创建对象的准备工作是很多的,虽然这里代码只有一行)
//可加关键字volatite,将instance声明为volatite
    
    private static volatile Bank instance=null;
    public static Bank getInstance(){
        if(instance==null){
            synchronized(Bank.class){
                if(instance==null){
    
                    try {
                        Thread.sleep(100);
                    } catch (Exception e) {
                        // TODO: handle exception
                        e.printStackTrace();
                    }
                    instance=new Bank();
                }
            }
        }
        return instance;
    }

2、死锁问题

举例:

public class DeadLock {
    public static void main(String[] args) {
        StringBuilder s1 = new StringBuilder();
        StringBuilder s2 = new StringBuilder();

        new Thread(){
            @Override
            public void run(){
                synchronized(s1){
                    
                    s1.append("A");
                    s2.append("1");
    
                    try {
                        Thread.sleep(100);
                    } catch (Exception e) {
                        e.printStackTrace();
                        // TODO: handle exception
                    }
    
                    synchronized(s2){
                        s1.append("B");
                        s2.append("2");
    
                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }.start();

        new Thread(){
            @Override
            public void run(){
                synchronized(s2){
                    
                    s1.append("C");
                    s2.append("3");
    
                    try {
                        Thread.sleep(100);
                    } catch (Exception e) {
                        e.printStackTrace();
                        // TODO: handle exception
                    }
    
                    synchronized(s1){
                        s1.append("D");
                        s2.append("4");
    
                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }.start();

    }
}

出现死锁:

3、Lock

针对购票问题:

代码:

import java.util.concurrent.locks.ReentrantLock;

public class LockTest {
    public static void main(String[] args) {
        Window window1 = new Window();
        Window window2 = new Window();
        Window window3 = new Window();

        window1.setName("窗口1");
        window2.setName("窗口2");
        window3.setName("窗口3");

        window1.start();
        window2.start();
        window3.start();
    }
    
}

class Window extends Thread{
    static int tikect=100;

    //创建Lock的实例,确保多个线程共用同一个Lock实例,需要考虑将Lock声明为static final
    private static final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run(){
        while (true) {

            //2.执行lock()方法,锁定对共享资源的调用
            lock.lock();

            try{

                if (tikect>0) {
    
                    try {
                        Thread.sleep(100);
                    } catch (Exception e) {
                        e.printStackTrace();
                        // TODO: handle exception
                    }
    
                    System.out.println(Thread.currentThread().getName()+"售票,票号:"+tikect); 
                    tikect--; 
                }else{
                    break;
                }

            }finally{

                //3.释放对同步共享数据的锁定
                lock.unlock();

            }


        }
    }

}

运行结果:

 

七、线程的通信(生产者消费者问题)

等待唤醒机制:

注意点:

案例:生产者&消费者

/*
 * 1、多线程:生产者、消费者
 * 2、共享数据:产品
 * 3、需要处理线程安全问题:使用同步机制
 * 4、存在线程通信
 */
public class PCTest {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();
        Producer producer = new Producer(clerk);
        Consumer consumer = new Consumer(clerk);

        producer.setName("生产者1");
        consumer.setName("消费者1");

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

class Producer extends Thread{

    private Clerk clerk;

    public Producer(Clerk clerk){
        this.clerk=clerk;
    }

    @Override
    public void run(){

        while (true) {
            System.out.println("生产产品中......");

            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
                // TODO: handle exception
            }

            clerk.addProduct();
        }

    }
}

class Consumer extends Thread{

    private Clerk clerk;

    public Consumer(Clerk clerk){
        this.clerk=clerk;
    }

    @Override
    public void run(){
        while (true) {
            System.out.println("消费产品......");

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
                // TODO: handle exception
            }

            clerk.minusProduct();
        }

    }
}

class Clerk{
    private int productNumber=0;

    public synchronized void addProduct(){
        if(productNumber>=20){
            
            //等待
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
                // TODO: handle exception
            }

        }
        
        productNumber++;
        System.out.println(Thread.currentThread().getName()+"生产了第"+productNumber+"个产品");
    }

    public synchronized void minusProduct(){

        if(productNumber<=0){
            
            //等待
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
                // TODO: handle exception
            }
    
        }
        
        System.out.println(Thread.currentThread().getName()+"消费了第"+productNumber+"个产品");
        productNumber--;

    }
}

wait()和sleep()的区别:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/484208.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Fantasy RPG Spell Pack 2

介绍奇幻角色扮演游戏魔法包VFX,这是为您的Unity奇幻角色扮演游戏提供的终极视觉效果解决方案!这个包包含30个独特的VFX,将为您的法术和能力带来生命,让您的玩家沉浸在魔法和奇迹的世界中。 从令人惊叹的彩虹盾和闪电到旋转门户和召唤圈,这个包有你需要的一切来创造一个真…

GETSHELL方法总结上

渗透的总步骤 1.信息收集找到弱漏洞 2.漏洞挖掘 漏洞验证 3.有一定权限 getshell 4.提权后---渗透 5.内网渗透】 前后台拿shell方法汇总 接下来我们实操一波dedecms也就是织梦cms 如果你们的靶场是空白的 可能是php版本 我们修改为5.2 可能是源码问题 我们不要急着上传…

ChatGPT论文指南|揭秘8大ChatGPT提示词研究技巧提升写作效率【建议收藏】

点击下方▼▼▼▼链接直达AIPaperPass &#xff01; AIPaperPass - AI论文写作指导平台 公众号原文▼▼▼▼&#xff1a; ChatGPT论文指南|揭秘8大ChatGPT提示词研究技巧提升写作效率【建议收藏】 目录 1.写作方法 2.方法设计 3.研究结果 4.讨论写作 5.总结结论 6.书…

MySQL--select count(*)、count(1)、count(列名) 的区别你知道吗?

MySQL select count(*)、count(1)、count(列名) 的区别&#xff1f; 这里我们先给出正确结论&#xff1a; count(*)&#xff0c;包含了所有的列&#xff0c;会计算所有的行数&#xff0c;在统计结果时候&#xff0c;不会忽略列值为空的情况。count(1)&#xff0c;忽略所有的列…

Axure RP 9 for mac中文版密钥激活版下载

Axure RP 9是一款专业的快速原型设计工具&#xff0c;它可以帮助产品设计师、交互设计师和用户体验设计师等创建高保真度、交互性强的原型&#xff0c;以便在产品开发之前进行测试和用户验证。 软件下载&#xff1a;Axure RP 9 for mac中文版密钥激活版下载 该工具具有丰富的功…

2023蓝桥杯C/C++A组省赛 B题: 有奖问答|DFS搜索 、线性dp

题目链接&#xff1a; 1.有奖问答 - 蓝桥云课 (lanqiao.cn) 说明&#xff1a; DFS做法&#xff1a; 因为是填空题&#xff0c;不用考虑超时&#xff0c;首先先考虑暴力做法DFS来做&#xff0c;根据题意&#xff0c;30道题&#xff0c;有一个答题的先后顺序&#xff0c;上一…

【算法篇】逐步理解动态规划1(斐波那契数列模型)

目录 斐波那契数列模型 1. 第N个泰波那契数 2.使用最小花费爬楼梯 3.解码方法 学过算法的应该知道&#xff0c;动态规划一直都是一个非常难的模块&#xff0c;无论是状态转移方程的定义还是dp表的填表&#xff0c;都非常难找到思路。在这个算法的支线专题中我会结合很多力…

Java学习笔记 | Java基础语法 | 03 | 流程控制语句

文章目录 0 前言1.流程控制语句1.1 流程控制语句分类1.2 顺序结构 2.判断语句2.1 if语句1. if语句格式1练习1&#xff1a;老丈人选女婿练习2&#xff1a;考试奖励 2. if语句格式2练习1&#xff1a;吃饭练习2&#xff1a;影院选座 3. if语句格式3练习1&#xff1a;考试奖励 2.2 …

Vue使用font-face自定义字体详解

目录 1 介绍2 使用2.1 语法2.2 属性说明2.3 Vue使用案例2.3.1 全局定义字体2.3.2 在页面使用 3 注意事项 1 介绍 font-face 是 CSS 中的一个规则&#xff0c;它允许你加载服务器上的字体文件&#xff08;远程或者本地&#xff09;&#xff0c;并在网页中使用这些字体。这样&am…

使用 STL 容器发生异常的常见原因分析与总结

目录 1、概述 2、使用STL列表中的元素越界 3、遍历STL列表删除元素时对迭代器自加处理有问题引发越界 4、更隐蔽的遍历STL列表删除元素时引发越界的场景 5、多线程同时操作STL列表时没有加锁导致冲突 6、对包含STL列表对象的结构体进行memset操作导致STL列表对象内存出异…

大学教材《C语言程序设计》(浙大版)课后习题解析 | 第一、二章

概述 本文主要提供《C语言程序设计》(浙大版) 第一、二章课后习题解析&#xff0c;以方便同学们完成题目后作为参考对照。后续将写出三、四章节课后习题解析&#xff0c;如想了解更多&#xff0c;请持续关注该专栏。 专栏直达链接&#xff1a;《C语言程序设计》(浙大版)_孟俊宇…

Hive SQL必刷练习题:复购率问题(*****)

是说这个数据表中&#xff0c;找到最后一天 &#xff0c;也就是今天的日期&#xff0c;max(date) over()S today 【借助开窗函数】 截至最后一天位置&#xff0c;也就是“今天“&#xff0c;表中的最新的一天 去看90天内“某商品复购率 近90天内购买它至少两次的人数 购买它…

c++常考基础知识(2)

二.c关键字 关键字汇总 c中共有63个关键字&#xff0c;其中包括int&#xff0c;char&#xff0c;double等类型关键字&#xff0c;if&#xff0c;else&#xff0c;while&#xff0c;do&#xff0c;等语法关键字&#xff0c;还有sizeof等函数关键字。 三.数据结构 1.数组&#x…

常见的OOM 问题的 6 种场景

今天跟大家一起聊聊线上服务出现 OOM 问题的 6 种场景,希望对你会有所帮助。 一、堆内存 OOM 堆内存 OOM 是最常见的 OOM 了。 出现堆内存 OOM 问题的异常信息如下: java.lang.OutOfMemoryError: Java heap space此 OOM 是由于 JVM 中 heap 的最大值,已经不能满足需求了…

Git的原理和使用(四)

目录 远程操作 理解分布式版本控制系统 远程仓库 新建远程仓库 克隆远程仓库 向远程仓库推送 拉取远程仓库 配置Git 忽略特殊文件 为命令配置别名 标签管理 理解标签 创建标签 操作标签 远程操作 理解分布式版本控制系统 1、每个人的电脑上都是一个完整的版本库…

批量重命名文件名,批量管理文件,复制后轻松删除原文件

在现代工作中&#xff0c;我们经常需要处理大量的文件&#xff0c;无论是工作文档、图片还是视频资料。对于很多人来说&#xff0c;文件管理是一项繁琐且耗时的任务。不过&#xff0c;现在有一种高效的文件管理方法&#xff0c;可以帮助你轻松复制文件后删除原文件夹&#xff0…

2024.03.24 exam

2024.03.24 exam 据说是事业单位考试例题&#xff0c;娱乐一下脑子

zabbix安装及使用(错误及解决方案)

安装zabbix 常见错误&#xff1a; Zabbix下载错误 6.0与5.0版本冲突 解决方法 yum -y install zabbix-server-mysql zabbix-web-mysql zabbix-get --skip-broken zabbix6.0-web 自己有数据库&#xff0c;使用以下命令 pid找不到 /var/log/zabbix/zabbix_server.log 错误&a…

Docker Command

小试牛刀 # 查看docker版本 docker -v docker --version # 查看帮助 docker --help # 永远的Hello World docker run hello-world镜像操作 查看本地已有的镜像 docker images -a :列出本地所有的镜像&#xff08;含中间映像层&#xff09; -q :只显示镜像ID --digests :显示…

尝试Docker Dev Environments

无法从本地目录创建容器环境 创建的容器环境无法在VS Code打开 从官方仓库打开 结果vscode报错。fine&#xff0c;告辞。老老实实用本地环境开发。