• 0066 线程基础


     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    /*
        线程相关概念
    
        程序:为完成特定任务,用某种语言编写的一组指令的集合,简单来说就是我们写的代码
    
        进程
        1.进程是指运行中的程序,比如使用IDEA,就启动了一个进程,操作系统就会为该进程分配内存空间
                             当使用微信,又启动了一个进程,操作系统将为微信分配新的内存空间
        2.进程是程序的一次执行过程,或是正在运行的一个程序,是动态过程,有自身产生、存在和消亡的过程
    
        线程
        1.线程是由进程创建的,是进程的一个实体
        2.一个进程可以拥有多个线程
        3.单线程:同一时刻,只允许执行一个线程
        4.多线程:同一时刻,可以执行多个线程,如一个微信进程,可以同时打开多个聊天窗口
    
        并发:同一时刻,多个任务交替执行,即单核CPU实现的多任务就是并发
        并行:同一时刻,多个任务同时执行,即多核CPU实现多任务就是并行
    
        线程基本使用
        创建线程的两种方式
        1.继承Thread类,重写run方法
        2.实现Runnable接口,重写run方法
            Java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时不能在继承Thread类创建线程
        //从Java设计上看,这两种方式没有区别,因为Thread类本身就实现了Runnable接口
          实现Runnable接口方式更加适合多个线程共享一个资源的情况,且避免了单继承限制
     */
    //  演示继承Thread类,重写run方法
    public class Thread_ {
        public static void main(String[] args) throws InterruptedException {
            AA aa = new AA();//创建AA对象
            aa.start();//启动线程-->最终执行aa的run方法
            //aa.run();是一个普通的方法, 没有真正的启动一个线程,把 run 方法执行完毕,才向下执行
    //        部分源码
    //        public synchronized void start() {
    //                    start0();
    //            }
    //
    //         start0() 是本地方法,是 JVM 调用, 底层是 c/c++实现
    //         真正实现多线程的效果, 是 start0(), 而不是 run
    //         start()方法调用start0()方法后,该线程并不一定会马上执行,只是将线程变成可运行状态
    //         什么时候执行取决于CPU,由CPU统一调度
    
            //当main线程启动一个子线程Thread-0后,主线程会继续执行,所以主线程和子线程是交替执行
            for (int i = 0; i < 10; i++) {
                System.out.println("主线程执行" + i + "\t线程名=" + Thread.currentThread().getName());
                //休眠1秒
                Thread.sleep(1000);
            }
        }
    }
    //当AA继承了Thread类,AA类就可以当作线程使用
    //Thread类实现了Runnable接口的run方法
    class AA extends Thread{
        int times = 0;
        @Override
        public void run() {//重写run方法,写上要实现的业务代码
            while(true){
                System.out.println("hello,java" + (++times) + "\t线程名=" + Thread.currentThread().getName());
                //进程---->main线程---->Thread-0
                try {//休眠1秒
                    Thread.sleep(1000);//1000毫秒=1秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(times == 25){//25次退出while,这时线程也退出
                    break;
                }
            }
        }
    
    }
    
    //  演示实现Runnable接口,重写run方法
    public class Thread02 {
        public static void main(String[] args) {
            BB bb = new BB();
            //bb.start(); 这里不能调用 start
            //创建了 Thread 对象,把 bb 对象(实现了Runnable),放入 Thread
            Thread thread = new Thread(bb);
            thread.start();
        }
    }
    class BB implements Runnable { //通过实现 Runnable 接口,创建线程
        int count = 0;
        @Override
        public void run() {//重写run方法
            while (true) {
                System.out.println("hello" + (++count) + "\t线程名=" + Thread.currentThread().getName());
                //休眠 1 秒
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (count == 10) {//10次后线程退出
                    break;
                }
            }
        }
    }
    //  演示 代码模拟 实现Runnable接口 开发线程的机制
    public class Thread03 {
        public static void main(String[] args) {
            CC cc = new CC();
            Proxy proxy = new Proxy(cc);
            proxy.star();
        }
    }
    class Proxy implements Runnable{//把Proxy当作Thread
        private Runnable target = null;//属性,类型为Runnable
    
        @Override
        public void run() {
            if (target != null){
                target.run();
            }
        }
        public Proxy(Runnable target){
            this.target = target;
        }
        public void star(){
            start0();
        }
        public void start0(){
            run();
        }
    }
    class CC implements Runnable{
        @Override
        public void run() {
            System.out.println("hei~");
        }
    }
    /*
        线程终止
        1.线程完成任务时自动终止
        2.设置一个变量控制run方法退出停止线程,即通知方式
     */
    //启动线程t,在main线程去停止线程t
    public class Thread05 {
        public static void main(String[] args) throws InterruptedException {
            T t = new T();
            t.start();
            //修改loop,让t线程退出---->通知方式
            Thread.sleep(2000);//休眠2秒后,通知t线程退出
            t.setLoop(false);
        }
    }
    class T extends Thread{
        private int count = 0;
        //设置一个控制变量loop
        private boolean loop = true;
        @Override
        public void run() {
            while (loop){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程运行中......." + (++count));
            }
        }
        public void setLoop(boolean loop) {
            this.loop = loop;
        }
    }
    /*
        线程常用方法
        setName                 //设置线程名称
        getName                 //获取线程名称
        start                   //开始执行线程
        run                     //调用线程对象run方法
        setPriority             //更改线程优先级
        getPriority             //获取线程的优先级
        sleep                   //在指定毫秒内让当前执行的线程休眠
        interrupt               //中断线程
    
        yield                   //线程礼让,让其他线程先执行,但不一定礼让成功
        join                    //线程插队,先执行完插队的线程的所有任务
    
        细节
        1.start底层会创建新的线程,调用run,run就是一个简单的方法调用,不会启动新线程
        2.线程优先级的范围
            MAX_PRIORITY 10
            MIN_PRIORITY 1
            NORM_PRIORITY 5
        3.interrupt中断线程并没有结束线程,一般用于中断正在休眠的线程
    
     */
    public class Thread06 {
        public static void main(String[] args) throws InterruptedException {
            DD dd = new DD();
            dd.setName("jack");//设置线程名称
            dd.setPriority(Thread.MIN_PRIORITY);//更改线程优先级
            dd.start();//启动子线程
    
            //中断子线程休眠
            System.out.println("中断子线程休眠.....");
            dd.interrupt();
            System.out.println("线程优先级=" + dd.getPriority());
    
            for (int i = 0; i < 10; i++) {
                Thread.sleep(1000);
                System.out.println("主线程" + i);
                if(i == 5){
                    System.out.println("让子线程插队");
                    dd.join();
                    System.out.println("子线程执行完毕,主线程继续执行....");
                }
            }
        }
    }
    class DD extends Thread{
        int count = 0;
        @Override
        public void run() {
            while (true) {
                for (int i = 0; i < 10; i++) {
                    //Thread.currentThread().getName()获取当前线程名称
                    System.out.println("hi\t" + Thread.currentThread().getName() + i);
                    count++;
                }
                if (count > 20){
                    break;
                }
                System.out.println("休眠中.......");
                try {
                    Thread.sleep(10000);//休眠10秒
                } catch (InterruptedException e) {//执行到interrupt方法时,InterruptedException e捕获中断异常
                    System.out.println(Thread.currentThread().getName() + "被中断了");
                }
    
            }
        }
    }
    
    
    /*
        用户线程:也叫工作线程,当线程的任务执行完毕 或 通知方式 后结束
        守护线程:一般是为工作线程服务的,当所有用户线程结束,守护线程自动结束,如:垃圾回收机制
     */
    public class Thread07 {
        public static void main(String[] args) throws InterruptedException {
            FF ff = new FF();
            //当main线程结束后,子线程自动结束---->将子线程设为守护线程
            ff.setDaemon(true);
            ff.start();
            for (int i = 1;i <= 10;i++){
                System.out.println("主线程执行中.....");
                Thread.sleep(1000);
            }
        }
    }
    
    class FF extends Thread{
        @Override
        public void run() {
            for (;;){//无限循环
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("子线程执行中.......");
            }
        }
    }
    /*
        线程的生命周期
        JDK 中用 Thread.State 枚举表示了线程的几种状态
        New                 //尚未启动的线程处于此状态
        Runnable(2种)      //在Java虚拟机执行的线程处于此状态
            Ready               //线程被挂起
            Running             //线程被执行
        Blocked             //被阻塞等待监视器锁定的线程处于此状态
        Waiting             //正在等待另一个线程执行特定动作的线程处于此状态
        Timed_Waiting       //正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
        Terminated          //已经退出的线程处于此状态
    
     */
    public class Thread08 {
        public static void main(String[] args) throws InterruptedException {
            GG gg = new GG();//创建
            System.out.println(gg.getName() + " 状态为 " + gg.getState());//NEW
            gg.start();//启动
    
            while (Thread.State.TERMINATED != gg.getState()) {
                System.out.println(gg.getName() + " 状态为 " + gg.getState());
                Thread.sleep(500);
            }
    
            System.out.println(gg.getName() + " 状态为 " + gg.getState());
        }
    }
    
    class GG extends Thread {
        @Override
        public void run() {
            while (true) {
                for (int i = 0; i < 10; i++) {
                    System.out.println("hi " + i);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                break;//退出
            }
        }
    }
    //模拟三个窗口同时售票,共100张---->同时进行出现超卖现象(出现负数)--->使用Synchronized
    public class Thread04 {
        public static void main(String[] args) {
            SellTicket sellTicket = new SellTicket();
            new Thread(sellTicket).start();//第一个窗口
            new Thread(sellTicket).start();//第二个窗口
            new Thread(sellTicket).start();//第三个窗口
        }
    }
    
    class SellTicket implements Runnable{
        private int ticketNum = 100;//多个线程共享ticketNum
    
        @Override
        public void run() {
            while (true){
                if (ticketNum <= 0){
                    System.out.println("===============票已卖光===============");
                    break;
                }
                //休眠50毫秒
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("窗口" + Thread.currentThread().getName() + "售出一张票,剩余票数=" + (--ticketNum));
            }
        }
    }
    
    /*
        Synchronized
        线程同步机制
        1.在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术
          保证在同一时刻最多只有一个线程访问,以保证数据完整性
        2.线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作
          直到该线程完成操作,其他线程才能对该内存地址进行操作
    
        同步方法
        1.同步代码块
            synchronized (对象){//得到对象的锁,才能操作同步代码
                //需要被同步的代码
            }
        2.放在方法声明种
            public synchronized void m(String name){
                //需要被同步的代码
            }
    
        互斥锁
        1.Java引入了对象互斥锁的概念,来保证共享数据操作的完整性
        2.每个对象对应于一个可称为"互斥锁"的标记,这个标记用来保证在任意时刻,只能有一个线程访问对象
        3.关键字synchronized用来与对象的互斥锁联系,当某个对象用synchronized修饰时,表明该对象在任意时刻只能有一个线程访问
        4.同步的局限性:导致程序的执行效率降低
        5.同步非静态方法的锁可以是this,也可以是其他对象
          同步静态方法的锁为当前类本身,即当前类.class
    
        步骤
        1.分析需要上锁的代码
        2.选择方法:同步方法或同步代码块
        3.要求多个线程的锁对象为同一个
    
        线程死锁
        多个线程占用了对方的锁资源,不肯想让,导致死锁
        如:A:你先去干活,才能给钱.   B:你给我钱,才能干活
    
        释放锁
        1.当前线程的同步方法、同步代码块执行结束
        2.当前线程的同步方法、同步代码块中有break,return
        3.当前线程的同步方法、同步代码块出现了未处理的ERROR或Exception,导致异常结束
        4.当前线程的同步方法、同步代码块执行了wait()方法
    
        注意下列操作不会释放锁:
        1.当前线程的同步方法、同步代码块调用Thread.sleep(),Thread.yield()方法暂停当前线程执行
        2.当前线程的同步代码块执行,其他线程调用了该线程的suspend()方法将线程挂起
    
    
     */
    //解决售票超卖问题
    //使用synchronized互斥锁实现线程同步
    public class Thread09 {
        public static void main(String[] args) {
            SellTicket2 sellTicket2 = new SellTicket2();
            new Thread(sellTicket2).start();//第一个窗口
            new Thread(sellTicket2).start();//第二个窗口
            new Thread(sellTicket2).start();//第三个窗口
        }
    }
    @SuppressWarnings({"all"})
    class SellTicket2 implements Runnable{
        private int ticketNum = 100;//多个线程共享ticketNum
        private boolean loop = true;
        Object object = new Object();//非静态方法的锁可以是this,也可以是其他对象
    //    静态方法中实现同步,锁在类本身 SellTicket2.class
    //    public static void sell2(){
    //    synchornized(SellTicket2.class){}
    //    }
        //public synchronized void sell(){}就是一个同步方法,锁在this对象
        public /*synchronized*/ void sell(){//同步方法
            synchronized (/*this*/object) {//同步代码块,可以是this对象,也可以是其他对象
                if (ticketNum <= 0) {
                    System.out.println("===============票已卖光===============");
                    loop = false;
                    return;
                }
                //休眠50毫秒
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("窗口" + Thread.currentThread().getName() + "售出一张票,剩余票数=" + (--ticketNum));
            }
        }
        @Override
        public  void run() {
            while (loop){
               sell();//调用sell()
            }
        }
    }
    
    
    //  演示死锁
    public class Thread10 {
        public static void main(String[] args) {
    //模拟死锁现象
            DeadLockDemo A = new DeadLockDemo(true);
            A.setName("A 线程");
            DeadLockDemo B = new DeadLockDemo(false);
            B.setName("B 线程");
            A.start();
            B.start();
        }
    }
    
    class DeadLockDemo extends Thread {
        static Object o1 = new Object();// 保证多线程,共享一个对象,这里使用 static
        static Object o2 = new Object();
        boolean flag;
    
        public DeadLockDemo(boolean flag) {//构造器
            this.flag = flag;
        }
    
        @Override
        public void run() {
            //1. 如果 flag 为 T, 线程 A 就会先得到 o1 对象锁, 然后尝试去获取 o2 对象锁
            //2. 如果线程 A 得不到 o2 对象锁,就会 Blocked 阻塞
            //3. 如果 flag 为 F, 线程 B 就会先得到 o2 对象锁, 然后尝试去获取 o1 对象锁
            //4. 如果线程 B 得不到 o1 对象锁,就会 Blocked 阻塞
            if (flag) {
                synchronized (o1) {//对象互斥锁
                    System.out.println(Thread.currentThread().getName() + " 得到o1对象锁");
                    synchronized (o2) {
                        System.out.println(Thread.currentThread().getName() + " 得到o2对象锁");
                    }
                }
            } else {
                synchronized (o2) {
                    System.out.println(Thread.currentThread().getName() + " 得到o2对象锁");
                    synchronized (o1) {
                        System.out.println(Thread.currentThread().getName() + " 得到o1对象锁");
                    }
                }
            }
        }
    }
    
    

     

     

     

  • 相关阅读:
    python 基础知识点(蓝桥杯python科目个人复习计划64)
    《mysql》--mysql约束
    mysql语句locate与substring联合使用方法
    Java基础知识面试题(一)(英语答案)
    负环与差分约束
    七大排序之直接插入排序
    中国居民身份证号码校验算法
    使用反射拼接SQL语句 和 使用 反射 + 注解 拼接SQL语句
    微信小程序连接蓝牙
    MySQL:数据库的物理备份和恢复-冷备份(3)
  • 原文地址:https://blog.csdn.net/m0_72797089/article/details/127386229