【狂神说Java】线程

多线程

  1. 线程是独立的执行路径

  2. 默认存在main()线程【用户线程】和gc()【守护线程】线程

  3. main()线程是程序入口,执行整个程序

  4. 一个进程中开辟多个线程,由调度器执行调度

  5. 多个线程对同一份资源进行操作时会存在资源抢夺

创建线程对象的方法

1. 继承Thread类

  1. 自定义线程类,然后继承Thread
  2. 重写Thread中的run()方法
  3. 在主线程main中实例化线程对象
  4. 调用start()开启线程

2.实现Runable接口【推荐】

  1. 自定义线程类,实现Runable接口
  2. 重写Runable接口中的run()方法
  3. 主线程中实例化线程对象,然后实例化Thead类

扩展

引入一个jar包之后,必须添加到library才能使用

3.Lambda表达式

()->{}:
{}:接口实现类的方法体
():接口中方法的参数

4.线程的五大状态

new 创建状态
start 就绪状态,等待cpu来调度
运行状态
阻塞状态:sleep,wait
死亡状态:线程正常执行完在这里插入图片描述

线程对象的方法:

设置线程优先级:setPriority(int new Priority)

public class TestPriority {
    public static void main(String[] args) {
        Myriority myriority = new Myriority();
        Thread t1 = new Thread(myriority,"T1");
        Thread t2 = new Thread(myriority,"T2");
        t1.setPriority(Thread.MAX_PRIORITY);
        t2.setPriority(Thread.MIN_PRIORITY);
        t1.start();
        t2.start();
    }
}
class Myriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"被执行了=="+Thread.currentThread().getPriority());
    }
}

线程休眠:sleep(),休眠的线程进入阻塞状态

public class TestSleep {
    public static void main(String[] args) {
        tenDown();
        System.out.println("System.currentTimeMillis()==="+System.currentTimeMillis());
        Date startTime = new Date(System.currentTimeMillis());
        System.out.println("start=="+startTime);
        while (true){
            try {
                Thread.sleep(1000);
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
                startTime = new Date(System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void tenDown(){
        int num = 10;
        while (true){
            try {
                Thread.sleep(1000);
                if(num <= 0){
                    break;
                }else {
                    System.out.println("倒计时"+num--+"秒");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

线程礼让:yield():线程礼让不一定成功,A线程礼让B线程,A从cpu出来和B同一起点

public class ThreadYield implements Runnable{
    public static void main(String[] args) {
            ThreadYield threadYield = new ThreadYield();
            new Thread(threadYield,"小明").start();
            new Thread(threadYield,"小红").start();

    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"开始执行");
        Thread.yield();//线程礼让
        System.out.println(Thread.currentThread().getName()+"停止执行");
    }
}

线程插队: join():优先执行全部的插队线程

public class TestJoin implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("VIP线程来插队了"+i);
        }
    }
    public static void main(String[] args) throws Exception{
          Thread thread = new Thread(new TestJoin());
        for (int i = 0; i < 400; i++) {
            System.out.println("主线程在排队"+i);
            if(i==100){
                thread.start();
                thread.join();
            }
        }
    }
}

线程状态:state()

线程优先级

线程的优先级用数字表示,范围1-10,线程优先级高也不一定先执行

  • Thread.MIN_PRIORITY=1 最小优先级
  • Thread.MAX_PRIORITY=10 最大优先级,
  • 先设置优先级,再启动
  • 优先级的高低表示线程调度的概率高低,存在性能倒置问题

守护线程(daemon)

线程分为:用户线程和守护线程
虚拟机必须确保用户线程执行完毕:main()
虚拟机不用确保守护线程执行完毕:gc() 垃圾回收 ,监控内存,
设置线程为守护线程:Thread.setDaemon(true);//默认值false,是用户线程

public class TestDemon {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();
        Thread thread = new Thread(god);
        thread.setDaemon(true);
        thread.start();
        new Thread(you).start();
    }
}
class God implements Runnable{
    @Override
    public void run() {
       while (true){
           System.out.println("守护线程");
       }
    }
}
class You implements  Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 36; i++) {
            System.out.println("用户线程");
        }
        System.out.println("bye~");
    }
}

线程同步(重点+难点)

多个线程操作同一个资源,又叫并发,本质也是排队
例如,抢票,取钱,微信抢红包,ArrayList添加数据
线程同步形成条件:队列+锁
优点:安全但损失性能
关键字:synchronized
多个线程操作同一对象并改变数据,就需要线程同步,其他对象进入对象等待池形成队列

队列和锁🔒

ArrayList线程不安全
原因:多条线程同一瞬间操作同一位置,线程的内存都是各自的,互不影响

同步方法

  • 方法加了synchronized关键字就叫同步方法
  • 对方法进行synchronized,保证安全
  • 每个线程对象都有一把锁,synchronized获得线程对象的锁,才能执行(比如每个人上厕所,synchronized相当于厕所的门,上厕所的那个人带了锁给了门,才能上厕所)
  • 一旦执行,对象独占该锁🔒

需要修改的方法才需要synchronized

同步块

操作同一共享资源,使用同步块,比如,银行卡取钱

public void run(){
	synchronized(account){
    	线程体逻辑
    }
}

死锁🔒

概念:多个线程各自占有共享资源,互相等待对方释放资源而都停止的情形
发生条件:某一同步块同时拥有:两个以上对象的锁(比如,厕所门本来是一个人带着锁进去锁门上厕所就是正常运行,而死锁是厕所有人好几个人的锁,我在等你去上厕所,同时你也在等我去上厕所)
避免发生条件

  • 互斥条件:一个资源每次只能被一个进程使用
  • 请求与保持条件:一个进程因请求资源而阻塞,对以获得的资源保持不放
  • 不剥夺条件:进程以获得资源,在未使用完之前,不强行剥夺
  • 循环等待条件:若干进程循环等待

Lock🔒

  • synchronized是隐式同步锁
  • Lock是显示同步锁
    Lock类似于Synchronized,有相同的并发性
    Lock的实现类是:ReentrantLock(可重用锁)
    加锁:lock.lock();
    解锁:lock.unlock();
使用步骤:
  • 定义可重用锁🔒:
  • 在线程体中调用lock()
  • 在finally块中释放可重用锁Lock🔒
private final ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            try {
                lock.lock();
                线程体保证线程安全的代码
            } finally {
                lock.unlock();
            }
        }

    }

Synchronized🔒和Lock🔒对比

  • Lock是显式锁(手动开启,关闭),synchronized是隐式锁,出作用域自动释放
  • Lock只有代码块锁,Synchronized有代码块锁+方法锁
  • Lock锁,JVM花费较少时间调度线程,性能更好,有更好的扩展性

生产者消费者问题

线程之间如何协作,通信 :wait() , notify()

生产者线程和消费者线程
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值