如何创建线程,多线程下又如何上锁保护公共资源?

2023-10-26

目录

一、创建线程几种方法:

1、继承thread类,重写run方法     2、实现runnable接口,重写run方法

3、使用匿名类 或 lamda表达式 让代码更简洁

4、Callable 接口

5、使用线程池创建线程

二、多线程下,需要上锁来保护公用资源(synchronized;lock)


一、创建线程几种方法:

1、继承thread类,重写run方法     2、实现runnable接口,重写run方法

//继承Thread类
public class MyThread extends Thread{
  public void run(){
  //重写run方法
  } 
}
//实现Runnable接口
public class MyThread2 implements Runnable {
  public void run(){
  //重写run方法
  }
}

public class Main {
  public static void main(String[] args){
    new MyThread().start();//创建并启动线程

        Thread t2 = new Thread(MyThread2);
        t2.setName("线程2");
        t2.start();
new Thread(MyThread2).start();
new Thread(MyThread2,"线程2").start();
  }
}

Java 是单继承编程语言,继承是十分宝贵的,所以一般不使用这种方法:继承Thread类 

3、使用匿名类 lamda表达式 让代码更简洁

//使用匿名类
new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    for (int i = 0; i < 100; i++) {
                        share.incr();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();

//lamda表达式
new Thread(() -> {
	System.out.println(Thread.currentThread().getName() + "\t上完自习,离开教室");
}, "AA").start();
 

4、Callable 接口

Runnable 缺少的一项功能是,当线程终止时(即 run()完成时),我们无法使线程返回结果。为了支持此功能,Java 中提供了 Callable 接口Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大。

需要使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值。

FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了Future和Runnable接口。

package com.atguigu.java2;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

//1.创建一个实现Callable的实现类
class NumThread implements Callable {
    //2.实现call方法,将此线程需要执行的操作声明在call()中
    @Override
    public Object call() throws Exception {
        int sum = 0;
        //把100以内的偶数相加
        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }
}

public class ThreadNew {
    public static void main(String[] args) {    
        //3.创建Callable接口实现类的对象
        NumThread numThread = new NumThread();

        //4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
        FutureTask futureTask = new FutureTask(numThread);

        //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
        new Thread(futureTask).start();

        try {
            //6.获取Callable中call方法的返回值
            //get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
            Object sum = futureTask.get();
            System.out.println("总和为:" + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

}


//————————————————————————————————————————————————————————
public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"执行Runnable");
        }).start();
        
        FutureTask<String> task = new FutureTask<>(() -> {
            System.out.println(Thread.currentThread().getName() + "使用Callable接口");
            return "Callable接口返回值";
        });
        
        new Thread(task).start();
        System.out.println("Callable返回值:" + task.get());
    }
}

5、使用线程池创建线程

二、多线程下,需要上保护公用资源(synchronized;lock

  1. synchronized是java关键字,内置,而lock不是内置,是一个类,可以实现同步访问且比 synchronized中的方法更加丰富,并且可以支持多个相关联的对象 Condition
  2. synchronized不会手动释放锁,而lock需手动释放锁(不解锁会出现死锁,需要在 finally 块中释放锁)
  3. lock等待锁的线程会相应中断,而synchronized不会相应,只会一直等待
  4. 通 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到
  5. Lock 可以提高多个线程进行读操作的效率(当多个线程竞争的时候)锁会出现死锁,需要在 finally 块中释放锁)

关键字 synchronized 与 wait()/notify() 这两个方法一起使用可以实现等待/通知模式
用 notify()通知时,JVM 会随机唤醒某个等待的线程

public synchronized void incr() throws InterruptedException {
        // 操作:判断、干活、通知
        if (number != 0) {
            this.wait();	
        }
        number++;
        System.out.print(Thread.currentThread().getName()+"::"+number);
        // 唤醒其他线程,注意这里的通知是随机的,就是只能通知全部
        this.notifyAll();
    }

Lock 实现可重入锁

使用 Condition 类可以进行选择性通知: Condition有以下两个方法await()和signal()

// 创建可重入锁
private final ReentrantLock lock = new ReentrantLock();
//Lock 接口中的 newContition() 方法返回 Condition 对象,Condition 类也可以实现等待/通知模式
private Condition condition = lock.newCondition();

try {
    //上锁
    lock.lock();
//功能操作:————————————————————————————————————————
    //使用 Condition 类可以进行选择性通知: Condition有以下两个方法await()和signal()
    //判断循环await()、干活、通知signal()
        // 判断
            while (number != 0) {
                condition.await();
            }
            // 干活
            number++;
            System.out.print(Thread.currentThread().getName() + "::" + number + "--->");
            // 通知
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
//——————————————————————————————————————————
}finally {
    //解锁
    lock.unlock();
}

案列:启动三个线程,按照如下要求: AA打印5此,BB打印10次,CC打印15次,一共进行10轮

由于需要选择性通知唤醒进程,因此使用lock

class Share{
    private int flag = 1;

    private Lock lock = new ReentrantLock();
    // 创建三个Comdition对象,为了定向唤醒相乘
    Condition c1 = lock.newCondition();
    Condition c2 = lock.newCondition();
    Condition c3 = lock.newCondition();

    public void Aprint(int loop) {
        //上锁
        lock.lock();
        try{
            // 判断
            while(flag!=1) {
                c1.await();
            }
            // 干活
            for (int i = 1; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName() + " ::本次第" + i + "次打印,是第" + loop+ "次循环");
            }
            flag = 2; //修改标志位,定向唤醒 线程b
            // 唤醒
            c2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 解锁
            lock.unlock();
        }
    }
    public void Bprint(int loop) {
        //上锁
        lock.lock();
        try{
            // 判断
            while(flag!=2) {
                c2.await();
            }
            // 干活
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName() + " ::本次第" + i + "次打印,是第" + loop+ "次循环");
            }
            flag = 3; //修改标志位,定向唤醒 线程b
            // 唤醒
            c3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 解锁
            lock.unlock();
        }
    }

    public void Cprint(int loop) {
        //上锁
        lock.lock();
        try{
            // 判断,这个唤醒最好在while里头执行,防止出现虚假唤醒
            //由于 wait() 方法使线程在哪里睡就在哪里醒,所以接下来C在执行时不会再通过 if 判断
            while(flag!=3) {
                c3.await();
            }
            // 干活
            for (int i = 1; i <= 15; i++) {
                System.out.println(Thread.currentThread().getName() + " ::本次第" + i + "次打印,是第" + loop+ "次循环");
            }
            flag = 1; //修改标志位,定向唤醒 线程b
            // 唤醒
            c1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 解锁
            lock.unlock();
        }
    }
}

public class CustomInterThreadCommunication {
    public static void main(String[] args) {
        Share share = new Share();
//在主函数里创建线程:
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 10; i++) {
                    share.Aprint(i);
                }

            }
        },"A").start();
        
// new Thread(r, "A").start();
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 10; i++) {
                    share.Bprint(i);
                }
            }
        },"B").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 100; i++) {
                    share.Cprint(i);
                }

            }
        },"C").start();
    }
}

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何创建线程,多线程下又如何上锁保护公共资源? 的相关文章

随机推荐

  • Linux字符设备驱动file_operations详解

    struct file operations struct file operations在Fs h这个文件里面被定义的 如下所示 struct file operations struct module owner 拥有该结构的模块的指针
  • Js中的枚举

    原文见 Js中的枚举 在JavaScript目前的版本中 没有枚举这个概念 当然 ECMA 262第三版中已经将enum作为关键字保留 然而 如同JavaScript中没有class一样 但我们仍然可以通过间接的方式 JSON来实现它 如下
  • 合并Dwg文件

    尝试用 NET平台C 写个打开其他DWG文件并读取所有内容 打包成块后再插入当前文档的程序 1 打开其他指定文件没问题 2 打包成块程序这里出了问题3 但若是直接在当前文档创建直线 再运行打包成块程序就没问题 不知道是何原因 可有大神出来指
  • 快手磁力金牛和小店通的区别

    手磁力金牛平台是全新电商营销平台磁力金牛 磁力金牛平台将集合快手粉条 小店通 真正打通公域私域流量 实现全站数字化营销 核心提示 磁力金牛平台整合了小店通 粉条的投放链路 公域私域流量融合 磁力金牛平台整合了小店通 粉条的投放链路 公域私域
  • win7 svn服务器搭建过程

    svn简介 https baike baidu com item subversion 7818587 fr aladdin SVN服务端分为 Subversion和VisualSVN Server 这里 我选择了VisualSVN Ser
  • Java笔记:UDP基础使用与广播

    文章目录 目的 作为客户端使用 作为服务器使用 广播 广播地址获取 广播功能演示 总结 目的 UDP是比较基础常用的网络通讯方式 这篇文章将介绍Java中UDP基础使用的一些内容 本文中使用 Packet Sender 工具进行测试 其官网
  • Java使用DES加密解密

    一 DES算法 DES Data Encryption Standard 数据加密标准 它是由IBM公司研制的一种对称密码算法 DES是一个分组加密算法 典型的DES以64位分组对数据加密 加密和解密用的是用一个算法 总长度64位 8字节
  • Spring:基于xml文件的控制反转(ioc)

    1 环境搭建 导入spring使用最基本的坐标
  • VMware Workstation 不可恢复错误: (vmx)

    errors VMware Workstation 不可恢复错误 vmx Exception 0xc0000006 disk error while paging has occurred 日志文件位于 K vmware centos vm
  • 运用决策表设计测试用例

    逻辑关系 逻辑关系 logic relationship 即 依赖关系 在项目管理中 指表示两个活动 前导活动和后续活动 中一个活动的变更将会影响到另一个活动的关系 强制依赖关系 所做工作中固有的依赖关系 可自由处理的依赖关系 由项目队伍确
  • MyBatis:尝试解决Spring Boot集成MyBatis 懒加载时序列化失败的三种方法以及原因FAIL_ON_EMPTY_BEANS

    MyBatis 解决No serializer found for class org apache ibatis executor loader javassist JavassistProxyFactory EnhancedResult
  • python3 Flask 简单入门(MVC模板类)

    跟上一篇文章一样的内容 Flask默认支持的模板是jinja2 jinja2简单实用 1 在Jinja2模板中 我们用 name 表示一个需要替换的变量 很多时候 还需要循环 条件判断等指令语句 在Jinja2中 用 表示指令 2 循环输出
  • win10 装黑苹果 完整教程

    一 材料准备 1 虚拟机软件VMware 2 适用于Windows版本的VMware解锁安装Mac OS的补丁 3 Mac OS X 10 10的黑苹果镜像 以上材料我都为你贴心地准备齐了 在我的云盘获取 链接 https pan baid
  • VUE3+Element-Plus form表单封装

    VUE3 Element Plus form表单封装 新建form组件页面 创建index vue 新建form组件页面 在components中创建新组件 将需要的form表单中常用的UI组件引入 vue3创建组件和vue2中多少有点区别
  • 大学《数据库原理与技术》复习题(二)

    数据库复习题 一 选择题 1 B 是按照一定的数据模型组织的 长期存储在计算机内 可为多个用户共享的数据的集合 A 数据库系统 B 数据库 C 关系数据库 D 数据库管理系统 2 数据库系统的基础是 A 数据结构 B 数据库管理系统 C 操
  • LVGL V8

    本文适用于LVGL V8版本 LVGL simulator vs2019 官方工程 lv sim visual studio 使用注意事项 1 将官方工程从github上下载下来 最好使用git 将整个工程clone下来 因为工程内部有依赖
  • c++坑人

    大家好 我是LCR 今天为大家带来的是c 中的弹窗病毒 当然你也可以把它理解为坑人代码 如果喜欢这篇文章 可以给我点一个赞吗 代码解释 system是c语言库里面自带的一个函数 start的原本意思为 跳转 后面本应接网址 当你的后面为空时
  • 多功能翻译工具:全球翻译、润色和摘要生成

    openai translator openai translator Stars 18 1k License AGPL 3 0 这个项目是一个多功能翻译工具 由 OpenAI 提供支持 可以进行全球单词翻译 单词润色和摘要生成等操作 提供
  • python项目导出依赖包requirements.txt文件

    只导出当前项目依赖包 注意 使用 pip freeze gt requirements txt 会导出大量无用的文件 包括很多个包信息 其实这里是把你当前 python 环境的所有包的相关信息导出来了 如果我们只需导出当前项目所需的依赖包
  • 如何创建线程,多线程下又如何上锁保护公共资源?

    目录 一 创建线程几种方法 1 继承thread类 重写run方法 2 实现runnable接口 重写run方法 3 使用匿名类 或 lamda表达式 让代码更简洁 4 Callable 接口 5 使用线程池创建线程 二 多线程下 需要上锁