广告
返回顶部
首页 > 资讯 > 后端开发 > Python >java多线程创建及线程安全详解
  • 827
分享到

java多线程创建及线程安全详解

2024-04-02 19:04:59 827人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

什么是线程 线程被称为轻量级进程,是程序执行的最小单位,它是指在程序执行过程中,能够执行代码的一个执行单位。每个程序程序都至少有一个线程,也即是程序本身。 线程

什么是线程

  • 线程被称为轻量级进程,是程序执行的最小单位,它是指在程序执行过程中,能够执行代码的一个执行单位。每个程序程序都至少有一个线程,也即是程序本身。

线程的状态

  • 新建(New):创建后尚未启动的线程处于这种状态
  • 运行(Runable):Runable包括了操作系统线程状态的Running和Ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待着CPU为它分配执行时间。
  • 等待(Wating):处于这种状态的线程不会被分配CPU执行时间。等待状态又分为无限期等待和有限期等待,处于无限期等待的线程需要被其他线程显示地唤醒,没有设置Timeout参数的Object.wait()、没有设置Timeout参数的Thread.join()方法都会使线程进入无限期等待状态;有限期等待状态无须等待被其他线程显示地唤醒,在一定时间之后它们会由系统自动唤醒,Thread.sleep()、设置了Timeout参数的Object.wait()、设置了Timeout参数的Thread.join()方法都会使线程进入有限期等待状态。
  • 阻塞(Blocked):线程被阻塞了,“阻塞状态”与”等待状态“的区别是:”阻塞状态“在等待着获取到一个排他,这个时间将在另外一个线程放弃这个锁的时候发生;而”等待状态“则是在等待一段时间或者唤醒动作的发生。在程序等待进入同步区域的时候,线程将进入这种状态。
  • 结束(Terminated):已终止线程的线程状态,线程已经结束执行。

线程的生命周期 

多线程创建方法

继承Thread



class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println(currentThread().getName()+"运行了");
    }
}
class Test{
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        System.out.println(Thread.currentThread().getName()+":运行了");
        myThread.start();
    }
}

实现Runable接口创建多线程



class MThread implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i<100;i++){
            if (i%2!=0){
                System.out.println(i);
            }
        }
    }
}
public class ThreadTest1 {
    public static void main(String[] args) {
        //3.创建实现类的对象
        MThread mThread = new MThread();
        //4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
        Thread thread = new Thread(mThread);
        thread.start();
    }
}

Thread和Runable创建多线程对比

开发中:优先使用Runable
1.实现的方式没有类的单继承的局限性。
2.实现的方式跟适合处理多个线程有共享数据的情况。
联系:Thread类中也实现了Runable,两种方式都需要重写run()。

实现Callable接口


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


class MCallable implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        int sum=0;
        for(int i=0;i<100;i++){
            sum+=i;
        }
        return sum;
    }
}
public class CallableTest {
    public static void main(String[] args) {
        //执行Callable 方式,需要FutureTask 实现实现,用于接收运算结果
        FutureTask<Integer> integerFutureTask = new FutureTask<Integer>(new MCallable());
       
        new Thread(integerFutureTask).start();
        //接受线程运算后的结果
        Integer integer = null;
        try {
            integer = integerFutureTask.get();
            System.out.println(integer);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}

与Runable相比,Callable功能更强大

相比run()方法可以有返回值
方法可以抛出异常
支持泛型的返回值
需要借助FutureTask类,比如获取返回结果

使用线程池进行创建

线程池创建的好处

提高响应速度(减少了创建新线程的时间)

降低资源消耗(重复利用线程池中线程,不需要每次都创建)

便于线程管理:

  • corePoolSize:核心线程池的大小
  • maximumPoolSize:最大线程数
  • keepAliveTime:线程没有任务时最多保持多长时间后悔中止

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


class Thread1 implements Runnable{

    @Override
    public void run() {
        for (int i=1;i<30;i++){
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}
public class ThreadPool {
    public static void main(String[] args) {
        //创建线程池
        ExecutorService executorService= Executors.newFixedThreadPool(10);
        Thread1 threadPool = new Thread1();
        for (int i=0;i<5;i++){
            //为线程池分配任务
            executorService.submit(threadPool);
        }
        //关闭线程池
        executorService.shutdown();
    }
}

Thread中的常用方法

  • start():启动当前线程;调用当前线程的run();
  • run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中。
  • currentThread():静态方法,返回当前代码的线程。
  • getName():获取当前线程的名字。
  • setName():设置当前线程的名字。
  • yield():释放当前cpu的执行权,切换线程执行。
  • join():在线程a中调用线程b的join(),此时线程a会进入阻塞状态,知道线程b完全执行完毕,线程a 才结束阻塞状态。
  • stop():强制线程生命期结束。(过时了,不建议使用)
  • isAlive():判断线程是否还活着。
  • sleep(long millitime):让当前线程睡眠指定的事milltime毫秒。在指定的millitime毫秒时间内,当前线程是阻塞状态。

线程的优先级

线程的优先级等级

  • MAX_PRIORITY:10
  • MIN_PRIORITY:1
  • NORM_PRIORITY:5

涉及的方法

  • getPriority():返回线程的优先值
  • setPriority(int newPriority):改变线程的优先级

说明

  • 线程创建时继承父线程的优先级
  • 低优先级知识获得调度的概率低,并非一定是在高优先级线程之后才被调用

线程的同步

问题

多线程卖票

基于实现Runable的方式实现多线程买票


package demo2;


class Thread2 implements Runnable{

    private  int ticket=100;
    @Override
    public void run() {
        while (true){
            if (ticket>0) {
                System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket);
                ticket--;
            }else {
                break;
            }
        }
    }
}
public class Test1 {
    public static void main(String[] args) {
        Thread2 thread2 = new Thread2();
        Thread t1 = new Thread(thread2);
        Thread t2 = new Thread(thread2);
        Thread t3 = new Thread(thread2);
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}

实现结果,存在重复的票

实现结果

如果在买票方法中加入sleep函数


 public void run() {
        while (true){
            if (ticket>0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket);
                ticket--;
            }else {
                break;
            }
        }
    }

则运行结果可能会出现-1,表示也是不正常的

出现-1的结果

理想情况

卖票理想状态

极端情况

极端情况

在java中,我们通过同步机制,来解决线程的安全问题。

同步代码块


synchronized(同步监视器){
	//需要被同步的代码
}

说明

  • 操作共享数据的代码就是需要被同步的代码。
  • 共享数据:多个线程共同操作的变量,比如本题中的ticket就是共享数据。
  • 同步监视器:俗称:锁。任何一个类的对象都可以充当锁。要求:多个线程必须要共用统一把锁。
  • 同步的方式,解决了线程的安全问题—好处。但是操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。-----局限性
  • 使用Runable接口创建多线程的方式中,可以使用this关键字;在继承Thread类中创建多线程中,慎用this充当同步监视器,可以考虑使用当前类充当同步监视器。Class clazz = windows.class 因此 类也是一个对象
  • 包裹操作共享数据的代码 不能多也不能少

修改之后的代码:


package demo2;


class Thread2 implements Runnable{

    private  int ticket=100;

    Object object = new Object();
    @Override
    public void run() {
        while (true){
            synchronized(object) { //括号中的内容可以直接使用当前对象this去充当
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket);
                    ticket--;
                } else {
                    break;
                }
            }
        }
    }
}
public class Test1 {
    public static void main(String[] args) {
        Thread2 thread2 = new Thread2();
        Thread t1 = new Thread(thread2);
        Thread t2 = new Thread(thread2);
        Thread t3 = new Thread(thread2);
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}

结果

运行结果

继承Thread的方式,去使用同步代码块,需要将声明的锁对象设为statci,否则创建的对象的同步监视器不唯一,就无法实现。


package demo2;


class WindowsTest2 extends Thread{
    private static int ticket=100;
    private static   Object obj = new Object();

    @Override
    public void run() {
        while (true){
            synchronized (obj){ //这里不能使用this去充当,可以直接写一个Test.class   类也是对象
                if (ticket>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName()+":买票,票号为:"+ticket);
                    ticket--;
                }else {
                    break;
                }
            }
        }
    }
}
public class  Test2{
    public static void main(String[] args) {
        WindowsTest2 w1 = new WindowsTest2();
        WindowsTest2 w2 = new WindowsTest2();
        WindowsTest2 w3 = new WindowsTest2();
        w1.setName("窗口一");
        w2.setName("窗口二");
        w3.setName("窗口三");
        w1.start();
        w2.start();
        w3.start();
    }
}

同步方法

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


通过实现Runable的方式实现同步方法。


package demo2;


class Thread3 implements Runnable {

    private int ticket = 100;


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

    }
    private synchronized void show(){
        if (ticket > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket);
            ticket--;
        }
    }
}

public class Test3 {
    public static void main(String[] args) {
        Thread3 thread3 = new Thread3();
        Thread t1 = new Thread(thread3);
        Thread t2 = new Thread(thread3);
        Thread t3 = new Thread(thread3);
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}

通过实现继承Thread的方式实现同步方法。使用的同步监视器是this,则不唯一,就会报错。所以将该方法定义为static。当前的同步换时期就变成Test4.class了


package demo2;


class WindowsTest4 extends Thread{
    private static int ticket=100;
    private static   Object obj = new Object();

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

    }
    public static synchronized void show(){//同步监视器不是this了,而是当前的类
//    public synchronized void show(){//同步监视器是this  ,t1,t2,t3
        if (ticket>0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":买票,票号为:"+ticket);
            ticket--;
        }
    }
}
public class  Test4{
    public static void main(String[] args) {
        WindowsTest4 w1 = new WindowsTest4();
        WindowsTest4 w2 = new WindowsTest4();
        WindowsTest4 w3 = new WindowsTest4();
        w1.setName("窗口一");
        w2.setName("窗口二");
        w3.setName("窗口三");
        w1.start();
        w2.start();
        w3.start();
    }
}

总结

  • 同步方法仍然设计到同步监视器,只是不需要我们去显示的声明。
  • 非静态的同步方法,同步监视器是:this静态的同步方法中,同步监视器是类本身。

Lock锁解决线程安全问题

synchronize与lock的异同

相同

  • 都可以解决线程安全问题

不同

  • synchronize机制在执行相应的同步代码以后,自动的释放同步监视器;Lock需要手动的启动同步lock(),同时结束同步也需要手动的实现unlock()。

建议优先使用顺序

Lock------>同步代码块(已经进入了方法体,分配了相应资源)---->同步方法(在方法体之外)


package demo2;

import java.util.concurrent.locks.ReentrantLock;


class Lock1 implements Runnable{
    private int ticket=50;

    //1.实例化
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while(true){
            try {
                //2.调用lock锁定方法
                lock.lock();
                if (ticket>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket);
                    ticket--;
                }else{
                    break;
                }
            } finally {
                //3.调用解锁方法
                lock.unlock();
            }
        }
    }
}
public class LockTest1 {
    public static void main(String[] args) {
        Lock1 lock1 = new Lock1();
        Thread t1 = new Thread(lock1);
        Thread t2 = new Thread(lock1);
        Thread t3 = new Thread(lock1);
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");
        t1.start();
        t2.start();
        t3.start();

    }
}

到此这篇关于java多线程创建及线程安全的文章就介绍到这了,更多相关java多线程创建内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: java多线程创建及线程安全详解

本文链接: https://www.lsjlt.com/news/125781.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • java多线程创建及线程安全详解
    什么是线程 线程被称为轻量级进程,是程序执行的最小单位,它是指在程序执行过程中,能够执行代码的一个执行单位。每个程序程序都至少有一个线程,也即是程序本身。 线程...
    99+
    2022-11-12
  • 聊聊java多线程创建方式及线程安全问题
    什么是线程 线程被称为轻量级进程,是程序执行的最小单位,它是指在程序执行过程中,能够执行代码的一个执行单位。每个程序程序都至少有一个线程,也即是程序本身。 线程的状态 新建(New)...
    99+
    2022-11-12
  • Java多线程之线程安全问题详解
    目录1. 什么是线程安全和线程不安全?2. 自增运算为什么不是线程安全的?3. 临界区资源和竞态条件总结:面试题: 什么是线程安全和线程不安全?自增运算是不是线程安全的?如何保证多线...
    99+
    2022-11-13
  • 详解Java线程的创建及休眠
    目录一、进程vs线程 二、线程的创建方式三、实现Runnable接口的方式(3种)四、实现Callable接口的方式(1种)五、线程的休眠六、线程优先级七、守护线程一、进程vs线程 ...
    99+
    2022-11-12
  • 【Java系列】详解多线程(三)—— 线程安全(下篇)
    个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【Java系列专栏】【JaveEE学习专栏】 本专栏旨在分享学习Java的一点学习心得,欢迎大家在评...
    99+
    2023-12-22
    java 安全 多线程 java-ee
  • Java多线程 - 线程安全和线程同步解决线程安全问题
    文章目录 线程安全问题线程同步方式一: 同步代码块方式二: 同步方法方式三: Lock锁 线程安全问题 线程安全问题指的是: 多个线程同时操作同一个共享资源的时候可能会出现业务安全问题,称为线程安全问题。 举例:...
    99+
    2023-08-20
    java 安全 jvm
  • Java多线程之线程的创建
    目录一、三种创建方式二、通过Thread类创建2.1 步骤2.2 案例2.3 注意的问题三、Thread类中常用的方法3.1 案例四、通过实现Runnable接口来创建线程4.1 创...
    99+
    2022-11-12
  • Java多线程之线程安全问题详情
    目录1.线程安全概述1.1什么是线程安全问题1.2一个存在线程安全问题的程序2.线程加锁与线程不安全的原因2.1案例分析2.2线程加锁2.2.1什么是加锁2.2.2如何加锁2.2.3...
    99+
    2022-11-13
  • 详解在Java中如何创建多线程程序
    创建多线程程序的第一种方式:创建Thread类的子类 java.lang.Thread类:是描述线程的类,我们想要实现多线程程序,就必须继承Thread类 实现步骤: 1.创建一个T...
    99+
    2022-11-12
  • 如何创建Java线程安全类
    今天小编给大家分享一下如何创建Java线程安全类的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。无状态当多个线程访问同一个实例...
    99+
    2023-07-02
  • 创建java多线程程序
    目录创建多线程程序的第一种方式:创建Thread类的子类实现步骤:创建多线程程序的第二种方式:实现RunnabLe接口java.Lang.Thread类的构造方法实现步骤:实现Run...
    99+
    2022-11-12
  • 多线程-线程的创建
    线程的创建方式总结一下多线程的创建方式,多线程的实现一共四种方法,接下来将详谈一下创建的方式继承Thread类,而后覆写run()方法实现Runnable接口,而后覆写run()方法实现callable接口,而后覆写call方法线程池(后面...
    99+
    2016-10-19
    java教程 线程
  • Java多线程之线程安全问题
    文章目录 一. 线程安全概述1. 什么是线程安全问题2. 一个存在线程安全问题的程序 二. 线程不安全的原因和线程加锁1. 案例分析2. 线程加锁2.1 理解加锁2.2 synchroni...
    99+
    2023-09-21
    java 线程安全 多线程 synchronized jvm
  • JAVA多线程线程安全性基础
    目录线程安全性什么是线程安全的代码什么是线程安全性 总结线程安全性 一个对象是否需要是线程安全的,取决于它是否被多个线程访问,而不取决于对象要实现的功能 什么是线程安全的代码 核心:...
    99+
    2022-11-12
  • 详解_beginthreadex()创建线程
    目录一、使用_beginthreadex()             二、_beginthreadex()与代C...
    99+
    2022-11-12
  • Java线程安全与非线程安全解析
    ArrayList和Vector有什么区别?HashMap和HashTable有什么区别?StringBuilder和StringBuffer有什么区别?这些都是Java面试中常见的基础问题。面对这样的问题,回答是:ArrayList是非线...
    99+
    2023-05-31
    java 线程安全 ava
  • java怎么创建多线程
    在Java中,有两种方法可以创建多线程:1. 继承`Thread`类:创建一个类,继承自`Thread`类,并重写`run()`方法...
    99+
    2023-08-12
    java
  • Java多线程是什么及如何创建
    本篇内容主要讲解“Java多线程是什么及如何创建”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java多线程是什么及如何创建”吧!1、线程池:什么是线程池?咱们也不看长篇大论,通俗的来讲,线程池...
    99+
    2023-06-29
  • Java多线程之线程安全问题怎么解决
    本篇内容主要讲解“Java多线程之线程安全问题怎么解决”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java多线程之线程安全问题怎么解决”吧!1.线程安全概述1.1什么是线程安全问题首先我们需要...
    99+
    2023-06-30
  • java多线程怎么保证线程安全
    Java中有多种方式可以保证线程安全,以下是一些常见的方法:1. 使用synchronized关键字:使用synchronized关...
    99+
    2023-09-13
    java
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作