多线程教程高并发开发

一、多线程

1、什么是多线程?

多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。
多进程是指操作系统能同时运行多个任务(程序)。
多线程是指在同一程序中有多个顺序流在执行。

什么是线程什么是进程?在这里插入图片描述
1)进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1–n个线程。

2)线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。

进程可以理解为是一个身体;
线程就是身体的一些部分(手、脚);

1。单进程单线程:一个人在一个桌子上吃菜。
2。单进程多线程:多个人在同一个桌子上一起吃菜。
3。多进程单线程:多个人每个人在自己的桌子上吃菜。

什么是多线程什么是单线程
你一只手用筷子夹菜吃饭,就是单线程,如果你用两只手拿筷子夹菜吃饭就是多线程;

多线程能做什么?
1、多线程能够快速的处理大量数据(一个手写字和俩手写字);
2、多线程能模拟一些场景练习(并发场景)(秒杀、抢票);
3、多线程能把复杂的问题变成简单的问题(新手很容易出错);

2、创建多线程的三种方式

1、继承Thread

public class Test {
    public static void main(String[] args) {
        Th th = new Th();
        th.start();	// 启动多线程
    }
}

class Th extends Thread{
    @Override
    public void run() {
        System.out.println("多线程~");
    }
}

2、实现Runnble接口

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

class Ru implements Runnable{
    @Override
    public void run() {
        System.out.println("多线程~");
    }
}

3、创建Thread

public class Test {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("多线程~");
            }
        }).start();

    }
}

3、java多线程的5种状态

线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。

1)新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();

2)就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;

3)运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

4)阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:

1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;

2.同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;

3.其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

5)死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

二、线程安全问题

同时满足一下两种条件时:

1、多个线程在操控共享数据。
2、操作共享数据的线程代码有多条。

当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。

1、代码演示

例1:100根冰棍2个线程操控

public class Test {
    public static void main(String[] args) {
        Th th = new Th();
        new Thread(th, "线程1").start();
        new Thread(th, "线程2").start();

    }
}


class Th implements Runnable{

    // 冰棍数量
    private int k = 100;

    @Override
    public void run() {
        while (k > 0){
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            sell();
        }
    }

    
    public void sell(){
        if (k > 0){
            System.out.println(Thread.currentThread().getName()+": 出售第"+(100-k+1)+"根冰棍");
            k--;
        }
    }
}

2、分析结果

打印:
在这里插入图片描述
这里我们看到的并不是出售第1,2,3,4…,根冰棍;
线程1已经出售了第一根了,但是线程2也出售了第一根;

分析:
1、线程1还没有执行k-的时候;
2、线程2就已经执行了出售(打印)了;
3、导致出现了两次出售第1根冰棍

3、解决思路

将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程不可以参与运算。
当前线程把这些代码都执行完毕后,其他线程才可以参与运算。
在java中,用同步代码块就可以解决这个问题。
同步代码块的格式:

synchronized(对象)
{
需要被同步的代码 ;
}

这个对象一般称为同步锁
同步的前提:同步中必须有多 个线程并使用同一个锁。
同步的好处:解决了线程的安全问题。
同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。

解决例1的线程安全问题代码:

public class Test {
    public static void main(String[] args) {
        Th th = new Th();
        new Thread(th, "线程1").start();
        new Thread(th, "线程2").start();

    }
}


class Th implements Runnable{

    // 冰棍数量
    private int k = 100;

    @Override
    public void run() {
        while (k > 0){
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            sell();
        }
    }


    public void sell(){
        synchronized(this){
            if (k > 0){
                System.out.println(Thread.currentThread().getName()+": 出售第"+(100-k+1)+"根冰棍");
                k--;
            }
        }
    }
}

结果:
在这里插入图片描述

===================================================

4、同步锁是什么?

同步函数使用的是this锁
静态同步函数使用的是当前类的字节码文件。

同步函数和同步代码块的区别:
同步函数的锁是固定的this。
同步代码块的锁是任意的对象。
建议使用同步代码块。

同步静态函数定义方式:

private static synchronized void sell(){
    if (k > 0){
        System.out.println(Thread.currentThread().getName()+"出售第"+(100 - k + 1)+"票");
        k--;
    }
}

5、死锁常见问题

同步嵌套时,两个线程你拿了我的锁,我拿了你的锁,都不释放,造成死锁。

死锁代码:

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Th th = new Th();
        Thread thread = new Thread(th, "线程1");
        Thread thread2 = new Thread(th, "线程2");
        thread.start();
        Thread.sleep(50);
        th.bo = false;
        thread2.start();

    }
}


class Th implements Runnable{

    // 冰棍数量
    private int k = 100;
    private Object object = new Object();   // object锁
    public boolean bo = true;


    @Override
    public void run() {
        if (bo){
            while (k > 0){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (object){
                    sell();
                }
            }
        }else{
            while (k > 0){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                sell2();
            }
        }
    }


    public void sell(){
        synchronized (this){
            if (k > 0){
                System.out.println(Thread.currentThread().getName()+": 出售第"+(100-k+1)+"根冰棍");
                k--;
            }
        }
    }


    public synchronized void sell2(){
        synchronized (object){
            if (k > 0){
                System.out.println(Thread.currentThread().getName()+": 出售第"+(100-k+1)+"根冰棍");
                k--;
            }
        }
    }
}

打印结果:
在这里插入图片描述

出售到第5根冰棍时卡住了,造成了死锁;

分析:
1、线程1执行sell(),先使用object锁,后使用this锁;
2、线程2执行sell2(),先使用this锁,后时间object锁;
3、线程1使用了object锁,这时需要this锁;
4、线程2使用了this锁,这时需要object锁;
5、线程1需要this锁,线程2需要object锁,但是this锁被线程2占用没有释放,线程1占用object锁没有释放,两者需要的锁都被占用,造成死锁;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值