多线程学习

基本概念

1、程序:一些列有组织的文件,封装操作系统的各种API,实现不同的效果
2、进程:程序在系统中的一次执行过程,是拥有资源的最小单位,不同进程之间相互独立
3、IP地址:唯一在网络上标识一台主机的位置
3、端口号:操作系统上定位一个进程,且一般不变
4、线程:同一个进程下共享资源,任务执行(系统调度)的基本单位
在这里插入图片描述
5、JDK中提供的线程库实际上利用OS提供的线程库进行二次封装。故JDK中唯一的进程不一定是OS中唯一的进程,可能好几个进程

创建线程

java.long.Thread
【Java一切皆对象–线程对象–Thread类】
【Thread.activeCount()目前只在执行的线程数量,要-1出去后台进程】
1、多线程的创建
①继承Thread类覆写run方法
产生线程对象

public class Test1 {
    private static class MyThread extends Thread{
        @Override
        public void run() {//JVM调用run方法
            System.out.println(Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) {
        MyThread m1 = new MyThread();
        MyThread m2 = new MyThread();
        MyThread m3 = new MyThread();
        m1.start();//启动线程
        m2.start();
        m3.start();
        System.out.println(Thread.currentThread().getName());
    }
}

②实现Runnable接口,覆写run方法
产生任务对象

public class Test2 {
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyThread());
        Thread t2 = new Thread(new MyThread());
        Thread t3 = new Thread(new MyThread());
        t1.start();
        t2.start();
        t3.start();
        System.out.println(Thread.currentThread().getName());
    }
}

class MyThread implements Runnable {//不是线程实体,是线程任务

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

推荐使用②,实现接口更加灵活,子类还能实现别的接口,继承别的类
方式①只能继承Thread类,造成单继承局限

【使用匿名内部类和lambda表达式创建线程】

public class Test3 {
    public static void main(String[] args) {
        Thread t1 = new Thread("继承Thread类") {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        };
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        }, "实现Runnable接口");
        Thread t3 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName());
        }, "使用lambda表达式实现Runnable接口");
        t1.start();
        t2.start();
        t3.start();
        System.out.println(Thread.currentThread().getName());
    }
}

③实现callable接口,覆写call方法
④使用线程池创建线程
【多线程应用场景】

把一个大任务拆分为多个子任务,多个子线程并发执行,提高系统的处理效率

Thread类

1、构造方法
在这里插入图片描述

public class ThreadName {
    public static void main(String[] args) {
        Thread t1 =new Thread();
        Thread t2 =new Thread(new Runnable() {
            @Override
            public void run() {

            }
        });
        Thread t3 = new Thread("小丑线程");
        Thread t4 = new Thread(new Runnable() {//常用
            @Override
            public void run() {

            }
        },"蝙蝠侠线程");
    }
}

2、核心属性
在这里插入图片描述
3、线程中断–通信方式
【中断一个正在执行的run方法还没结束的线程】
①线程中断的两种方式
1)通过共享变量进行中断

  /**
     * 通过共享变量中断线程
     */
    private static class MyThread extends Thread {
        volatile boolean isQuit = false;//此关键字保证可见性

        @Override
        public void run() {
            while (!isQuit) {
                System.out.println("进行中");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + "被中断了");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        System.out.println("子线程开始");
        myThread.start();
        Thread.sleep(1000);
        myThread.isQuit = true;
        System.out.println("停止子线程");
    }

在这里插入图片描述

2)使用 Thread.interrupted()静态方法 或者 Thread对象的成员方法isInterrupted()

public class Test5 {
    private static class MyThread implements Runnable {
        @Override
        public void run() {
            while (!Thread.currentThread().isInterrupted()) {//成员方法
                //while (!Thread.isInterrupted()) {   ---静态方法
                System.out.println(Thread.currentThread().getName() + "正在进行");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);//被中断后抛出中断异常
 //抛出异常之后,中断状态会被清除
                    System.out.println("被中断了");
                    break;
                }
            }
            System.out.println(Thread.currentThread().getName() + "被中断了");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new MyThread(),"小丑");
        System.out.println("子线程开始");
        t1.start();
        Thread.sleep(5000);
        System.out.println("该停止了");
        t1.interrupt();
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
【Thread.isInterrupted()与Thread.currentThread().isInterrupted()之间的区别】

public class Test6 {
    private static class MyThread implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.interrupted());//清除中断标志
                /**
                 * 运行结果
                 * true
                 * false
                 * false
                 * false
                 * false
                 * false
                 * false
                 * false
                 * false
                 * false
                 */
            }
        }
    }
    private static class MyThread1 implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().isInterrupted());//不会清除中断标志,判断而已
                /**
                 * true
                 * true
                 * true
                 * true
                 * true
                 */
            }
        }
    }
    public static void main(String[] args) {
//        Thread t1 = new Thread(new MyThread());
//        t1.start();
//        t1.interrupt();
        Thread t2 =new Thread(new MyThread1());
        t2.start();
        t2.interrupt();//修改指定线程的状态为中断状态
    }
}

【修改指定线程的状态为中断状态—调用线程interrupt方法】
【Thread类的静态方法和成员方法isInterrupt作判断】
在这里插入图片描述
4、等待
join()方法,在哪调用在哪等待(阻塞状态)
【也是进程之间通信的一种方式】
在这里插入图片描述
5、休眠
sleep()
【sleep和yield方法不会百分百刷新工作内存读取主存】
6、线程状态
在这里插入图片描述
创建态:new
就绪态:ready
运行态:running(就绪和运行都是RUNNABLE)
终止态:terminated
阻塞态:(TIMED_WAITING BLOCKED WAITING)

在这里插入图片描述
在这里插入图片描述
7、线程状态转换
在这里插入图片描述
notify() 唤醒
yiele()方法 让出CPU,从运行态进入就绪态等待CPU调度–OS调度程序员无法选择
【使用不多无法控制】
【一个线程创建后只能启动一次】

callable接口

【submit()可以接收Rannable或者callable接口】
在这里插入图片描述
在这里插入图片描述

public class Callable_Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 0; i <= 100; i++) {
                    sum += i;
                }
                return sum;
            }
        };
        //接收call方法返回值使用FutureTask类
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        //启动callable接口还是得使用thread类的start方法,且必须通过FutureTask类
        Thread t1 = new Thread(futureTask);
        t1.start();
        int result = futureTask.get();//使用get方法会阻塞,直到call方法执行完毕
        System.out.println(result);
    }
}

线程安全问题

** 1、什么是线程不安全?**
A:多个线程串行和并发执行的结果不同,称为线程不安全
2、保证线程安全三个特性
A:原子性 -------------------一个操作一次执行完不会被中断
可见性 -----------------------一个线程对共享变量的修改,能够及时被其他线程看到
防止指令重排 ---------------代码的书写顺序不一定是最终JVM或者CPU的执行顺序
在这里插入图片描述

Java的内存模型

【描述多线程场景下,Java线程内存(高速缓存和寄存器)和主内存之间的关系】
在这里插入图片描述
在这里插入图片描述

synchronized关键字—线程上锁–锁的是对象

【满足可见性和原子性】
【在变量或者方法上使用synchronized】
【监视器锁–monitor lock(对象锁)】

【锁的是对象】
特性:
1、互斥----mutex lock 某个线程获取到该对象的锁时,其他线程也要获取同一个对象的锁时,处于阻塞等待状态
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、synchronized代码块刷新内存
在这里插入图片描述
在这里插入图片描述

3、可重入
【Java中的线程安全锁都是可重入的,包括java.concurrent.lock】
【获取到对象锁的线程可以再次加锁】
【若不支持可能会造成死锁】

【当synchronized锁的是静态方法,相当于将此方法的类的所有对象都锁了(锁的是类名.class对象 全局唯一)】
在这里插入图片描述
【锁的优化都是在优化锁的力度】

juc下的常见子类

对象锁Lock

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public class Lock_Test {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        Thread t1 = new Thread(() -> {
            lock.lock();//加锁
            System.out.println("加锁成功-----");
            try {
                System.out.println(Thread.currentThread().getName() + "获取到锁,其他线程等待");
                Thread.sleep(8000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();//解锁
            }
            System.out.println("解锁成功-----");
        }, "小丑");
        t1.start();
        Thread t2 = new Thread(() -> {
            boolean isLocked = false;
            try {
                isLocked = lock.tryLock(3000, TimeUnit.MICROSECONDS);//代表千分之一毫秒的时间单位
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (isLocked) {
                    lock.unlock();
                }
            }
            System.out.println(Thread.currentThread().getName() + "不找你了,回去吃饭!");
        }, "蝙蝠侠");
        t2.start();
    }
}

lock与synchronized区别

在这里插入图片描述

死锁

【锁对象的循环等待问题】

产生死锁的原因

在这里插入图片描述

如何避免死锁

【可以使用jconsole检测死锁】
【破坏循环等待条件】

concurrent类

信号量Semaphore

【p-- 申请资源操作】【acquire()】
【v–释放资源操作】【release()】
【pv操作都是原子性的】
【每次申请和释放都是相同资源数】

public static void main(String[] args) {
        //构造参数传入可用资源的个数
        Semaphore semaphore = new Semaphore(5);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName() + "准备申请资源");
                    //P操作申请一个资源,无参默认
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "获取成功");
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + "释放资源");
                    //v操作,释放一个占有的资源
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
    }

计数器CountDownLatch–大号join

【countDown() —计数器-1】
【await() 等待计数器为0】

public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(10);//10表示等待数量
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(new Random().nextInt(1000));
                    System.out.println(Thread.currentThread().getName() + "到达");
                    latch.countDown();//当一个进程到达,计数器减一
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(runnable, "运动员" + i + 1);
            t.start();
        }
        //main宪政就是裁判线程,需要等待所有运动员到达重点再恢复执行
        //当countDown方法将计数器减为0则继续执行
        latch.await();
        System.out.println("所有运动员均已到位");
    }

循环栅栏 CyclicBarrier–自己了解补充

两个线程交换器 Exchanger–自己了解补充

线程安全类

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

volatile关键字

在这里插入图片描述
【只能保证可见性,不能保证原子性】
【内存屏障,防止指令重排】
在这里插入图片描述

等待与唤醒

【wait和notify是Object类的方法,用于线程的等待与唤醒,必须搭配synchronized锁使用】

等待方法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

唤醒方法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值