自己总结的java线程,交流学习

    技术2022-07-10  122

    什么是线程

    线程是操作系统能够进行运算调度的最小单位,一个程序运行就是一个进程,一个进程包括至少一个线程  

    线程和进程有什么区别

    线程是进程的子集;进程占据较多的系统资源,线程仅占用一些必不可少的系统资源;进程之间的内存空间是独立的,线程之间是共享的;进程的上下文切换代价较大,线程之间的切换代价很小。  

    实现Runnable接口和继承Thread接口的区别

      1、首先应当明白无论是何种方式,最后都是通过Thread.start方法开启的线程(注意,Runnable接口的run方法并不是开启了新的线程) 2、实现Runnable接口构建线程时,需要以此接口的实现类做为真正线程类的构造方法中的构造参数 Thread thread = new Thread(runnable,"Runnable接口实现线程"); 继承Thead类时,由于没有Runnable接口的实现类,自然使用无参构造函数 创建线程类 Thread thread = new Thread();//使用无参构造方法创建线程 因此,两种方式构造线程时的区别在于线程之间的资源是否能够共享。资源共享如果不太清楚可以参考这个博主的文章 https://www.cnblogs.com/fxust/p/8998696.html 3、最明显的区别自然就是实现接口可以有更好的扩展性了。  

    synchronized关键字

    大家一直习惯称它为同步,我觉得将之称为按照顺序执行更合适,线程是具有并发性的,因此两个线程一起运行的时候可能会出现数据错误的情况,比如下面,我每次执行线程时同时执行一个sleep方法,模拟线程执行较慢的情况(注意:在加锁的情况下,执行sleep方法不会释放锁哦); public class Test implements Runnable{     private int i=0;     @Override     public void run() {        while (i<5){             System.out.println(Thread.currentThread().getName()+"卖出的票号为:"+i);             try {                 Thread.sleep(1);             }catch (Exception e){                 e.printStackTrace();             }            i=i+1;         }     }     public static void main(String[] args) {         Runnable runnable = new Test();         Thread thread1 = new Thread(runnable,"线程一");//使用无参构造方法创建线程         Thread thread2 = new Thread(runnable,"******线程二");         thread1.start();         thread2.start();     } }

     

      运行结果如下:   可以看到,我卖出了两张票号为0的票,自然就出了错误,为了避免这种错误,我们可以引入synchronized关键字   如下 public synchronized void run() {     System.out.println(Thread.currentThread().getName()+"想要售卖票------");    while (i<5){         System.out.println(Thread.currentThread().getName()+"卖出的票号为:"+i);         try {             Thread.sleep(1);         }catch (Exception e){             e.printStackTrace();         }        i=i+1;     } }

     

    如下我们可以看到,不会卖出错乱重复的票了   synchronizedTest可以在普通方法,静态方法,代码块中使用。作用在普通方法中时,锁的是当前实例对象;作用在静态方法时,锁的是这个类(静态方法属于类,仅此一个嘛); 作用在代码块时,代码块中是什么,自然锁的就是什么,如下: 锁的是当前实例对象 public void run() {     System.out.println(Thread.currentThread().getName()+"想要售卖票------");     synchronized (this){         while (i<5){             System.out.println(Thread.currentThread().getName()+"卖出的票号为:"+i);             try {                 Thread.sleep(1);             }catch (Exception e){                 e.printStackTrace();             }             i=i+1;         }     } }

     

    也可以锁当前这个类   public void run() {     System.out.println(Thread.currentThread().getName()+"想要售卖票------");     synchronized (Test.class){         while (i<5){             System.out.println(Thread.currentThread().getName()+"卖出的票号为:"+i);             try {                 Thread.sleep(1);             }catch (Exception e){                 e.printStackTrace();             }             i=i+1;         }     } }

     

     

    锁的概念

    这里的锁的概念我刚开始一直不太明白,说下我自己的理解。首先个人认为锁有两个关键点,锁了谁,锁了哪里。   举个例子,交警叔叔在立水桥路口查未佩戴头盔骑电动车的人,不符合要求的不允许通行,这其实就是一个锁。锁了谁呢,骑电动车未佩戴头盔的人;锁了哪里呢,立水桥路口。 那么如果我没骑电动车且没佩戴头盔,那交警叔叔也不会查处我的,因为我不是锁的对象(锁普通方法时,仅锁当前实例对象,再生成一个实例对象时,不会上锁); 如果我骑了电动车还未佩戴头盔,那么我可以不走立水桥路口啊,因为别的地区没上锁(锁代码块时,代码块之外的代码仍然可以被线程并发访问) 如果我骑了电动车还带着头盔自然可以从立水桥通过,因为我是获得了资源的。 如果我骑了电动车未佩戴头盔还从立水桥过,那么你未获得权限,成功被拦截,不允许通行(不允许访问上锁的资源)  

    start、run、join、yield方法的区别

    run方法只是Runnable接口的一个普通方法,运行它并未开启真正的线程 start方法是Thread类的方法,运行它才是开启了线程,开启后会调用run方法 join方法是在当前线程调用另一个线程的join方法,那么会阻塞当前线程,直到另一个线程执行完毕,才会继续执行当前线程 如下,首先main方法也是一个线程,在不加thread1.join()的情况下,由于线程的并发性,那么打印最后打印出来的i的值很可能为0,加上thread1.join()后,则先执行thread1这个线程,再执行main方法的线程,最后打印的数据为5 public class Test implements Runnable{     private int i=0;     @Override     public void run() {         while (i<5){             i=i+1;         }     }     public static void main(String[] args) throws Exception{         Runnable runnable = new Test();         Thread thread1 = new Thread(runnable,"线程一");//使用无参构造方法创建线程         thread1.start();         thread1.join();         System.out.println(((Test) runnable).i);     } }

     

    yield是Thread类的一个静态方法,会让当前线程交出cpu资源,让线程重新竞选执行机会,如下,如果去掉了Thread.yield(),如果计算机运行速度较快的话,则可能一直是线程一运行直至运行完毕,加上此方法后,则使线程二在每次i++之后都能够有获取cpu资源的机会 public class Test implements Runnable{     private int i=0;     @Override     public void run() {         while (i<10){             System.out.println(Thread.currentThread().getName()+"卖出的票号为:"+i);             i++;             Thread.yield();         }     }     public static void main(String[] args) {         Runnable runnable = new Test();         Thread thread1 = new Thread(runnable,"线程一");//使用无参构造方法创建线程         Thread thread2 = new Thread(runnable,"******线程二");         thread1.start();         thread2.start();     } }

     

     

    守护线程

    守护线程和普通线程的创建没有太大区别,在生成一个线程实例的时候,调用setDaemon(true)即可设置为守护线程,注意需要在start方法之前。 当一个进程内的非守护线程执行完成后,无论有没有正在执行的守护线程,JVM都会停止。              
    Processed: 0.013, SQL: 9