zoukankan      html  css  js  c++  java
  • 多线程基础

    一、线程运行状态

    Java内存模型(JMM)是围绕着多线程的原子性、可见性、有序性来建立的。

    原子性:是指一个操作是不可中断的。java内存模型直接保证的原子性操作包括read、load、assign、use、store、write,synchronized块之间的操作也具备原子性。

    可见性:是指当一个线程修改了某一个共享变量的值,其他线程是能立即知道这个修改。volatile、synchronized和final能够实现可见性。普通变量与volatile变量的区别是,volatile保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。final关键字的可见性是指:被final修饰的字段在构造函数中一旦初始化完成,并且构造器没有把“this”的引用传递出去,那在其他线程中就能看见final字段的值。

    有序性:有序性问题的原因是因为程序在执行时,可能会进行指令重排,重排后的指令与原指令的顺序未必一致。volatile、synchronized能保证线程之间的有序性。

    二、Thread类方法解析

    2.1 start()方法(不是run()方法)

    start()方法会新建一个线程并让这个线程执行run()方法。

    2.2 stop()方法

    一般来说,线程执行完毕后就会结束。stop方法会直接终止一个线程,并且会立即释放这个线程所持有的锁。太过于暴力,可能会引起数据不一致的问题,不建议使用。

    2.3 sleep(long millis)

    让线程休眠多少毫秒,让出CPU资源。当休眠结束后,不一定立即获取CPU执行,是由阻塞进入就绪,重新竞争CPU。

    注意:当线程休眠时,如果调用interrupt()方法会产生一个中断异常InterruptedException。

    2.4 线程中断 interrupt()

    线程中断并不会使线程立即退出,而是给线程发送一个通知,告知线程可以退出了,至于线程怎么处理,完全有线程决定。若设置了中断标志,但是线程没有判断中断标志并处理,线程也不会退出。

    interrupt()  //设置线程中断标志

    isInterrupted() // 判断是否被中断

    interrupted() // 判断是否被中断,并清除中断标志

    public static void main(String[] args) {
    		
    		Runnable target = new Runnable() {	
    			@Override
    			public void run() {
    				while(true){
    					System.out.println(Thread.currentThread().getName()+" runing ");
    					if(Thread.currentThread().isInterrupted()){
    						System.out.println("当前线程被设置的中断标志,手动结束线程");
    						break;
    					}
    					try {
    						Thread.sleep(2000);
    						//if(出现某种错误){
    							//当出现某种错误,需要当前线程退出时,可以设置线程中断
    							//Thread.currentThread().interrupt();
    						//}
    						System.out.println("线程是否设置中断标志:"+Thread.currentThread().isInterrupted());
    						
    					} catch (InterruptedException e) {
    						System.out.println("在休眠的时候中断操作,抛出中断异常,线程是否设置中断标志:"+Thread.currentThread().isInterrupted());
    						Thread.currentThread().interrupt();
    					}
    				}
    			}
    		};
    		Thread t1 = new Thread(target);
    		t1.start();
    		
    	}
    

    2.5 等待(wait)和通知(notify)

    这两个方法不是Thread类中的,是Object类中的。

    如果一个线程调用了object.wait(),那么它就进入object对象的等待队列,等待被唤醒。在等待队列中,可能会有多个线程,当object.notify()被调用时,会随机选择一个线程,将其唤醒,notifyAll()方法会唤醒所有的线程。

    注意:wait和sleep一样,在wait时发生中断,也会抛出中断异常

    2.6 挂起(suspend)和继续执行(resume)

    被废弃,不推荐使用。因为suspend在导致线程暂停的同时,并不会去释放任何资源。其他线程想要访问被它暂用的锁时,导致无法继续运行。直到进行了resume操作,才能继续。可以使用wait和notify实现其功能。

    2.7 等待线程结束(jion)和谦让(yield)

    join()

    jion(long millis)

    yield() 方法会让当前线程让出CPU,进入就绪状态,但还会进行CPU的争夺。

    第一个jion表示无限等待,它会一直阻塞当前线程,直到目标线程(谁调用jion谁是目标线程)执行完毕。第二个join给出了一个最大等待时间,如果超过给定时间目标线程还在执行,当前线程也会继续往下执行。

    Thread t0 = new Thread(()->{
      for(int i=0;i<100;i++){
        System.out.println(Thread.currentThread().getName()+"执行");
      }		
    });
    Thread t1 = new Thread(()->{
      for(int i=0;i<100;i++){
        System.out.println(Thread.currentThread().getName()+"执行");
      }
    });
    t0.start();
    t1.start();
    try {
      t0.join();
      t1.join();
    } catch (InterruptedException e) {			
      e.printStackTrace();
    }
    System.out.println("主线程执行");
    

    当没有执行t0.jion()和t1.jion()结果如下:

    可以看到主线程抢到CPU先执行,然后t0和t1交替执行。

    当执行t0.jion()和t1.jion()结果如下:

    可以看到是t0和t1先交替执行,然后主线程再执行,所以当目标线程加上join后,主线程会等目标线程执行结束后再执行。

    注意:当仅仅执行t0.jion()时,效果也是这样,join阻塞的是当前线程,其他线程不干扰。其原理是目标线程让当前线程(只是当前线程)进行wait()等待,当目标线程执行完成后,被等待的线程会在退出前调用notifyAll()唤醒所有线程。

    2.8 守护线程

    守护线程是系统的守护者,在后台默默完成一些服务,当用户线程全部结束后,守护线程也会结束。

    Thread t0 = new Thread(target);
    t0.setDaemon(true);
    t0.start();
    // 主线程的操作 ...
    

    当前线程只有用户线程(主线程)和t0,t0设置为守护线程,当主线程执行结束后,t0也会结束;若不设置的话,t0会一直执行。

  • 相关阅读:
    ウェブプロジャクトがジャワープロジャクトに参照する方法
    web.xml 中的listener、 filter、servlet 加载顺序及其详解
    财富定律
    jsp文件下载完整方法
    使用Axis开发Web Service程序
    CDH安装指南——酒仙网技术
    linux下 putty 的痛苦编译之路
    博客园
    windows 下 cannal & otter 配置
    Go 1.8 正式发布,编译时间比 Go 1.7 提高约 15%
  • 原文地址:https://www.cnblogs.com/wwzyy/p/10090992.html
Copyright © 2011-2022 走看看