zoukankan      html  css  js  c++  java
  • 线程技术讨论

    一:线程的概念

    多线程是个有意思的概念,一个应用程序,比如酷我音乐或者某个游戏,是一个进程,然后cpu给该进程

    分配内存空间,如果电脑内存空间有限,而且运行的程序比较多时就会比较卡,一个进程可能有多个线程

    执行,比如游戏中,多个任务,建筑物等都是线程在操作,在单核处理器的情况下,是并发执行的,cpu给

    每个线程随机分配时间片,得到时间片的那个任务执行,由于切换的频率比较快,所以用户看起来是一起

    执行的。在多核处理器的情况下,是并行的,确实可以一起执行,所以多线程的好处就是不必考虑电脑是否多核,

    确实可以提高执行效率。

    下面看一个简单的例子如下:

    //实现倒计时功能
    class CountDown implements Runnable{
    	
    	private int countDown = 10; //用来计数
    	
    	public static int taskCount = 0; //用来标记实例,从零开始
    	
    	public final int id = taskCount++; //定义为final类型,不可修改
    	
    	public CountDown(){}
    	
    	public CountDown(int countDown){
    		this.countDown = countDown;
    	}
    	
    	public String getStatus(){
    		return "#"+id+ "("+(countDown>0?countDown:"liftOff!")+")";
    	}
    	
    	@Override
    	public void run() {
    		while(countDown-->0){
    			System.out.println(getStatus());
    		}
    		
    	}
    }
    

    实现一个简单的倒计时功能,countDown用来计数,id用来标记访问实例,在main方法中调用run:

    public class TestThread2 {
    	public static void main(String[] args) {
    		CountDown countDown = new CountDown();
    		countDown.run();
    	}
    }
    

    执行结果:

    #0(9)
    #0(8)
    #0(7)
    #0(6)
    #0(5)
    #0(4)
    #0(3)
    #0(2)
    #0(1)
    #0(liftOff!)
    

    顺序执行,实际上上面的执行并没有涉及到线程,计数的执行都是在main线程中执行的,如果想实现多线程执行,就必须

    把上面的任务依附到某个线程上。

    二:创建线程的两种方式

    1:继承Thread

    // 创建线程方式一
    class MyThread extends Thread {
    	public void run() {
    		for (int index = 0; index < 100; index++) {
    			System.out.println(Thread.currentThread().getName() + ":::" + index);
    		}
    	}
    }
    
    public class TestThread1 {
    	
    	public static void main(String[] args) {
    		Thread thread = new MyThread();
    		thread.start();
    		for (int index = 0; index < 10; index++) {
    			System.out.println(Thread.currentThread().getName() + ":::" + index);
    		}
    	}
    	
    }
    
    Thread-0:::0
    Thread-0:::1
    Thread-0:::2
    main:::0
    Thread-0:::3
    Thread-0:::4
    Thread-0:::5
    Thread-0:::6
    Thread-0:::7
    Thread-0:::8
    Thread-0:::9
    main:::1
    main:::2
    main:::3
    main:::4
    main:::5
    main:::6
    main:::7
    main:::8
    main:::9
    

    从运行结果可以看出,main线程和Thread0线程随机获取cpu时间片执行任务!

    //创建线程方式二
    class MyRun implements Runnable{
    	
    	@Override
    	public void run() {
    		for (int index = 0; index < 100; index++) {
    			System.out.println(Thread.currentThread().getName() + ":::" + index);
    		}
    	}
    }
    
    public class TestThread1 {
    	
    	public static void main(String[] args) {
    		Runnable run = new MyRun();
    		Thread thread = new Thread(run);
    		thread.start();
    		for (int index = 0; index < 10; index++) {
    			System.out.println(Thread.currentThread().getName() + ":::" + index);
    		}
    	}
    	
    }
    
    main:::0
    Thread-0:::0
    Thread-0:::1
    main:::1
    Thread-0:::2
    Thread-0:::3
    main:::2
    Thread-0:::4
    Thread-0:::5
    main:::3
    Thread-0:::6
    main:::4
    main:::5
    main:::6
    main:::7
    Thread-0:::7
    Thread-0:::8
    Thread-0:::9
    main:::8
    main:::9
    

    运行结果类似上面,所以也是多线程执行

    三:线程池的使用

    如果需要执行主线程之外的线程时,可以用上面的方式创建线程,但是如果并发的任务比较多时,频繁的创建和销毁线程会消耗过多的资源

    所以最好的选择是使用线程池,在使用的时候由线程池分配,使用完成由线程池回收掉

    1:没有返回值的线程池

    newCachedThreadPool 、newFixedThreadPool、newSingleThreadPool这是几种常见的线程池,第一种是线程池是无界的,但是内存

    的资源是有限的,一般会有限制,第二种是固定长度线程池,在创建线程池的时候就指定线程的长度,第三种只会创建一个线程,如果第二种

    的参数为1就是第三种

    public static void main(String[] args) {
    		ExecutorService service = Executors.newCachedThreadPool();
    		for(int i=0;i<5;i++){
    			service.execute(new CountDown());
    		}
    		service.shutdown();
    	}
    
    #0(9)
    #0(8)
    #0(7)
    #0(6)
    #0(5)
    #0(4)
    #0(3)
    #0(2)
    #0(1)
    #0(liftOff!)
    #2(9)
    #2(8)
    #1(9)
    #1(8)
    #1(7)
    #1(6)
    #1(5)
    #1(4)
    #1(3)
    #1(2)
    #1(1)
    #1(liftOff!)
    #4(9)
    #4(8)
    #4(7)
    #4(6)
    #4(5)
    #4(4)
    #4(3)
    #4(2)
    #4(1)
    #4(liftOff!)
    #2(7)
    #2(6)
    #2(5)
    #2(4)
    #2(3)
    #2(2)
    #2(1)
    #2(liftOff!)
    #3(9)
    #3(8)
    #3(7)
    #3(6)
    #3(5)
    #3(4)
    #3(3)
    #3(2)
    #3(1)
    #3(liftOff!)
    

    从运行结果可以看出,随机放入5个执行任务到线程池中,然后这5个任务随机抢夺cpu的执行权

    newFixedThreadPool和newCacheThreadPool的用法比较类似,cache是第一选择,如果有问题可以选择fixed,对于第三种

    newSingleThreadPool,只有一个线程,如果对给它太多的任务,它会阻塞,然后顺序执行每一个任务:

    public static void main(String[] args) {
    		ExecutorService service = Executors.newSingleThreadExecutor();
    		for(int i=0;i<5;i++){
    			service.execute(new CountDown());
    		}
    		service.shutdown();
    	}
    
    #0(9)
    #0(8)
    #0(7)
    #0(6)
    #0(5)
    #0(4)
    #0(3)
    #0(2)
    #0(1)
    #0(liftOff!)
    #1(9)
    #1(8)
    #1(7)
    #1(6)
    #1(5)
    #1(4)
    #1(3)
    #1(2)
    #1(1)
    #1(liftOff!)
    #2(9)
    #2(8)
    #2(7)
    #2(6)
    #2(5)
    #2(4)
    #2(3)
    #2(2)
    #2(1)
    #2(liftOff!)
    #3(9)
    #3(8)
    #3(7)
    #3(6)
    #3(5)
    #3(4)
    #3(3)
    #3(2)
    #3(1)
    #3(liftOff!)
    #4(9)
    #4(8)
    #4(7)
    #4(6)
    #4(5)
    #4(4)
    #4(3)
    #4(2)
    #4(1)
    #4(liftOff!)
    

    这是没有返回值的线程池的用法,这种方式在需要异步调用的时候使用,同步调用的时候需要用到有返回值的线程池

    2:有返回值的线程池

    如果想让多线程执行完毕后,将结果返回,则需要实现Callable而不是Runnable,重写call()方法,而不是run()方法

    //创建有返回值的线程池
    class MyCall implements Callable<Integer>{
    	
    	private int id;
    	
    	public MyCall(int id){
    		this.id = id;
    	}
    	
    	@Override
    	public Integer call() throws Exception {
    		return id;
    	}
    	
    }
    
    public class TestThread3 {
    	
    	public static void main(String[] args) {
    		ExecutorService exec = Executors.newFixedThreadPool(5);
    		List<Future<Integer>> result = new ArrayList<Future<Integer>>();
    		for(int i=0;i<5;i++){
    			Future<Integer> future = exec.submit(new MyCall(i));
    			result.add(future);
    		}
    		for(Future<Integer> future:result){
    			Integer i;
    			try {
    				i = future.get();
    				System.out.println(i);
    			} catch (InterruptedException | ExecutionException e) {
    				e.printStackTrace();
    			} finally{
    				exec.shutdown();
    			}
    			
    		}
    	}
    	
    }
    
    0
    1
    2
    3
    4
    

    多线程执行完任务后,最后将结果返回,其中get()会阻塞线程,起到计数器的作用,如果有一个线程没有执行完毕,那么就会一直阻塞

    不会结束。

    模拟如下:

    修改call()方法代码如下:

    public Integer call() throws Exception {
    		if(3==id){
    			Thread.sleep(3000);
    		}
    		return id;
    	}
    

     那么在运行的时候,就会在id=3处阻塞,3后面的任务都不会执行,直到3执行结束。

    今天多线程的知识就总结到这里了,后面继续......

  • 相关阅读:
    【BZOJ3166】ALO(主席树)
    【UOJ#188】Sanrd(min_25筛)
    伯努利数
    【51Nod1258】序列求和V4(FFT)
    【BZOJ5306】[HAOI2018]染色(NTT)
    【BZOJ4943】【NOI2017】蚯蚓排队(哈希)
    【BZOJ4912】天才黑客(最短路,虚树)
    【BZOJ5333】荣誉称号(动态规划)
    NOI2018前的每日记录
    【BZOJ1088】扫雷(递推)
  • 原文地址:https://www.cnblogs.com/warrior4236/p/6965022.html
Copyright © 2011-2022 走看看