• Thread(26)


    1、进程:进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。

    2、线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

    简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。

    3、多线程:具有多个执行代码路径。

    4、Thread类是一个标准的线程对象。两个路径,两个线程,在抢夺CPU资源(时间)。

    5、创建线程步骤:

      1、定义一个类继承Thread。

      2、重写 run 方法。

      3、创建子类对象,就是创建线程对象。

      4、使用start方法,开启线程并让线程执行,同时还会告诉JVM去调用run方法。

    6、定义类实现Runnable接口:

    public class Demo02 {
        public static void main(String[] args) {
            //创建线程执行目标类对象
            Runnable runn = new MyRunnable();
            //将Runnable接口的子类对象作为参数传递给Thread类的构造函数
            Thread thread = new Thread(runn);
            Thread thread2 = new Thread(runn);
            //开启线程
            thread.start();
            thread2.start();
            for (int i = 0; i < 10; i++) {
                System.out.println("main线程:正在执行!"+i);
            }
        }
    }
    class MyThread extends Thread {  //继承Thread
        MyThread(String name){
            super(name);
        }
        //复写其中的run方法
        public void run(){
            for (int i=1;i<=20 ;i++ ){
                System.out.println(Thread.currentThread().getName()+",i="+i);
            }
        }
    }
    class ThreadDemo {
        public static void main(String[] args)     {
            //创建两个线程任务
            MyThread d = new MyThread();
            MyThread d2 = new MyThread();
            d.run();//没有开启新线程, 在主线程调用run方法
            d2.start();//开启一个新线程,新线程调用run方法
        }
    }

    7、 自定义线程执行任务类:

    public class MyRunnable implements Runnable{
    
        //定义线程要执行的run方法逻辑
        @Override
        public void run() {
            
            for (int i = 0; i < 10; i++) {
                System.out.println("我的线程:正在执行!"+i);
            }
        }
    }

     8、为什么需要定一个类去实现Runnable接口呢?继承Thread类和实现Runnable接口有啥区别呢?

    实现Runnable接口,避免了继承Thread类的单继承局限性。覆盖Runnable接口中的run方法,将线程任务代码定义到run方法中。

    创建Thread类的对象,只有创建Thread类的对象才可以创建线程。线程任务已被封装到Runnable接口的run方法中,而这个run方法所属于Runnable接口的子类对象,所以将这个子类对象作为参数传递给Thread的构造函数,这样,线程对象创建时就可以明确要运行的线程的任务。

    9、线程的匿名内部类使用。

    方式1:创建线程对象时,直接重写Thread类中的run方法。

        new Thread() {
                public void run() {
                    for (int x = 0; x < 40; x++) {
                System.out.println(Thread.currentThread().getName()
    + "...X...." + x); } } }.start();

     方式2:使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法。

    Runnable r = new Runnable() {
                public void run() {
                    for (int x = 0; x < 40; x++) {
                        System.out.println(Thread.currentThread().getName()
                                + "...Y...." + x);
                    }
                }
            };
            new Thread(r).start();

    10、线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。

    作用:线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。

    11、  使用线程池中线程对象的步骤:

       1、创建线程池对象。

       2、 创建Runnable接口子类对象。

       3、提交Runnable接口子类对象。

       4、关闭线程池。

    public class ThreadPoolDemo {
        public static void main(String[] args) {
            //创建线程池对象
            ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
            //创建Runnable实例对象
            MyRunnable r = new MyRunnable();
            
            //自己创建线程对象的方式
            //Thread t = new Thread(r);
            //t.start(); ---> 调用MyRunnable中的run()
            
            //从线程池中获取线程对象,然后调用MyRunnable中的run()
            service.submit(r);
            //再获取个线程对象,调用MyRunnable中的run()
            service.submit(r);
            service.submit(r);
    //注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中
    
    //关闭线程池
            //service.shutdown();
        }
    }
    
    
    
    
    //Runnable接口实现类:
    
    public class MyRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("我要一个教练");
            
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("教练来了: " +Thread.currentThread().getName());
            System.out.println("教我游泳,交完后,教练回到了游泳池");
        }
    }

     12、静态方法内不允许直接调非静态的方法。栈内存,都是线程私有的。

    13、Thread.currentThread() 获取当前线程对象,也就是说运行在本类中run 方法所对应的线程对象。方法调用链:Thread.currentThread() .getName() 获取当前线程对象的名字。

    14、Thread对象方法:sleep(毫秒参数a)    ///  线程睡觉a毫秒自动醒来再接着执行代码。   

    15、第二种方式Runnable实现线程的好处:避免了单继承的局限性。更加的符合面向对象。较为常用。实现Runnable接口,将线程任务单独分离出来封装成对象,类型呢就是Runnable接口类型,Runnable接口对线程对象和线程任务进行解耦。还有另一个好处就是:让你的资源能够共享

    public class RunnableDemo implements Runnable{
        
        public RunnableDemo() {
            super();
        }
    
        public void run(){
            for(int i=0;i<1;i++){
                try{
                    Thread.sleep(1000);                
                }catch(Exception ex){}
                System.out.println("runnable"+i);
            }
        }
    }

    16、高内聚自己的事情自己做,自己能做的事情绝不让外面的人去做。  低耦合尽量降低类与类之间的联系性。而接口的应用就恰好帮你完成这件事。

    17、线程的生命周期:new  -  runnable  -  blocked (受阻塞) -  waiting  -  time waiting  -  terminated (结束)

    18、线程生命周期图解:

     19、线程池(缓冲池):

    20、从JDK5以后,就内置了线程池技术,直接使用。

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class ThreadPoll {
        public static void main(String[] args) {
            ExecutorService es = Executors.newFixedThreadPool(2);
            es.submit(new ThreadPollRunnable());
            es.submit(new ThreadPollRunnable());
            es.submit(new ThreadPollRunnable());
        }
    }
     

     21、Runnable接口方法run有两个缺点:1、 无返回值 void。  2、 不能抛异常。   Callable接口就能解决这个问题。

    22、父类有抛出异常,子类可抛可不抛出异常。

    23、泛型不能写基本类型,要写基本类型的包装类,否则会报错误   “Syntax error, insert "Dimensions" to complete ReferenceType”。

    24、双线程求和 Callable接口实现类泛型。

    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    public class ThreadPoll {
        public static void main(String[] args) throws InterruptedException, ExecutionException {
            ExecutorService es = Executors.newFixedThreadPool(2);
            Future<Integer> ft1 = es.submit(new CallableDemo(100));
            Future<Integer> ft2 = es.submit(new CallableDemo(200));
            System.out.println("sum1 "+ ft1.get());
            System.out.println("sum2 "+ ft2.get());
            es.shutdown();
        }
    }
    import java.util.concurrent.Callable;
    
    public class CallableDemo implements Callable<Integer>{
        private int a;
        
        public CallableDemo(int num){
            a = num;
        }
        
        public Integer call(){
            int i = a;
            int sum = 0;
            while(i > 0){
                sum = sum + i;
                i--;
            }
            return sum;
        }
    }

     25、多线程同时操作一个共享数据的时候,往往会出现线程安全问题。(多窗口卖票案例中,判断有票情况下,没减减就被别的线程抢去资源了(失去CPU时间),等获得CPU时间的时候,再执行往下的代码就会出现问题。)

    26、同步代码块解决线程安全问题。  线程共享数据,保证安全,加入同步代码块  synchronized(任意对象){  线程要操作的共享数据 }  同步代码块    =》 线程安全就意味着运行速度必然降低。牺牲速度来保证安全!   安全问题的前提条件是:多线程,操作共享数据。

    27、同步保证安全性:同步锁(对象监视器)的原理:

    28、同步方法:  StringBuffer类(线程安全)的方法都是同步方法 synchronized.  而它兄弟 StringBuiilder是线程不安全的,类中方法没有同步方法。

    public class Tickets implements Runnable {
        private int ticket = 100;
        
        public void run (){
            while(true){
                saleTicket();
            }
        }
        
        public synchronized void saleTicket(){
            if(ticket > 0){
                try{
                    Thread.sleep(10);
                }catch(Exception ex){}
                System.out.println(Thread.currentThread().getName()+"售出第"+ ticket-- + "张票~");                
            }
        }
    }

    29、静态优先于非静态的存在。静态方法内不能引用非静态的成员变量等。

    30、静态方法的同步锁是:本类类名.class       非静态方法的同步锁就是 this 

    31、JDK1.5新特性  Lock  比 synchronized  更加地灵活, 获取锁和释放锁地操作都能看见。

    32、线程的死锁原理:(两人抓头发,你等着我松手,我等着你松手)

     代码实现:

    public class DeadLock implements Runnable{
        private int i = 0;
        
        public void run(){
            while(true){
                if(i % 2 == 0){
                    //先进入A同步,再进入B同步
                    synchronized(LockA.locka){
                        System.out.println("if...locka");
                        synchronized(LockB.lockb){
                            System.out.println("if...lockb");
                        }
                    }
                }else{
                    //先进入B同步,再进入A同步
                    synchronized(LockB.lockb){
                        System.out.println("else...lockb");
                        synchronized(LockA.locka){
                            System.out.println("else...locka");
                        }
                    }
                }
                i++;
            }
        }
    }

     

     33、线程等待与唤醒案例安全解决:一切为了不出现人妖问题。   问题产生原因:赋值时被抢夺了CPU时间,导致后半部分的赋值未完成就被打印值

    解决方案1:synchronized(唯一的对象){ 共享数据代码块 }

    解决方案2:使用线程间的通信方法 .wait() 与 .notify()

    ====》 .wait()  与  notify()  方法。

  • 相关阅读:
    团队冲刺--第二阶段(五)
    团队冲刺--第二阶段(四)
    团队冲刺--第二阶段(三)
    团队冲刺--第二阶段(二)
    团队冲刺--第二阶段(一)
    第一阶段意见评论
    人月神话阅读笔记02
    基础-定位
    基础-颜色
    标准文档流
  • 原文地址:https://www.cnblogs.com/ivan5277/p/10038619.html
Copyright © 2020-2023  润新知