论坛首页 Java企业应用论坛

边读边写【7】 ----java 多线程实战【Thread /Executors】

浏览 8037 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-04-20   最后修改:2012-04-23
前面2个月一致忙碌,但是还是不忘在iteye 上发发帖子,写写文章。终于这周over了2套系统,剩下的时间基本上修改跟维护了。
在完成的一套“应用安装系统”后对多线程的理解又深了一点。
这里写出来,一来跟大家分享,二来让坛子里的大牛们指教指教,许多不足之处。
ps:看《maven 实战》看着看着就想睡觉,不知为什么?我一般看书很认真的。

ok。看看需求:

首先来实现一个多线程。简单的说就是在一个方法里头要完成6件事,6件事没有直接关联关系。不会因为一件事不完成导致下面的事情无法完成。都完成好了,就执行下一个方法。


线程俩种实现方式,如果你的代码已经继承其他类了,就实现Runnable借口。这俩种方式的线程都是没有返回值的。
公司的代码不好贴,这里贴个变相的demo.

策略一:
public class TestThread {
	//
	private static int i;
	//这里就相当做事的方法,所以线程都做这一件事,同时做
	public void get(long name) {
		try {
			Thread.sleep(name);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("-----name-----"+name);
		i++;
	}
	//全部做完了。
	public int  over(int n){
		System.out.println("---over--"+n);
		return n;
	}
	
	//正开始做
	public void  ing (){
		final TestThread td = new TestThread();
		
		Thread tr4  = new Thread(new Runnable(){
			@Override
			public void run() {
				td.get(1500L);
			}
		});
		tr4.start();
		
		new Thread(){
			public void run (){
				td.get(500L);
			}
		}.start();
		
		
		new Thread(){
			public void run (){
				td.get(100L);
			}
		}.start();
	}
	
	public void show(){
		System.out.println("=======show()========");
	}
	

	public static void main(String[] args) {
                //调用
		final TestThread ted = new TestThread();
		ted.ing();
		//来一个守护线程,监视看是不是所以的事情都做完了。做完了守护线程也跟着挂了
		Thread tt = new Thread(){
		public void run (){
			while(true){
				try {    //守护线程每100ms检查一下各个线程的完成状况
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
                                     //方法运行了3此,代码多做了,这是守护线程也要挂了,挂之前。。。。。
				if(i==3){
					ted.show();
					ted.over(1);
				}
				
			}
		}		
		};
		tt.setDaemon(true);
		tt.start();
		
		//ted.show();	  这个方法会同是运行
		
	}
}


这样做貌似也能达到要求。但是有明显的缺点。
第一:线程同时运行,必须用守护线程去执行线程做完以后将要运行的方法:这是不可取的,守护线程会立刻跟这死掉,如果将要做的事情的需要消耗大量的时间,或者开销的话,那么这个方法是不会正确执行的。【不知道具体原因,希望大鸟能指点迷津。】
第二:程序一执行,各个线程独立跑,守护线程只能监视各个线程的状态而无法获取各个线程所执行的事情是否成功了。这是相当不理想的。

策略二:Callable
public class HasValueThread {
	public void a(){
		Callable ca1 = 	new Callable(){
			@Override
			public Object call() throws Exception {
				System.out.println("==yes===");
				return true;
			}
			
		};
		
		Date d1 = new Date();
		System.out.println("d1"+d1.toLocaleString());
		 //ExecutorService exec = Executors.newCachedThreadPool();//创建线程池 
		 ExecutorService exec = Executors.newFixedThreadPool(7);//创建线程池 
		  Callable c1 = new MyCallable("A",1000); 
          Callable c2 = new MyCallable("B",5000); 
          Callable c3 = new MyCallable("c",15000); 
         Future f1 =  exec.submit(c1);
         Future f2 =  exec.submit(c2);
         Future f4 =  exec.submit(c3);
         Future f3 =  exec.submit(ca1);
         
         try {
        	 
			System.out.println(f1.get().toString()+System.currentTimeMillis());
			System.out.println(f2.get().toString()+System.currentTimeMillis());
			System.out.println(f4.get().toString()+System.currentTimeMillis());
			System.out.println(f3.get().toString()+System.currentTimeMillis());
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
         exec.shutdown();
	}
	
	public static void main(String[] args) {
		new HasValueThread().a();
		
		System.out.println("------");
	}

}
class MyCallable implements Callable{ 
    private String oid; 
    private long n;
    MyCallable(String oid,long n) { 
            this.oid = oid; 
            this.n = n;
    } 

    @Override 
    public Object call() throws Exception { 
    	Date d2 = new Date();
		System.out.println("d2"+d2.toLocaleString());
    		Thread.sleep(n);
            return oid+"任务返回的内容"; 
    } 
} 


使用策略二的好处有:
第一:Callable 的好处是有返回值,也就是说一个线程是否成功完成了他的使命,我们可以从他的返回的结果中【自定义结果】中得知。
第二:ThreadPoolExecutor【写了一些并发包相关的博文放在草稿箱里没有发表出来,主要是怕错误太多 - -,贻笑大方】它提供了一个线程池,使用方法通常使用 Executors 工厂方法配置。 api说:线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行任务集时使用的线程)的方法。每个 ThreadPoolExecutor 还维护着一些基本的统计数据,如完成的任务数。用Executor提交一个带返回值的Callable到ExecutorService里面。返回只类型是Futrue[未来,很有意思]。更有意思的是他会等全部的Callable执行完了以后在往下走,因为:ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。当然需要控制的是:
 //ExecutorService exec = Executors.newCachedThreadPool();//创建线程池 
		 ExecutorService exec = Executors.newFixedThreadPool(7);//创建线程池 

第一个newCachedThreadPool();他会启动n个线程来完成你的任务。他能自动线程回收。但是他的时间是60s.可以把这个demo跑下,发现让他sleep70秒他可以。这个问题我搞了很久没搞明白【求明白的分享】。所以我没有。用的是.newFixedThreadPool(7)参数能设置他的线程数。

总的来说还是那2句话,一是分享,一是请教。谢谢

   发表时间:2012-04-20   最后修改:2012-04-20
发现一个问题,论坛跟博客的内容没有同步。修改博客内容,贴子没同步过来。需要再修改一次,希望iteye能增加这个功能啊。
0 请登录后投票
   发表时间:2012-04-20  
策略一:i++不是原子操作,程序会出BUG
策略二:看API或者源码。

其实,这个需求用jdk自带的CountDownLatch最合适不过。。
0 请登录后投票
   发表时间:2012-04-21  
苏睿灬 写道
策略一:i++不是原子操作,程序会出BUG
策略二:看API或者源码。

其实,这个需求用jdk自带的CountDownLatch最合适不过。。


LZ需求中考虑了长程任务的场景,用CountDownLatch会导致完成较短任务的线程资源不能及时释放,这样会不会导致比如线程池资源耗尽的情况出现呢?
0 请登录后投票
   发表时间:2012-04-22  
推荐使用java.util.concurrent.CountDownLatch
0 请登录后投票
   发表时间:2012-04-23  
CountDownLatch,这个东西貌似没有返回值!!
0 请登录后投票
   发表时间:2012-04-23  
atomduan 写道
苏睿灬 写道
策略一:i++不是原子操作,程序会出BUG
策略二:看API或者源码。

其实,这个需求用jdk自带的CountDownLatch最合适不过。。


LZ需求中考虑了长程任务的场景,用CountDownLatch会导致完成较短任务的线程资源不能及时释放,这样会不会导致比如线程池资源耗尽的情况出现呢?

同感
0 请登录后投票
   发表时间:2012-04-23  
楼主 countDownLatch可以直接实现。
建议多了解下juc的提供的很多高级锁机制,有很多现成解决方案。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics