多线程

多线程:
高可用、高性能、高并发
juc

一.概念
线程 独立的执行路径

多线程 开辟了多条路径

Process与Thread的区别:
Process: 作为资源分配的单位;每个进程都有独立的代码和数据空间,进程的切换会有较大的开销;
在操作系统中能同时运行多个任务,系统在运行时会为不同的进程分配不同的内存区域;
没有线程的进程可视为单线程,如果一个进程拥有多个线程,则执行过程不是一条线的,而是由多线程共同完成
Thread: 调度和执行的单位;线程可视为轻量级的进程,线程共享堆和方法区,每个线程拥有独立的栈、本地方法栈和程序计数器,线程的切换开销小
除了CPU之外,不会为线程分配内存,线程组只是共享资源
线程是进程的一部分,所以线程又被称为轻权进程
注意:很多多线程是模拟出的,真正的多线程是指多个CPU,即多核,如服务器

在程序运行时,即使没有自己创建线程,后台也会存在多个线程,如GC线程、主线程

main()称为主线程,作为系统的入口点,用于执行整个程序

在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序时不能人为干预

对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制

线程会带来额外的开销,如CPU调度时间,并发控制开销

每个线程在自己的工作内存交互,加载和存储主内存控制不当会造成数据不一致

二.继承Thread
1.创建线程的三种方法
继承Thread类
实现Runnable接口
实现Callable接口(juc包下)
2.案例

public class StartThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("唱歌");
        }
    }

    public static void main(String[] args) {
        new StartThread().start(); //不保证立即运行,由CPU调度
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("跳舞");
        }

    }
}

查看start(),调用本地方法
在这里插入图片描述

public class StartRun implements  Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("唱歌");
        }
    }

    public static void main(String[] args) {
        new Thread(new StartRun()).start(); //不保证立即运行,由CPU调度
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("跳舞");
        }

    }
}

public class Web12306 implements Runnable {
    /*共享资源 线程并发*/
    private  int num=99; //99张票
    @Override
    public void run() {
        while (true) {
            if(num<=0){
                return;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "抢到" + num--);
        }
    }
    public static void main(String[] args) {
        Web12306 web12306 = new Web12306();  //同一份资源
        /*多个代理*/
        new Thread(web12306,"小明").start();
        new Thread(web12306,"小红").start();
        new Thread(web12306,"校长").start();
    }
}

线程同步

1.问题
当使用多个线程去访问同一资源,且多个线程对资源有写的操作时,容易出现线程安全问题

2.方案
1.同步代码块
2.同步方法
3.锁机制

3.同步代码块
1.格式:
synchronized(锁对象){
可能会出现线程安全问题的代码块(访问了共享数据的代码)
}
注意:
1.通过代码块的锁对象,可以使用任意的对象
2.但必须保证多个线程使用的锁对象是同一个
3.锁对象作用:只让一个线程在同步代码块中执行

同步技术原理:
使用一个锁对象(同步锁 对象监视器),多个线程一起抢夺CPU的执行权时, 谁抢到谁执行run();A线程抢到CPU的执行权,执行run(),遇到synchronized代码块
这时会检测synchronized代码块是否存在有锁对象,发现有,立即获取锁对象,然后进入同步中执行;B线程抢到CPU的执行权,遇到synchronized代码块,检查synchronized代码块是否有锁对象,发现没有
线程B进入阻塞状态,会一直等待A线程归还锁对象,直到A线程执行完同步代码,才会归还锁对象给同步代码块,B线程才能执行
同步保证只有一个线程在同步中执行共享数据,保证了安全,程序频繁的判断锁,获取锁,程序的效率会降低

4.同步方法
使用步骤:
1.把访问了共享数据的代码抽取出来,放到一个方法中
2.把方法添加synchronized修饰符
锁对象 this

静态同步方法(静态方法优于对象,锁对象是本类的class属性)

5.使用Lock锁
Lock接口 lock()获取锁 unlock()释放锁
ReentrantLock implements Lock接口

使用步骤:
在成员位置创建ReentrantLock对象
在可能会出现安全问题的代码前调用Lock接口中的lock()
在可能会出现安全问题的代码后调用Lock接口中的unlock()


```java
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class RunnableImpl implements Runnable {
    private int ticket = 100;
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            lock.lock();
            if (ticket > 0) {
                try {
                    Thread.sleep(100);
                    System.out.println(Thread.currentThread().getName() + "卖出第" + ticket + "张票");
                    ticket--;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock(); //释放锁
                }

            }
        }

    }

    public static void main(String[] args) {
        RunnableImpl runnable = new RunnableImpl();
        new Thread(runnable).start();
        new Thread(runnable).start();
        new Thread(runnable).start();
    }
}
线程状态
1.新建状态  new
2.运行状态 start() 获取到CPU执行权
3.阻塞状态  没有CPU执行权
4.死亡状态  run()结束  stop()
5.休眠状态 sleep(时间)  wait(时间)
6.无限等待状态 	Object.wait()

等待唤醒:线程之间的通信
重点:有效的利用资源

进入到Timewaiting(计时等待)有两种方式
sleep(long m)  在毫毛值结束之后,线程睡醒进入到Runnable/Blocked状态
wait(long m)  
唤醒的方法
notify()
notifyAll()

wait方法和notify方法必须由同一个锁对象调用。对应的锁对象可通过notify唤醒使用同一锁对象调用的wait方法后的线程
wait方法和notify方法属于Object类的方法,锁对象可以是任意对象
wait方法和notify方法必须在同步代码块或同步函数中使用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

但行益事莫问前程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值