单双线程

package com.te;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;


class single{
	static File file=null;
	static InputStream input=null;
	static InputStreamReader reader=null;
	static BufferedReader read=null;
	static String a=new String("");//读出来的数据临时存放的地方
	static String b=null;//没次从文件读出的数据
	static long starttime=0;
	static OutputStream out=null;
	static OutputStreamWriter writer=null;
	static BufferedWriter write=null;
	static File file1=null;
	static long endtime=0;
	
	
	static{
		System.out.println("我是静态块");
		file=new File("C:/Users/绝影/Desktop/input.txt");
		file1=new File("C:/Users/绝影/Desktop/out.txt");
		try {
			out=new FileOutputStream(file1);
			input=new FileInputStream(file);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		writer=new OutputStreamWriter(out);
		write=new BufferedWriter(writer);
		reader=new InputStreamReader(input);
		read=new BufferedReader(reader);
	}
	
	
	
	
	//执行写操作
    public void write(String a) {
    	System.out.println("此时写入的数据是:"+a);
    	try {
    		if(write!=null) {
    			 write.write(a+"\r\n");
    		}
					} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				if(write!=null) {
					write.flush();	
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
    
 

public void run() {
	
	try {
		while((b=read.readLine())!=null) {
				System.out.println("新读入的行数据:"+b);
				//取数据
				a=read();
				
				//写数据
				if(a.length()>5) {
					write(a.substring(0, 5));
				    a=a.substring(5);
				    System.out.println("上一次剩下的:"+a);
				}
				
			}
		
		//写数据
		while(a.length()>5) {
			write(a.substring(0, 5));
		    a=a.substring(5);
		    System.out.println("上一次剩下的:"+a);
		}
		
		if(a.length()<=5&&a.length()>0&&b==null) {
			  write(a);
			   a=a.substring(a.trim().length());
			   endtime=System.currentTimeMillis();
			   System.out.println("读写完毕总共消耗时间"+(endtime-starttime));
			
			  if(a.length()==0) {
					try {
						write.close();
						writer.close();
						out.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					
				}
		}
	
	} catch (IOException e1) {
		// TODO Auto-generated catch block
		e1.printStackTrace();
	}
	
	
}


//执行取数据
public String read() {
	a+=b;
	return a;
}
	
	
}

public class Threagood implements Runnable{
	static File file=null;
	static InputStream input=null;
	static InputStreamReader reader=null;
	static BufferedReader read=null;
	static String a=new String("");//读出来的数据临时存放的地方
	static String b=null;//没次从文件读出的数据
	static long starttime=0;
	static OutputStream out=null;
	static OutputStreamWriter writer=null;
	static BufferedWriter write=null;
	static File file1=null;
	static long endtime=0;
	private static Queue<String> queue = new LinkedBlockingQueue();//定义一个队列来存储数据  
	
	
	static{
		System.out.println("我是静态块");
		file=new File("C:/Users/绝影/Desktop/input.txt");
		file1=new File("C:/Users/绝影/Desktop/out.txt");
		try {
			out=new FileOutputStream(file1);
			input=new FileInputStream(file);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		writer=new OutputStreamWriter(out);
		write=new BufferedWriter(writer);
		reader=new InputStreamReader(input);
		read=new BufferedReader(reader);
	}
	
	
	class A extends Thread{
		
		//执行写操作
	   public void write(String a) {
	    	System.out.println("此时写入的数据是:"+a);
	    	try {
	    		if(write!=null) {
	    			 write.write(a+"\r\n");
	    		}
						} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally{
				try {
					if(write!=null) {
						write.flush();	
					}
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	    
	 
	@Override
	public void run() {
		//写数据
		while(b!=null) {
			if(queue.size()>5) {
				write(a.substring(0, 5));
			    a=a.substring(5);
			    queue.poll();
			    queue.offer(a);
			    System.out.println("上一次剩下的:"+a);
			}else {//临时存放的数据长度不满足5并且还没有读到文件尾,阻塞等待线程1读入
				
				synchronized (queue) {
					try {
						System.out.println("我现在数据不够5等待读入。。。。。");
						queue.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
		}
		
		
		while(a.length()>=5&&b==null) {
			   write(a.substring(0,5));
			   a=a.substring(5);
		}
		
		 
		  if(a.length()<5) {
			    write(a);
			    endtime=System.currentTimeMillis();
				System.out.println("读写完毕总共消耗时间"+(endtime-starttime));
				try {
					write.close();
					writer.close();
					out.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			}
		
	}
	
	}
	
    
   //执行取数据
    public String read() {
    	a+=b;
    	return a;
    }
    
 
    
	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
				while((b=read.readLine())!=null) {
					System.out.println("新读入的行数据:"+b);
					//取数据
					a=read();
					queue.offer(a);
					synchronized (queue) {
						System.out.println("我现在有数据了唤醒写线程:");
						queue.notify();
					}
				}
				
				if(b==null&&a.length()==0) {
					read.close();
					reader.close();
				    input.close();
				}
				
	
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
	}
	
	

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		starttime=System.currentTimeMillis();
		//单线程
		//new single().run();
		
		//多线程
		new Thread(new Threagood()).start();
		(new Threagood()).new A().start();
		
	}

}


上面用到了队列,这里要说明一个小小的误区,由于string对象时不可变的,所以我一开始只用string对象实现多线程,出现java.lang.IllegalMonitorStateException 
违法的监控状态异常。当某个线程试图等待一个自己并不拥有的对象(O)的监控器或者通知其他线程等待该对象(O)的监控器时,抛出该异常。所以才想到每次我的string对象都在变,自然就是某个线程试图等待一个自己并不拥有的对象,一直等待造成阻塞现象。


下面的理解有误但是为了清晰的知道自己错哪里也就不去了,对的放在最后面形成对比,我一开始对多线程的理解是这样的


线程:
   我写了一个多线程(一个线程读一个线程写)程序,一个单线程,两种方式分别执行把字符串从一个记事本文件读出来,然后分割成5个字符一次的
换行写入到另外一个记事本文件。
   单线程:读写完毕总共消耗时间1512441311495
   多线程:读写完毕总共消耗时间72




   可见运行差别真的是数量级别的:时间的差别是怎样来的呢,下面这是我个人的理解
  
    单线程做这个任务时:
    我必须要先读入数据,然后判断长度大于5,开始截取长度为5的字符串写入,依次这样顺序执行下去,但是这个过程中不可能cpu一直为这段程序服务。
这段程序io操作频度是比较高的,如果cpu单独做这个操作io操作等待时间是非常久的,操作系统是不允许发生这种情况的.
   所以cpu它会把这个操作分派下去,这里比方说就告诉我们物理硬盘,当io操作时cpu它可以自己去干其他的事情,而这段程序的其它操作就会被阻塞不会往下。
当io完成后只需要通知cpu一声我完成了即可:cpu就会重新给这段程序分配时间片等待下次被调用。
换而言之就是这段程序完成从读到写完的整个过程cpu参与进来的量是一样的。但是被分配的时间片就一个,执行完后就要等待下一次重新分配。等待分配到新的时间片时再
回过头来继续执行,这个频繁的不同程序之间上下文切换时间开销时很大的,每两次执行之间 获得CPU时间 间隔可能很大,所以完整这段程序就会很久。


   这个时候要想提高这段程序的运行效率就有两种方式,要吗提高它的优先级别,让CPU尽可能多的执行它。或者把这件事情分解成不同的小线程,这样每次被系统能调度获得CPU的
机会也就多了,不再是一个线程去抢占cpu,而是多个线程去抢占CPU,这些线程都是执行同一个任务不会在意具体它们谁先抢到,自然分得的执行这段程序的机会就比较多。


   不同应用程序之间的切换对这个对于操作系统本身来说是非常有效的,可以提高cpu利用率,但是对于我们编写程序来说,我们要求高效啊,自然时间很宝贵,
要使得很快速完成这个操作,就不应该让cpu在我的这段程序和其它应用之间频繁的进行上下文切换,要让CPU尽可能多的时间来做我们这件事,虽然系统整体效率低了,但是这段程序运行效率肯定高了。
   
上面说的两种方式
   1.让CPU尽可能多的执行它:
   我们会发现单线程每次申请获得CPU,操作系统是在众多程序中给他们合理的分配时间片,所以每次对这段程序来说都是给一个线程去抢占,运行完又等待分配,所以
我们可以考虑把这件事情的分配给多个线程来完成它,人多力量大,在这个过程中,即使有阻塞的情况,但是我们有几个线程是做这件事情的,不再是一个线程去抢占cpu,而是多个线程去抢占CPU,
所以整体执行效率能够提高。多线程就是让尽可能多做这个任务的线程分配到时间片,这几个线程不管是那个线程先被分配到,它都是在执行这个任务,整体
效率就上来了。也不会降低CPU的利用率。


2.提高它的优先级别:线程调度是操作系统去合调度的,人为改变可能会带来很多麻烦,所以方案一比较适合使用。
它的实质是减少CPU在不同程序之间进行上下文切换,更多的让cpu在同个程序内部进行上下文切换或者减小每两次运行同一段程序
之间的间隔。
   

正确的理解:

    

下面分析最上面的程序代码:

任务:把一个记事本文件中的数据读出来然后把它分割成5个字符一组写入到另外一个记事本文件


主线程中只有一个单线程
new single().run();//读写操作




主线程中启动两个线程:
new Thread(new Threagood()).start();//读操作
(new Threagood()).new A().start();//写操作


最后执行时间:
   单线程:读写完毕总共消耗时间1512441311495
   多线程:读写完毕总共消耗时间72


PS:我自己的电脑是双核4线程的


下面是我的理解:


假设读线程:new Thread(new Threagood()).start();首先运行,那么到
b=read.readLine(),此时会阻塞不需要使用到CPU,CPU就会空闲下来。
这时候写线程:new Threagood()).new A().start();开始执行。
接下来不管是读线程先读完还是写线程判断有数据长度大于5,在读线程阻塞的
这段时间里,CPU是没有等待的。
   同样如果判断有数据长度大于5,那么写线程在写数据,这段时间同样也会阻塞,CPU又会执行比方读线程中的字符串拼接和
入栈的操作,总之在阻塞这段时间里CPU是没有空闲等待的。都是在执行另外一个线程的某个命令。这个过程尽管对于单核CPU来说
线程上下文切换也是需要花销时间的,但是相对于IO的阻塞时间,这个时间是可以忽略的。


但是我看到知乎回答说多线程是不能提高执行效率的,最后花销的时间是一样的。
那么难道上面的时间差仅仅是因为我是双核CPU,多线程同时执行的线程量比单线程多才出来的?
实际上对于频繁IO操作,非CPU密集型的情况多线程还是可以提高执行效率的。但是最初的单核CPU用多
线程主要是解决IO阻塞的情况,很少因为效率用它,可以理解用到多线程仅仅是不愿看到CPU空闲下来,避免阻塞。
如果非CPU密集型的程序,单核CPU是不会乱用多线程的,频繁的线程之间进行上下文切换也会带来很大的时间开销。


       实际上多线程并不会真正意义上的提高执行速度,上面两段程序的时间差是来自于频繁的IO操作阻塞导致的,虽然线程之间切换也需要时间,但是对于IO操作来说这个是几乎可以忽略的,也说明了频繁进行IO操作导致了阻塞的时间是很大的。这也是多线程提高速度的真正原因之一,因为如果是多核CPU,我们开多线程,并行同时执行几个线程也会提高速度。

     操作系统的分配资源的单位是进程,分配CPU资源单位的是线程,所以分配到某个进程的CPU资源多少是与线程数量无关的。对于单核CPU非IO密集型的任务来说,在指定的时间内我们执行完一个任务需要时间10秒,那么我们开10个线程执行这个任务执行完成后用的时间还是10秒,因为10个线程之间是并发执行的,并不是同时执行,只是10个线程把这个任务分解成了10个部分来分别执行,同一个进程CPU分配是一定的,单线程一次就获得了分配到进程的所有CPU,所以执行完成一个任务模块的速度自然快,在执行完分配到的1秒的时间片后就执行完任务的10分之一。执行完的意思就是这部分全部完成,多线程把获得的CPU分配给多个线程,执行完单个线程执行速度自然慢。

       简单来说上面这种情况如果这个任务可以分成10个部分,对于单线程来说我1秒就完成了这个任务的一部分,10秒我就完成整个任务的全部。但是对于多线程,由于1秒内同时执行10个线程,所以1秒结束后,对一个线程来说只是完成了一个线程的10分之一,因为一个线程获得的CPU是单线程的10分之一,但是10秒结束时10个线程也是做完的,任务完成。

      红色字体的部分注意区分,一个是完成了任务的10分之一(一个线程完成了),一个是完成了一个线程的10分之一,也就是任务的100分之一。一个线程是多线程中的一个线程,单线程是单线程中的单线程,1秒时10个线程同时都完成了10个线程的10分之一。但是没有那个线程全部执行完成的,但是对于单程程来说,1秒时就已经全部运行完一个线程了。10秒结束时。

   多线程:10个(线程)乘上1/100(一个任务的)乘上10(秒)==一个任务。

   单线程:10秒乘上1/10(一个任务的)==一个任务。

这里有一个区别就是在第1秒时,单线程完整的运行完了一个线程,但是多线程只是运行完每个单独线程的10分之一。




    下面时来之一个知乎用户的经典回答:完全让我最上面的错误认识得到了深刻的了解:

    内容如下:

   

作者:罗然
链接:https://www.zhihu.com/question/37396742/answer/71862529
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

问题1概括下来就是很多人喜欢争论的多线程究竟能不能提高性能?
首先,回答是“能或者不能”。至于“不能”你已经理解了,那么我来说说为什么多线程“能”提高性能。要知道一个作业可不总是CPU密集型的,必然穿插着大量的IO调用在其中。而IO的一个特性就是阻塞等待。这个阻塞等待的时间消耗往往是远远大于线程切换所消耗的时间的,如果你要访问10个url获取接口内容,假如一次http访问平均阻塞时间大概是1s,那么你是一个一个的线性访问快还是10个线程访问快?相信不用算也知道多线程肯定更快。
最后就可以得出结论,多线程在CPU密集型的作业下的确不能提高性能甚至更浪费时间,但是在IO密集型的作业下则可以提升性能(或者更准确点说叫平均响应时间)。

问题2,进程是最小作业单元,跟进程内开多少线程都无关,CPU对进程的调度是统一的。所以多线程无法促进进程被CPU青睐。python的GIL也是只在CPU密集型的作业下显现的,通常的业务充斥着大量的IO,所以如果你不是做科学计算,那么放心大胆的使用多线程吧。

问题3,4,虽说操作系统有自己的调度策略,比如争抢,时间片轮转,但是用户态进程仅仅想通过自身应用级的代码实现如多线程等手段企图加大自身的CPU调度权重是不行的,不过自身的线程是可以实现优先级设置的。也就是说CPU给你整个进程的资源是有限且无法更改的,但是这些资源如何分配你是可以参与的,比如设置线程的优先级,也只是参与不能主导CPU在某个线程的调度时间,这个是无法控制的。跟当时的系统压力有关。

综上,你的问题提到了“阻塞”,这是服务端编程永恒的经典话题。不管是多进程,多线程,还是协程,大多都是致力于解决IO问题,说白了都是怎么样把阻塞变成非阻塞的手段。

来自https://www.cnblogs.com/hitwhhw09/p/4718404.html

什么时候该使用多线程呢?这要分四种情况讨论:

a.多核CPU——计算密集型任务。此时要尽量使用多线程,可以提高任务执行效率,例如加密解密,数据压缩解压缩(视频、音频、普通数据),否则只能使一个核心满载,而其他核心闲置。

b.单核CPU——计算密集型任务。此时的任务已经把CPU资源100%消耗了,就没必要也不可能使用多线程来提高计算效率了;相反,如果要做人机交互,最好还是要用多线程,避免用户没法对计算机进行操作。

c.单核CPU——IO密集型任务,使用多线程还是为了人机交互方便,

d.多核CPU——IO密集型任务,这就更不用说了,跟单核时候原因一样。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值