JAVA-File递归练习与多线程

File递归练习

练习1:统计该文件夹大小

  • 从键盘接受一个文件夹路径

    • 创建键盘录入对象
    • 定义一个无限循环
    • 将键盘录入的结果存储并封装成File对象
    • 对File对象判断
      • 路径是否存在 exists()
      • 录入的是文件路径isFile()
    • 将文件夹路径对象返回
  • 统计该文件夹大小

    • 获取该文件夹下所有的文件和文件夹listFiles()
    • 遍历数组
    • 判断
      • 文件累加
      • 文件夹递归
    public static void main(String[] args) {
    	//File dir = new File("F:\\day06");
    	//System.out.println(dir.length());				//直接获取文件夹的结果是0
    	File dir = getDir();
    	System.out.println(getFileLength(dir));
    	
    }
    
    public static File getDir() {
    	Scanner sc = new Scanner(System.in);
    	System.out.println("请输入一个文件夹路径:");
    	while(true) {
    		String line = sc.nextLine();
    		File dir = new File(line);
    		if(!dir.exists()) {
    			System.out.println("您录入的文件夹路径不存在,请输入一个文件夹路径:");
    		}else if(dir.isFile()) {
    			System.out.println("您录入的是文件路径,请输入一个文件夹路径:");
    		}else {
    			return dir;
    		}
    	}
    	
    }
    
    public static long getFileLength(File dir) {	//dir = F:\day06\day07
    	long len = 0;
    	File[] subFiles = dir.listFiles();			
    	for (File subFile : subFiles) {
    		if(subFile.isFile()) {
    			len = len + subFile.length();
    		}else {
    			len = len + getFileLength(subFile);
    		}
    	}
    	return len;
    }
    

练习2:删除该文件

  • 获取该文件夹下所有的文件和文件夹

  • 遍历数组

    • 文件删除
    • 文件夹递归调用
    • 循环结束后把空文件删除
    public static void main(String[] args) {
    	File dir = Test1.getDir();				
    	deleteFile(dir);
    }
    
    public static void deleteFile(File dir) {	
    	File[] subFiles = dir.listFiles();		
    	for (File subFile : subFiles) {
    		if(subFile.isFile()) {
    			subFile.delete();
    		}else {
    			deleteFile(subFile);			
    		}
    	}
    	dir.delete();
    }
    

    练习3:拷贝

    • 在目标文件夹中创建原文件夹
    • 获取原文件夹中所有的文件和文件夹并存储
    • 遍历数组
      • 如果是文件就用io流读写
      • 文件夹递归
    public static void main(String[] args) throws IOException {
    	File src = Test1.getDir();
    	File dest = Test1.getDir();
    	if(src.equals(dest)) {      //不能在原文件下拷贝原文件
    		System.out.println("目标文件夹是源文件夹的子文件夹");
    	}else {
    		copy(src,dest);
    	}
    }
    /*
     * 把其中一个文件夹中(包含内容)拷贝到另一个文件夹中
     * 1,返回值类型void
     * 2,参数列表File src,File dest
     */
    public static void copy(File src, File dest) throws IOException {
    	File newDir = new File(dest, src.getName());
    	newDir.mkdir();
    	File[] subFiles = src.listFiles();
    	for (File subFile : subFiles) {
    		if(subFile.isFile()) {
    			BufferedInputStream bis = new BufferedInputStream(new FileInputStream(subFile));
    			BufferedOutputStream bos = 
    					new BufferedOutputStream(new FileOutputStream(new File(newDir,subFile.getName())));
    			
    			int b;
    			while((b = bis.read()) != -1) {
    				bos.write(b);
    			}
    			
    			bis.close();
    			bos.close();
    		}else {
    			copy(subFile,newDir);
    		}
    	}
    }
    

练习4:求1000的阶乘中所有的0

  • 非递归方法:

    public static void main(String[] args) {
    		/*int result = 1;
    		for(int i = 1; i <= 1000; i++) {
    			result = result * i;
    		}
    		
    		System.out.println(result);		//因为1000的阶乘远远超出了int的取值范围
    		*/
    		//demo1();
    		demo2();
    	}
    
    	public static void demo2() {		//获取1000的阶乘尾部有多少个零
    		BigInteger bi1 = new BigInteger("1");
    		for(int i = 1; i <= 1000; i++) {
    			BigInteger bi2 = new BigInteger(i+"");
    			bi1 = bi1.multiply(bi2);	//将bi1与bi2相乘的结果赋值给bi1
    		}
    		String str = bi1.toString();	//获取字符串表现形式
    		StringBuilder sb = new StringBuilder(str);
    		str = sb.reverse().toString();	//链式编程
    		
    		int count = 0;					//定义计数器
    		for(int i = 0; i < str.length(); i++) {
    			if('0' != str.charAt(i)) {
    				break;
    			}else {
    				count++;
    			}
    		}
    		
    		System.out.println(count);
    	}
    
    	public static void demo1() {		//求1000的阶乘中所有的零
    		BigInteger bi1 = new BigInteger("1");
    		for(int i = 1; i <= 1000; i++) {
    			BigInteger bi2 = new BigInteger(i+"");
    			bi1 = bi1.multiply(bi2);	//将bi1与bi2相乘的结果赋值给bi1
    		}
    		String str = bi1.toString();	//获取字符串表现形式
    		int count = 0;
    		for(int i = 0; i < str.length(); i++) {
    			if('0' == str.charAt(i)) {	//如果字符串中出现了0字符
    				count++;				//计数器加1
    			}
    		}
    		System.out.println(count);
    	}
    
  • 递归

    • 规律:
      5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100…1000 1000 / 5 = 200
      5 * 5 5 * 5 * 2 5 * 5 * 3 5 * 5 * 4 5 * 5 * 5 5 * 5 * 6 200 / 5 = 40
      5 * 5 * 5 * 1 5 * 5 * 5 * 2 5 * 5 * 5 * 3 5 * 5 * 5 * 4 5 * 5 * 5 * 5 5 * 5 * 5 * 6 5 * 5 * 5 * 7 5 * 5 * 5 * 8
      40 / 5 = 8
      5 * 5 * 5 * 5 8 / 5 = 1
    	public static void main(String[] args) {
    		System.out.println(fun(1000));
    	}
    
    	public static int fun(int num) {
    		if(num > 0 && num < 5) {
    			return 0;
    		}else {
    			return num / 5 + fun(num / 5);
    		}
    	}
    

练习5:约瑟夫环

	public static void main(String[] args) {
		System.out.println(getLucklyNum(8));
	}

	/*
	 * 获取幸运数字
	 * 1,返回值类型int
	 * 2,参数列表int num
	 */
	public static int getLucklyNum(int num) {
		ArrayList<Integer> list = new ArrayList<>();		//创建集合存储1到num的对象
		for(int i = 1; i <= num; i++) {
			list.add(i);									//将1到num存储在集合中
		}
		
		int count = 1;										//用来数数的,只要是3的倍数就杀人
		for(int i = 0; list.size() != 1; i++) {				//只要集合中人数超过1,就要不断的杀
			if(i == list.size()) {							//如果i增长到集合最大的索引+1时
				i = 0;										//重新归零
			}
			
			if(count % 3 == 0) {							//如果是3的倍数
				list.remove(i--);								//就杀人
			}
			count++;
		}
		
		return list.get(0);
	}

Thread

  • 定义

    • 线程是程序执行的一条路径, 一个进程中可以包含多条线程
    • 多线程并发执行可以提高程序的效率, 可以同时完成多项工作
  • 并行和并发

    • 并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)
    • 并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。
  • 实现方法

    • 继承Thread

      • 定义类继承Thread
      • 重写run方法
      • 把新线程要做的事写在run方法中
      • 创建线程对象
      • 开启新线程, 内部会自动执行run方法
      	public static void main(String[] args) {
      			MyThread mt = new MyThread();							//4,创建自定义类的对象
      			mt.start();												//5,开启线程
      			
      			for(int i = 0; i < 3000; i++) {
      				System.out.println("bb");
      			}
      		}
      	
      	}
      	class MyThread extends Thread {									//1,定义类继承Thread
      		public void run() {											//2,重写run方法
      			for(int i = 0; i < 3000; i++) {							//3,将要执行的代码,写在run方法中
      				System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa");
      			}
      		}
      	}
      
    • 实现Runnable

      • 定义类实现Runnable接口
      • 实现run方法
      • 把新线程要做的事写在run方法中
      • 创建自定义的Runnable的子类对象
      • 创建Thread对象, 传入Runnable
      • 调用start()开启新线程, 内部会自动调用Runnable的run()方法
      
      	public static void main(String[] args) {
      			MyRunnable mr = new MyRunnable();						//4,创建自定义类对象
      			//Runnable target = new MyRunnable();
      			Thread t = new Thread(mr);								//5,将其当作参数传递给Thread的构造函数
      			t.start();												//6,开启线程
      			
      			for(int i = 0; i < 3000; i++) {
      				System.out.println("bb");
      			}
      		}
      	}
      	
      	class MyRunnable implements Runnable {							//1,自定义类实现Runnable接口
      		@Override
      		public void run() {											//2,重写run方法
      			for(int i = 0; i < 3000; i++) {							//3,将要执行的代码,写在run方法中
      				System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa");
      			}
      		}
      		
      	}
      
  • 两种方式的区别

    • 查看源码的区别:

      • a.继承Thread : 由于子类重写了Thread类的run(), 当调用start()时, 直接找子类的run()方法
      • b.实现Runnable : 构造函数中传入了Runnable的引用, 成员变量记住了它, start()调用run()方法时内部判断成员变量Runnable的引用是否为空, 不为空编译时看的是Runnable的run(),运行时执行的是子类的run()方法
    • 继承Thread

      • 好处是:可以直接使用Thread类中的方法,代码简单
      • 弊端是:如果已经有了父类,就不能用这种方法
    • 实现Runnable接口

      • 好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的
      • 弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂
  • 匿名内部类实现

    • 继承Thread类

        new Thread() {													//1,new 类(){}继承这个类
        	public void run() {											//2,重写run方法
        		for(int i = 0; i < 3000; i++) {							//3,将要执行的代码,写在run方法中
        			System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa");
        		}
        	}
        }.start();
      
    • 实现Runnable接口

        new Thread(new Runnable(){										//1,new 接口(){}实现这个接口
        	public void run() {											//2,重写run方法
        		for(int i = 0; i < 3000; i++) {							//3,将要执行的代码,写在run方法中
        			System.out.println("bb");
        		}
        	}
        }).start(); 
      

ThreadMethod

  • Thread.currentThread()

  • getName()

  • 设置名字

    • 通过构造函数直接设置
    • setName()
  • sleep()

    • sleep(毫秒,纳秒), 控制当前线程休眠若干毫秒1秒= 1000毫秒 1秒 = 1000 * 1000 * 1000纳秒 多设置毫秒
  • setDaemon()

    • 设置一个线程为守护线程, 该线程不会单独执行, 当其他非守护线程都执行结束后, 自动退出

      Thread t1 = new Thread() {
      	public void run() {
      		for(int i = 0; i < 50; i++) {
      			System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
      			try {
      				Thread.sleep(10);
      			} catch (InterruptedException e) {
      				e.printStackTrace();
      			}
      		}
      	}
      };
      
      Thread t2 = new Thread() {
      	public void run() {
      		for(int i = 0; i < 5; i++) {
      			System.out.println(getName() + "...bb");
      			try {
      				Thread.sleep(10);
      			} catch (InterruptedException e) {
      				e.printStackTrace();
      			}
      		}
      	}
      };
      
      t1.setDaemon(true);						//将t1设置为守护线程
      
      t1.start();
      t2.start();
      
  • 加入线程

    • join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续
    • join(int), 可以等待指定的毫秒之后继续
    final Thread t1 = new Thread() {
    	public void run() {
    		for(int i = 0; i < 50; i++) {
    			System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
    			try {
    				Thread.sleep(10);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    };
    
    Thread t2 = new Thread() {
    	public void run() {
    		for(int i = 0; i < 50; i++) {
    			if(i == 2) {
    				try {
    					//t1.join();						//插队,加入
    					t1.join(30);						//加入,有固定的时间,过了固定时间,继续交替执行
    					Thread.sleep(10);
    				} catch (InterruptedException e) {
    					
    					e.printStackTrace();
    				}
    			}
    			System.out.println(getName() + "...bb");
    		
    		}
    	}
    };
    
    t1.start();
    t2.start();
    
  • 礼让线程

    • yield让出cpu
  • 设置线程的优先级

    • setPriority()设置线程的优先级
    • yield和setPriority()效果不明显仅做了解

同步代码块

  • 作用点
    • 当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步.
    • 如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码.
  • synchronized
    • 同步代码块需要使用同一个锁对象

      • 锁对象没有限制任意设置
      class Printer {
      				Demo d = new Demo();
      				public static void print1() {
      					synchronized(d){				//锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象
      						System.out.print("黑");
      						System.out.print("马");
      						System.out.print("程");
      						System.out.print("序");
      						System.out.print("员");
      						System.out.print("\r\n");
      					}
      				}
      	
      				public static void print2() {	
      					synchronized(d){	
      						System.out.print("传");
      						System.out.print("智");
      						System.out.print("播");
      						System.out.print("客");
      						System.out.print("\r\n");
      					}
      				}
      			}
      

同步方法

  • 使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的

      class Printer {
      	public static void print1() {
      		synchronized(Printer.class){				//锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象
      			System.out.print("黑");
      			System.out.print("马");
      			System.out.print("程");
      			System.out.print("序");
      			System.out.print("员");
      			System.out.print("\r\n");
      		}
      	}
      	/*
      	 * 非静态同步函数的锁是:this
      	 * 静态的同步函数的锁是:字节码对象
      	 */
      	public static synchronized void print2() {	
      		System.out.print("传");
      		System.out.print("智");
      		System.out.print("播");
      		System.out.print("客");
      		System.out.print("\r\n");
      	}
      }
    

线程安全问题

  • 多线程并发操作同一数据时, 就有可能出现线程安全问题

  • 使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作

  • 继承thread:

      public class Demo2_Synchronized {
    
      	/**
      	 * @param args
      	 * 需求:铁路售票,一共100张,通过四个窗口卖完.
      	 */
      	public static void main(String[] args) {
      		TicketsSeller t1 = new TicketsSeller();
      		TicketsSeller t2 = new TicketsSeller();
      		TicketsSeller t3 = new TicketsSeller();
      		TicketsSeller t4 = new TicketsSeller();
      		
      		t1.setName("窗口1");
      		t2.setName("窗口2");
      		t3.setName("窗口3");
      		t4.setName("窗口4");
      		t1.start();
      		t2.start();
      		t3.start();
      		t4.start();
      	}
      
      }
      
      class TicketsSeller extends Thread {
      	private static int tickets = 100;
      	static Object obj = new Object();
      	public TicketsSeller() {
      		super();
      		
      	}
      	public TicketsSeller(String name) {
      		super(name);
      	}
      	public void run() {
      		while(true) {
      			synchronized(obj) {
      				if(tickets <= 0) 
      					break;
      				try {
      					Thread.sleep(10);//线程1睡,线程2睡,线程3睡,线程4睡
      				} catch (InterruptedException e) {
      					
      					e.printStackTrace();
      				}
      				System.out.println(getName() + "...这是第" + tickets-- + "号票");
      			}
      		}
      	}
      }
    
  • Runnnable:

      	/**
      	 * @param args
      	 * 火车站卖票的例子用实现Runnable接口
      	 */
      	public static void main(String[] args) {
      		MyTicket mt = new MyTicket();
      		new Thread(mt).start();
      		new Thread(mt).start();
      		new Thread(mt).start();
      		new Thread(mt).start();
      		
      		/*Thread t1 = new Thread(mt);				//多次启动一个线程是非法的
      		t1.start();
      		t1.start();
      		t1.start();
      		t1.start();*/
      	}
      	
      	class MyTicket implements Runnable {
      		private int tickets = 100;
      		@Override
      		public void run() {
      			while(true) {
      				synchronized(this) {
      					if(tickets <= 0) {
      						break;
      					}
      					try {
      						Thread.sleep(10);				//线程1睡,线程2睡,线程3睡,线程4睡
      					} catch (InterruptedException e) {
      						
      						e.printStackTrace();
      					}
      					System.out.println(Thread.currentThread().getName() + "...这是第" + tickets-- + "号票");
      				}
      			}
      		}
      	}
    

死锁

  • 多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁
    • 尽量不要嵌套使用

        private static String s1 = "筷子左";
        private static String s2 = "筷子右";
        public static void main(String[] args) {
        	new Thread() {
        		public void run() {
        			while(true) {
        				synchronized(s1) {
        					System.out.println(getName() + "...拿到" + s1 + "等待" + s2);
        					synchronized(s2) {
        						System.out.println(getName() + "...拿到" + s2 + "开吃");
        					}
        				}
        			}
        		}
        	}.start();
        	
        	new Thread() {
        		public void run() {
        			while(true) {
        				synchronized(s2) {
        					System.out.println(getName() + "...拿到" + s2 + "等待" + s1);
        					synchronized(s1) {
        						System.out.println(getName() + "...拿到" + s1 + "开吃");
        					}
        				}
        			}
        		}
        	}.start();
        }
      

以前的线程安全类回顾

  • 看源码:Vector,StringBuffer,Hashtable
    • Vector是线程安全的,ArrayList是线程不安全的
    • StringBuffer是线程安全的,StringBuilder是线程不安全的
    • Hashtable是线程安全的,HashMap是线程不安全的
  • Collections.synchroinzed(xxx)
    • 将类设置成线程安全的
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值