首页 > 作文

聊聊java多线程创建方式及线程安全问题

更新时间:2023-04-04 13:10:51 阅读: 评论:0

什么是线程

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

线程的状态

新建(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

/** * @author gocchin * @date 2021/5/11 11:56 * @blog: itdfq.com * @qq: 909256107 * @descript: */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接口创建多线程

/** * @author gocchin * @date 2021/5/11 12:37 * @blog: itdfq.com * @qq: 909256107 * @descript: 实现runable接口的方式创建多线程 * 1.创建一个实现了r网站销售方案unable接口的类 * 2.实现类去实现runable中的抽象方法,run(); * 3.创建实现类的对象 * 4.将此对象作为参数传递到thread类的构造器中,创建thread类的对象 * 5.通过thread类的对象调用start() */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;/** * @author gocchin * @date 2021/5/11 13:03 * @blog: itdfq.com * @qq: 909256107 * @descript: */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.executorrvice;import java.util.concurrent.executors;/** * @author gocchin * @date 2021/5/11 13:10 * @blog: itdfq.com * @qq: 909256107 * @descript: */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) {        //创建线程池        executorrvice executorrvice= executors.newfixedthreadpool(10);        thread1 threadpool = new thread1();        for (int i=0;i<5;i++){            //为线程池分配任务            executorrvice.submit(threadpool);        }        //关闭线程池        executorrvice.shutdown();    }}

thread中的常用方法start():

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

线程的优先级

线程的优先级等级

max_priority:10min_priority:1norm_priority:5

涉及的方法

getpriority():返回线程的优先值tpriority(int newpriority):改变线程的优先级

说明

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

线程的同步

多线程卖票

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

package demo2;/** * @author gocchin * @date 2021/5/11 13:37 * @blog: itdfq.com * @qq: 909256107 * @descript: 创建三个窗口买票,总票数为100张,使用runable接口的方式 *      存在线程安全问题,待解决 */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--;            }el {                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.tname("窗口一");        t2.tname("窗口二");        t3.tname("窗口三");        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--;            }el {                break;            }        }    }

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

理想情况

极端情况

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

同步代码块

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

说明

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

修改之后的代码:

package demo2;/** * @author gocchin * @date 2021/5/11 13:37 * @blog: itdfq.com * @qq: 909256107 * @descript: 创建三个窗口买票,总票数为100张,使用runable接口的方式 *      存在线程安全问题,待解决 */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--;                } el {                    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.tname("窗口一");        t2.tname("窗口二");        t3.tname("窗口三");        t1.start();        t2.start();        t3.start();    }}

结果

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

package demo2;/** * @author gocchin * @date 2021/5/11 14:45 * @blog: itdfq.com * @qq: 909256107 * @descript: */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--;                }el {                    break;                }            }        }    }}public class  test2{    public static void main(string[] args) {        windowstest2 w1 = new windowstest2();        windowstest2 w2 = new windowstest2();        windowstest2 w3 = new windowstest2();        w1.tname("窗口一");        w2.tname("窗口二");        w3.tname("窗口三"撕心裂肺的近义词);        w1.start();        w2.start();        w3.start();    }}

同步方法

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

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

package demo2;/** * @author gocchin * @date 2021/5/11 13:37 * @blog: itdfq.com * @qq: 909256107 * @descript: 创建三个窗口买票,总票数为100张,使用runable接口的方式 * 存在线程安全问题,待解决 */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 mai两小儿辩日译文n(string[] args) {        thread3 thread3 = new thread3();        thread t1 = new thread(thread3);        thread t2 = new thread(thread3);        thread t3 = new thread(thread3);        t1.tname("窗口一");        t2.tname("窗口二");        t3.tname("窗口三");        t1.start();        t2.start();        t3.start();    }}

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

package demo2;/** * @author gocchin * @date 2021/5/11 14:45 * @blog: itdfq.com * @qq: 909256107 * @descript: */class windowstest4 extends thre外国文学论文ad{    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.tname("窗口一");        w2.tname("窗口二");        w3.tname("窗口三");        w1.start();        w2.start();        w3.start();    }}

总结

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

lock锁解决线程安全问题

synchronize与lock的异同

相同

都可以解决线程安全问题

不同

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

建议优先使用顺序
lock——>同步代码块(已经进入了方法体,分配了相应资源)—->同步方法(在方法体之外)

package demo2;import java.util.concurrent.locks.reentrantlock;/** * @author gocchin * @date 2021/5/11 15:58 * @blog: itdfq.com * @qq: 909256107 * @descript: */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--;                }el{                    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.tname("窗口一");        t2.tname("窗口二");        t3.tname("窗口三");        t1.start();        t2.start();        t3.start();    }}

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

本文发布于:2023-04-04 13:10:49,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/zuowen/5d5ef2fb4d73e9af30176cd9039709b0.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

本文word下载地址:聊聊java多线程创建方式及线程安全问题.doc

本文 PDF 下载地址:聊聊java多线程创建方式及线程安全问题.pdf

标签:线程   方法   窗口   监视器
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图