• 线程的基本操作(读书笔记)


    • 新建线程
         新建线程很简单,只要使用new关键字创建一个线程对象.并且将它start()起来即可.那么线程start()后,会干什么呢?这才是问题的关键.线程Tread,有一个run()方法.start()方法就会新建一个线程并让这个线程执行run()方法
    Thread t1 = new Thread();
    t1.start();
     
    这里要注意 下面的代码也能通过编译,也能正常运行.但是 却不能新建一个线程,而是在当前线程中调用run方法,只是当做一个普通方法调用
    Thread t2 = new Thread();
    t1.run();
    注意:不要用run()方法来开启新线程.他只会在当前线程中,串行执行run()中的代码
     
    创建线程的两种方法:
    Thread t1 = new Thread() {
        @Override
        public void run() {
            System.out.println("Hello,I am 他");
        }
    };
    t1.start();
    //Java8 版本
    Thread t2 = new Thread(() -> System.out.println("Hello,I am 他"));
    t2.start();
    
    public class CreateThread implements Runnable {
    
        public static void main(String[] args) {
            Thread t1 = new Thread(new CreateThread());
            t1.start();
        }
    
        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see Thread#run()
         */
        @Override
        public void run() {
            System.out.println("Oh, I am Runnable");
        }
    }
    下面的方法是最常用的!
    • 终止线程
    在jdk中 我们发现Thread提供了一个stop()方法,但是stop方法却被废弃了.原因是这个方法太过去暴力了.强行把执行到一半的线程终止.可能会引起一些数据不一致的问题.
    因为他释放了这个线程所持有的所有锁,而这些锁恰恰是用来维持一个对象一致性的.例如写数据写到一半,并强行终止.那么对象就会被写坏.同时 由于锁已经被释放了,另一个等待的读线程顺理成章的读到了这个不一致的对象,悲剧也就发生了!
    那如果需要停止一个线程时,其实方法很简单,.只需要由我们自行决定线程何时退出就可以了.
    public class CreateThread implements Runnable {
    
        volatile boolean stopme = false;
    
        public void stopMe() {
            stopme = true;
        }
        @Override
        public void run() {
            while (true) {
                if (stopme) {
                    System.out.println("exit by stop me");
                    break;
                }
                synchronized (u) {
                    int v = (int) (System.currentTimeMillis() / 1000);
                    u.setId(v);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    u.setName(String.valueOf(v));
                }
                Thread.yield();
            }
        }
    }
    • 线程终端 在Java中 线程中断是一种重要的线程协作方式,从表面理解,中断就是让目标线程停止执行的意思,实际上并非完全如此, 
    严格意义上说, 线程中断并不是线程立即退出,而是给线程发送一个通知,告知目标线程,有人希望你退出啦.至于目标线程得到通知后如何处理,则完全由目标线程自行决定,这点很重要!
         与线程中断有关的,有三个方法,

    Thread t1 = new Thread(new CreateThread());
    t1.interrupt();//中断线程 实例方法
    t1.isInterrupted();//判断是否中断 实例方法
    Thread.interrupted();//判断是否中断,并清除当前中断状态 静态方法
    
    public class CreateThread implements Runnable {
    
    
        @Override
        public void run() {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("Interrupted");
                    break;
                }
                try {
                    Thread.sleep(2000);//实例线程 休眠2s  当线程休眠中被中断 抛出InterruptedException 异常
                } catch (InterruptedException e) {
                    System.out.println("Interrupted When Sleep");
                    Thread.currentThread().interrupt();
                }
                Thread.yield();
            }
        }
    
    
        public static void main(String[] args) throws InterruptedException {
    
            Thread t1 = new Thread(new CreateThread());
            t1.start();
            Thread.sleep(1000);//main 休眠1s 后执行中断
            t1.interrupt();
        }
    }
    • 等待(wait)和通知(notify)
    为了支持多线程间的协作,JDK提供了两个非常重要的接口线程等待wait()方法和通知notify()方法,这两个方法并不是在Thread类中的 而是在Object类中,这意味着任何对象都可以调用这两个方法.
     
         当在一个对象实例上调用wait()方法后,当前先吃就会在这个对象上等待,比如线程A中,调用了obj.wait()方法,那么线程A就会停止继续执行,而转为等待状态,等待何时结束呢?线程A会一直等到其他线程调用了obj.notify()方法为止,这时,obj对象就俨然成为了多线程之间通信的有效手段.
         那wait()和notify()究竟是如何工作的呢?如果一个线程调用了object.wwait() 那么他就会进入object对象的等待队列,这个等待队列中,可能会有多个线程,因为系统运行多个线程同时等待一个对象.当object.notify()被调用时,他就会从这个等待队列中,随机选择一个线程,并将其唤醒,这个选择是不公平的,并不是先等待的线程就会优先选择,这个选择是完全随机的.
         除了notify()方法外,Object对象还有一个类似的notifyAll()方法,.他和notify()的功能基本一样,区别在于他会唤醒等待队列中所有等待的线程,而不是随机的一个,
         这里需要注意,Object.wait()方法并不是可以随便调用的.他必须包含在对应的synchronized语句中.无论是wait()和notify()都需要首先获得目标对象的一个监听器,
    public class SimpleWN {
        final static Object object = new Object();
    
        public static class T1 extends Thread {
            public void run() {
                synchronized (object) {
                    System.out.println(System.currentTimeMillis() + ":T1 start!");
                    try {
                        System.out.println(System.currentTimeMillis() + ":T1 wait for object ");
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(System.currentTimeMillis() + ":T1 end! ");
                }
            }
        }
    
        public static class T2 extends Thread {
            public void run() {
                synchronized (object) {
                    System.out.println(System.currentTimeMillis() + ":T2 start! notify one thread ");
                    object.notify();
                    System.out.println(System.currentTimeMillis() + ":T2 end! ");
    
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        public static void main(String[] args) {
            Thread t1 = new T1();
            Thread t2 = new T2();
            t1.start();
            t2.start();
        }
    }
     
         上述代码 开启了2个线程T1和T2,T1执行了wait(),注意 T1在执行等待之前,现获取了object对象锁,然后执行等待 T1会停下来等待,并释放对象锁,T2在执行notify()方法之前 获取object对象锁后,执行notify(),为了让效果明显.我们特意T2执行notify()后休眠2秒,,这样做可以明显了看出,T1得到通知后,还会先尝试重新获取对象锁,
     
    注意 Object.wait()和Thread.sleep()两个方法都可以让线程等待若干时间.除了wait()可以被唤醒外,wait()执行完会释放目标对象的锁,而sleep()不会释放任何资源!
     
    • 等待线程结束(join)和谦让(yield)
    很多时候 一个线程的输入可能非常依然于另一个或者多个线程的输出.此时 这个线程就需要等待依赖线程执行完毕,才能继续执行,JDK提供了join()操作来实现这个功能,
    public final synchronized void join(long millis)throws InterruptedException
    public final void join() throws InterruptedException
    第一个方法给出了一个最大等待时间,如果超出给定时间目标线程还在执行,当前线程也会因为等待不及了,而继续执行下去 第二方法表示无线等待.它会阻塞当前线程,知道目标线程执行完毕.
     下面提供一个简单的join实例,
    public class JoinMain {
        public volatile static int i = 0;
    
        public static class AddThread extends Thread {
            public void run() {
                for (i = 0; i < 10000000; i++) ;
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            AddThread at = new AddThread();
            at.start();
            at.join();
            System.out.println(i);
        }
    }
     主函数中,如果不是用join()等待AddThread, 那么得到的i 很可能是0或者一个非常小的数字,但在join()后,表示主线程愿意等待AddThread执行完毕,跟着AddThread一起往前走,故在join()返回时.AddThread已经执行完了.故i 永远是10000000.
    注意 不要在应用程序中 在Thread对象实例上使用类似wait()或者notify等方法, 因为折痕有可能会影响系统API的运行,或者被系统API所影响!
     
    另外一个比较有趣的方法是Thread.yield();
    public static native void yield();
    这是一个静态方法,一旦执行,它会使当前线程让出CPU.当时注意,让出CPU并不代表当前线程不执行了.当前线程让出CPU后,还会进行CPU资源的争夺,但是是否能够再次被分配到,就不一定了,因此 对Thread.yield()的调用就好像在说:我已经完成一些最重要的工作 了.我应该休息一下了.可以给其他线程一些工作机会了!
         如果你觉得一个线程不那么重要,或者优先级很低,而且害怕他会占用太多CPU资源,那么可以适当的时候调用这个方法,.给予其他重要的线程更多的工作机会!
    • volatile Java使用了一些特殊的操作或者关键字来申明,告诉虚拟机,在这个地方,要尤其注意,不能随意变动优化目标指令. 易变得 不稳定的.
    当你用volatile声明一个变量时,就等于告诉虚拟机,这个变量极有可能会被某些程序或者线程修改.为了确保可见性,虚拟机做了特殊的手段,保证这个变量的可见性!
  • 相关阅读:
    Python04 range()方法的使用、turtle.textinput()方法和write()的使用、turtle.numinput()的使用
    SpringBoot12 QueryDSL02之利用QueryDSL实现多表关联查询
    SpringBoot12 QueryDSL01之QueryDSL介绍、springBoot项目中集成QueryDSL、利用QueryDSL实现单表RUD、新增类初始化逻辑
    ES02 变量、数组、对象、方法
    JavaScript问题01 js代码放在header和body的区别
    Python03 字符串类型、强制类型转化、列表、元组、字典、集合
    Python02 标准输入输出、数据类型、变量、随记数的生成、turtle模块详解
    使用 Multipath TCP 为 iOS 创建备份连接(转)
    linux中的条件变量
    linux中网络编程<1>
  • 原文地址:https://www.cnblogs.com/ten951/p/6171006.html
Copyright © 2020-2023  润新知