• Android多线程研究(4)——从一道面试题说起


    有一道这种面试题:开启一个子线程和主线程同一时候运行,子线程输出10次后接着主线程输出100次,如此重复50次。先看以下代码:

    package com.maso.test;
    
    /**
     * 
     * @author Administrator
     * 两个线程,当中是一个主线程,第一个线程先运行输出10次,主线程接着运行输出100次,如此重复50次
     */
    public class ThreadTest3 implements Runnable{
    	private static Test test;
    	@Override
    	public void run() {
    		for(int i=0; i<50; i++){
    			test.f1(i);
    		}
    	}
    	
    	public static void main(String[] args) {
    		test = new Test();
    		new Thread(new ThreadTest3()).start();
    		for(int i=0; i<50; i++){
    			test.f2(i);
    		}
    	}
    	
    	/**
    	 * 将控制和逻辑及数据分类(该类就是数据)
    	 * @author Administrator
    	 *
    	 */
    	static class Test{
    		private boolean isf1 = true;
    		/**
    		 * 输出10次
    		 */
    		public synchronized void f1(int j){
    			if(!isf1){
    				try {
    					this.wait();
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    			for(int i=1; i<=10; i++){
    				System.out.println(Thread.currentThread().getName() + "第" + j + "次轮巡,输出" + i);
    			}
    			isf1 = false;
    			notify();
    		}
    		
    		/**
    		 * 输出100次
    		 */
    		public synchronized void f2(int j){
    			if(isf1){
    				try {
    					this.wait();
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    			for(int i=1; i<=100; i++){
    				System.out.println(Thread.currentThread().getName() + "第" + j + "次轮巡,输出" + i);
    			}
    			isf1 = true;
    			notify();
    		}
    	}
    }
    
    上面推断用的是if语句,这样做看似没有什么问题,实际上这样做是不安全的,由于线程在等待的过程中有可能被假唤醒,所以我们须要使用while语句。另外在使用wait和notify的时候须要注意一下几点:

    1、调用object的wait方法和notity方法时,必须先获得object的对象锁(必须写在synchronized中)。

    2、假设调用了object的wait方法,则该线程就放掉了对象锁。

    3、假设A1、A2、A3都在object.wait(),则B调用object.notify()仅仅能唤醒A1、A2、A3中的一个(详细哪一个由JVM决定)

    4、object.notifyAll()能够唤醒所有。

    5、B在唤醒A的时候,B假设还持有对象锁,则要等到B释放锁后,A才有机会运行。

    Sleep和Wait有什么差别?

    sleep()并不释放对象锁,wait()释放对象锁。可是wait()和sleep()都能够通过interrupt()方法打断线程的暂停状态,从而使线程立马抛出InterruptedException。假设线程A希望马上结束线程B,则能够对线程B相应的Thread实例调用interrupt方法。假设此刻线程B正在wait/sleep/join,则线程B会立马抛出InterruptedException,在catch() {} 中直接return就可以安全地结束线程。须要注意的是,InterruptedException是线程自己从内部抛出的,并非interrupt()方法抛出的。对某一线程调用interrupt()时,假设该线程正在运行普通的代码,那么该线程根本就不会抛出InterruptedException。可是,一旦该线程进入到wait()/sleep()/join()后,就会立马抛出InterruptedException。

    以下我们来看看线程的生命周期:


    实现线程调度的方法例如以下:

    1、sleep():该线程是让线程休眠一定的时间,须要捕获InterruptedException

    2、yield():暂停当前线程,让同等级优先权的线程运行,假设没有同等级优先权线程则不会起作用。起作用后会让出CPU运行时间,进入就绪状态。

    3、join():让一个线程等待调用join方法的线程运行完成后再继续运行。


    看一段代码:

    public class ThreadTest4 implements Runnable{
    	private static int a = 0;
    	@Override
    	public void run() {
    		for(int i=0; i<10; i++){
    			a++;
    		}
    	}
    
    	public static void main(String[] args) {
    		new Thread(new ThreadTest4()).start();
    		System.out.println(a);
    	}
    }
    这段代码会输出10吗?答案是不会的,由于在启动子线程后,就立马输出了a的值,此时子线程对a还没有操作。改动例如以下:

    public class ThreadTest4 implements Runnable{
    	private static int a = 0;
    	@Override
    	public void run() {
    		for(int i=0; i<10; i++){
    			a++;
    		}
    	}
    
    	public static void main(String[] args) throws InterruptedException {
    		Thread t = new Thread(new ThreadTest4());
    		t.start();
    		t.join();
    		System.out.println(a);
    	}
    }
    这回输出了10,join()方法的作用由此可见,它会让其它线程等待该线程运行完成后再运行。






  • 相关阅读:
    linux 学习之七-部分ssh命令
    揭秘淘宝286亿海量图片存储与处理架构
    大型网站后台架构的演变
    Linux学习之六-Yum命令的使用
    res://ieframe.dll/acr_error.htm 纯手动解决方法
    VS2013启动项目调试的时候会启动本地IIS
    linux学习之(六)-主机名、网络IP的配置与查看
    deployment.yaml 带同步时区
    Deployment
    mysql.yaml
  • 原文地址:https://www.cnblogs.com/yxwkf/p/3847474.html
Copyright © 2020-2023  润新知