Java-多线程(什么是线程,线程的组成,线程的创建,线程的状态,线程的安全,线程池,线程的安全集合)

本文详细介绍了Java中的进程与线程概念,包括进程作为资源分配单位,线程作为CPU调度单位。讲解了线程的创建方式,如继承Thread类、实现Runnable接口和Callable接口,并通过实例演示了线程的运行。同时,阐述了线程状态、常用方法如sleep、yield、join以及线程的优先级和守护线程设置。讨论了线程安全问题,死锁的概念及避免策略,以及线程间的通信方法。最后,举例说明了线程同步中的wait和sleep方法的区别。

一、进程的概念

正在运行的程序,是系统进行资源分配的基本单位

目前操作系统都是支持多进程,可以同时执行多个进程,通过进程ID区分

单核CPU在同一时刻,只能有一个进程;宏观并行,微观串行

二、线程的概念

线程,又称为轻量级进程(Light Weight Process)

进程中的一条执行路径,也是CPU的基本调度单位。

一个进程由一个或多个线程组成,彼此间完成不同的工作,同时执行,称为多线程。

三、进程和线程的区别

1.进程是操作系统资源分配的基本单位,而线程是CPU的基本调度单位。

2.一个程序运行后至少有一个进程

3.一个进程可以包含多个线程,但是至少需要有一个线程,否则这个进程是没有意义。

4.进程间不能共享数据段地址,但是同进程的线程之间可以

四、线程的组成

任何一个线程都具有基本的组成部分   
          CPU时间片: 操作系统(OS)会为每个线程分配执行时间  

           运行数据:
                堆空间: 存储线程需要的对象,多个线程可以共享堆中的数据。
                栈空间:  存储线程需使用的局部变量,每个线程都拥有独立的栈。

            线程的逻辑代码.

五、线程的特点

1. 线程抢占式执行

        效率高
        可防止单一线程长时间独占CPU.

2. 在单核CPU中,宏观上同时执行,微观上顺序执行

六、线程的创建方式

1. 【继承Thread类,重写run方法】
2. 【实现Runnable接口】
3.  实现Callable接口

七、创建线程(继承Thread类)

1.创建

2. 获取和设置线程的名称

(1)获取线程ID和线程名称

1. 在Thread的子类中调用this.getId()或this.getName()
2. 使用Thread.currentThread().getId()和
Thread.currentThread().getName()

(2)修改线程名称

1. 调用线程对象的setName()方法
2. 使用线程子类的构造方法赋值

例子:4个窗口各卖100张票
 

public class Thread_01 extends Thread{
    int num=100;
    @Override
    public void run() {
        for (int i=0;i<=100;i++){
            System.out.println(Thread.currentThread().getName()+"卖的第"+i+"张票");
        }
    }
}
public class Test02 {
    public static void main(String[] args) {
        int num=100;
        Thread_01 m1=new Thread_01();
        m1.start();
        Thread_01 m2=new Thread_01();
        m2.start();
        Thread_01 m3=new Thread_01();
        m3.start();
        Thread_01 m4=new Thread_01();
        m4.start();


    }
}

八、创建线程(实现Runnable接口)

1.创建

2.例子:4个窗口共卖100张票

public class MyRunnable implements Runnable{
    static int num=100;
    @Override
    public void run() {
       while (num>0){
            num--;
            System.out.println(Thread.currentThread().getName()+"还剩"+num+"张票");
        }

    }
}
 public static void main(String[] args) {
        MyRunnable myRunnable=new MyRunnable();
        Thread t1=new Thread(myRunnable,"售票员1");
        Thread t2=new Thread(myRunnable,"售票员2");
        Thread t3=new Thread(myRunnable,"售票员3");
        Thread t4=new Thread(myRunnable,"售票员4");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }

 九、线程的状态

十、线程的常见方法

休眠:
     public static void sleep(long millis)
     当前线程主动休眠millis毫秒。

package com.gsh.demon_01;

/**
 * @Auther: haohao
 * @Date:2022/7/1723:40
 */
public class SleepThread extends Thread {


    @Override
    public void run() {
        try {
            sleep(500);//休眠(等待500毫秒)
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"这是第"+i+"次执行");
        }
    }
}
class ThreadTest{

    public static void main(String[] args) {
        SleepThread sleepThread=new SleepThread();
        sleepThread.start();//主线程main会先执行,因为sellp线程每次都休眠500毫秒
        for (int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"这是第"+i+"次执行");
        }
    }
}

放弃:
      public static void yield()
      当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片

package com.gsh.demon_02;

/**
 * @Auther: haohao
 * @Date:2022/7/1723:56
 */
public class YieldThread extends Thread{

    @Override
    public void run() {
        for (int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"这是第"+i+"次执行");
            Thread.yield();//线程放弃,不去争夺cpu放弃时间片
        }
    }
}
class ThreadTest{
    public static void main(String[] args) {
        //虽然线程放弃了当前的时间片回到就绪状态,但是cup还是会选择它,它还是有可能会执行
        //不是说放弃了就一定不会执行
        YieldThread yieldThread=new YieldThread();
        yieldThread.start();
        for (int i=0;i<10;i++){

            System.out.println(Thread.currentThread().getName()+"这是第"+i+"次执行");
        }
    }
}

加入:
      public final void join()
      允许其他线程加入到当前线程中

package com.gsh.demon_03;

import javax.swing.*;

/**
 * @Auther: haohao
 * @Date:2022/7/1800:07
 */
public class JoinThread extends Thread{
    @Override
    public void run() {
        for (int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"这是第"+i+"次执行");
        }
    }
}
class JoinThread02 extends Thread{
    @Override
    public void run() {
        for (int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"这是第"+i+"次执行");
        }
    }
}
class ThreadTest{
    public static void main(String[] args) throws InterruptedException {
        JoinThread joinThread=new JoinThread();
        joinThread.start();
        JoinThread02 joinThread02=new JoinThread02();
        joinThread02.start();
        joinThread.join();

        for (int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"这是第"+i+"次执行");
        }
    }
}

优先级:

        线程对象.setPriority()

        线程优先级1-10,默认为5,优先级越高,表示获取CPU的概率越搞

守护线程:

        线程对象.setDaemon(true);设置为守护线程.

        线程有两类:用户线程(前台线程)和守护线程(后台线程)

        如果程序中所有前台线程都执行完毕了,后台线程也会自动结束.

        垃圾回收属于守护线程。

package com.gsh.demon_04;

/**
 * @Auther: haohao
 * @Date:2022/7/1800:32
 */
public class SetDemon implements Runnable{
    @Override
    public void run() {
        for (int i=0;i<50;i++){
            System.out.println(Thread.currentThread().getName()+"执行第"+i+"次************");
        }

    }
}
class ThreadMethod{
    public static void main(String[] args) {
        SetDemon setDemon=new SetDemon();
        Thread thread=new Thread(setDemon,"守护线程!");
        thread.setDaemon(true);//主函数执行完后守护执行也停止;
        thread.start();
        for (int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"这是第"+i+"次执行");
        }


    }
}

十一、线程的安全

线程安全问题:

解决方法:

    

线程的同步方法:

线程的同步规则:

十二、死锁

死锁的概念:

        当A线程拥有锁资源a时,这时A线程需要锁资源b, 而B线程拥有锁资源b,这时B线程需要锁资源a, 这样会导致A等待B线程释放资源b, B线程等待A线程释放锁资源a。 从而二个处于永久等待。从而操作死锁。

操作死锁的概念:

锁与锁之间有嵌套导致。

死锁的解决方案:

1. 尽量减少锁得嵌套。
2. 可以使用一些安全类。
3. 可以使用Lock中得枷锁,设置枷锁时间。

例子:

package com.gsh.demon_01;

/**
 * @Auther: haohao
 * @Date:2022/7/1814:38
 */
//死锁
    //造成这个死锁的原因:

/**
 * 当线程1拥有锁man时,线程1也需要锁womanm
 * 这时线程2拥有锁woman而线程2需要锁man
 * 这会导致线程1等待线程2释放锁woman而线程2需要线程1释放锁man从而产生死锁
 */
class BoyLock extends Thread{
    @Override
    public void run() {
        synchronized (LockObject.man){
            System.out.println(Thread.currentThread().getName()+"得到了她的心但没有得到她的爱!");
            synchronized (LockObject.woman){
                System.out.println(Thread.currentThread().getName()+"得到了心也得到了爱");
                System.out.println("直接原地结婚吧!");
            }
        }
    }
}
class GirlLock extends Thread{
    @Override
    public void run() {
        synchronized (LockObject.woman){
            System.out.println(Thread.currentThread().getName()+"得到了她的爱但没有得到她的心!");
            synchronized (LockObject.man){
                System.out.println(Thread.currentThread().getName()+"得到了心也得到了爱");
                System.out.println("直接原地结婚吧!");
            }
        }
    }
}
class LockObject{
    public static Object man=new Object();
    public static Object woman=new Object();
}
class Test01{
    public static void main(String[] args) {
        BoyLock deadLock=new BoyLock();
        GirlLock deadLock1=new GirlLock();
        deadLock.start();
        deadLock1.start();
    }
}

 

十三、线程通信
        

使用线程通信的意义:

        我们无法决定哪个线程先获取CPU,这样也就无法决定哪个线程先执行

        我们可能要指定那些线程先执行哪些线程后执行,这样我们们就需要使用线程通信技术.

线程通信中使用的方法

 

                

 例子:

BankCard类:

package com.gsh.demon_02;

/**
 * @Auther: haohao
 * @Date:2022/7/1815:15
 */
public class BankCard {
    //余额
    private double balance;
    //银行卡状态
    private boolean flag=false;//true:有钱,false:没钱

    public void setBalance(double balance) {
        this.balance = balance;
    }
    public  double getBalance(){
        return balance;
    }
    //存钱
    public synchronized void save(double money){
        if(flag==true){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //卡里没钱存钱
        this.balance=this.balance+money;
        System.out.println(Thread.currentThread().getName()+"往卡中存"+money+"卡中余额"+this.balance);
        //改变余额状态
        flag=true;
        //唤醒等待队列
        this.notify();
    }
    //取钱
    public synchronized void take(double money){
        //判断卡里是否有钱
        if(flag==false){
            //等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //卡里有钱花钱
        this.balance=this.balance-money;
        System.out.println(Thread.currentThread().getName()+"在卡中取"+money+"卡中余额"+this.balance);
        //改变余额状态
        flag=false;
        //唤醒
        this.notify();
    }

}

BoySave类:

package com.gsh.demon_02;

/**
 * @Auther: haohao
 * @Date:2022/7/1815:48
 */
public class BoySave implements Runnable{
    private BankCard bankCard;
    public BoySave(BankCard bankCard) {
        this.bankCard=bankCard;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            bankCard.save(1000);
        }
    }
}

GirlTake类:

package com.gsh.demon_02;

/**
 * @Auther: haohao
 * @Date:2022/7/1815:48
 */
public class GirlTake implements Runnable{
    private BankCard bankCard;
    public GirlTake(BankCard bankCard) {
        this.bankCard=bankCard;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            bankCard.take(1000);
        }
    }
}

测试类(CardTest):

package com.gsh.demon_02;

/**
 * @Auther: haohao
 * @Date:2022/7/1815:50
 */
public class CradTest {
    public static void main(String[] args) {
        BankCard bankCard=new BankCard();

        BoySave boySave=new BoySave(bankCard);
        GirlTake girlTake=new GirlTake(bankCard);

        Thread m1=new Thread(boySave,"存钱");
        Thread m2=new Thread(girlTake,"取钱");

        m1.start();
        m2.start();
    }
}

十四、sleep与wait的区别

1.所在得类不同。sleep属于Thread类,wait属于Object类。
2.使用的地方: sleep可以使用再任何代码块。wait只能再同步代码块中。
3.是否释放锁资源: sleep不释放锁资源,wait会释放锁资源。
4.sleep时间到了自动唤醒,wait必须需要使用notify和notifyAll唤醒

十五、notify和notyfyAll区别?

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值