• java--线程--习题集锦


    匿名类的一个好处是可以很方便的访问外部的局部变量。
    前提是外部的局部变量需要被声明为final。(JDK7以后就不需要了)

    ======================

    同步方法1:普通式

    同步方法2:在对象方法里 写关键字,用this

    同步方法3:在方法前,加上修饰符synchronized,效果等同方法2

    ==================

    题目1--同步查找文件内容

    把 练习-查找文件内容 改为多线程查找文件内容
    原练习的思路是遍历所有文件,当遍历到文件是 .java的时候,查找这个文件的内容,查找完毕之后,再遍历下一个文件

    现在通过多线程调整这个思路:
    遍历所有文件,当遍历到文件是.java的时候,创建一个线程去查找这个文件的内容,不必等待这个线程结束,继续遍历下一个文件

    package zsc.czy.zhonghe;
    
    import java.io.File;
    import java.io.FileReader;
    
    public class findContent {
    
    	public static void main(String[] args) {
    		File f = new File("d:/test");
    		
    		search(f, "Hello");
    	}
    
    	public static void search(File f, String search) {
    		System.out.println(f); //d:find.txt
    		System.out.println(f.getAbsolutePath());
    //		System.out.println(f.getName());//find.txt
    		if (f.isFile()) {
    			if (f.getName().toLowerCase().endsWith(".java")) {
    				new Thread(new Runnable() {
    					
    					@Override
    					public void run() {
    						String fileContent = readFileConent(f);
    						if (fileContent.contains(search)) {
    							System.out.printf("找到子目标字符串%s,在文件:%s%n", search, f);
    						}
    					}
    				}).start();
    				
    			}
    		}
    		if(f.isDirectory()){
    			File[] fs = f.listFiles();
    			for(File file :fs){
    				search(file,search);
    			}
    		}
    
    	}
    
    	private static String readFileConent(File f) {
    		try (FileReader fr = new FileReader(f);) {
    			char[] c = new char[(int) f.length()];
    			fr.read(c);
    			String s = new String(c);
    			return s;
    
    		} catch (Exception e) {
    			return null;
    		}
    
    	}
    }
    
    

    题目2--英雄充能

    英雄有可以放一个技能叫做: 波动拳-a du gen。
    每隔一秒钟,可以发一次,但是只能连续发3次。

    发完3次之后,需要充能5秒钟,充满,再继续发。

    package zsc.czy.thread;
    
    public class Hero {
    	String name;
    	int hp;
    	int damage;
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public int getHp() {
    		return hp;
    	}
    
    	public void setHp(int hp) {
    		this.hp = hp;
    	}
    
    	public int getDamage() {
    		return damage;
    	}
    
    	public void setDamage(int damage) {
    		this.damage = damage;
    	}
    
    	public boolean isDead() {
    		return 0 >= hp ? true : false;
    	}
    
    	public void attackHero(Hero h) {
    		try {
    			// 为了表示攻击需要时间,每次攻击暂停1000毫秒
    			Thread.sleep(1000);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		h.hp -= damage;
    		System.out.format("%s 正在攻击 %s,%s的血编程了 %.0f%n", name, h.name, h.name,
    				h.hp);
    		if (h.isDead()) {
    			System.out.println(h.name + "死了");
    		}
    	}
    
    	int totalTime = 3;
    
    	public void adugen() {
    		while (true) {
    			for (int i = 0; i < totalTime; i++) {
    				System.out.printf("波动拳第%d发%n", i + 1);
    				try {
    					Thread.sleep(1000);
    				} catch (Exception e) {
    					// TODO: handle exception
    				}
    			}
    			System.out.println("开始为时5秒的充能");
    			try {
    				Thread.sleep(5000);
    			} catch (InterruptedException e) {
    
    				e.printStackTrace();
    			}
    
    		}
    	}
    
    	public static void main(String[] args) {
    		Hero h = new Hero();
    		h.name = "红仔";
    		h.adugen();
    	}
    }
    
    

    题目3--破解密码

    1. 生成一个长度是3的随机字符串,把这个字符串作为当做密码

    2. 创建一个破解线程,使用穷举法,匹配这个密码

    3. 创建一个日志线程,打印都用过哪些字符串去匹配,这个日志线程设计为守护线程

    提示: 破解线程把穷举法生成的可能密码放在一个容器中,日志线程不断的从这个容器中拿出可能密码,并打印出来。 如果发现容器是空的,就休息1秒,如果发现不是空的,就不停的取出,并打印。

    ==============================

    HashMap不严格 可以存放 null --不是线程安全的类
    Hashtable严格

    ArrayList是非线程安全的
    Vector是线程安全的类

    ====================

    题目4-线程安全的MyStack

    借助把非线程安全的集合转换为线程安全,用另一个方式完成 练习-线程安全的MyStack

    题目5--死锁

    3个同步对象a, b, c
    3个线程 t1,t2,t3

    故意设计场景,使这3个线程彼此死锁

    //我这样设计是不成功的
    package zsc.czy.thread;
    
    public class SiSuo {
    	public static void main(String[] args) {
    		final Hero ahri = new Hero();
    		ahri.name = "九尾妖狐";
    		final Hero annie = new Hero();
    		annie.name = "安妮";
    		final Hero leqing = new Hero();
    		annie.name = "李青";
    
    		Thread t1 = new Thread() {
    			public void run() {
    				// 占有九尾妖狐
    				synchronized (ahri) {
    					System.out.println("t1 已占有九尾妖狐");
    					try {
    						// 停顿1000毫秒,另一个线程有足够的时间占有安妮
    						Thread.sleep(1000);
    					} catch (InterruptedException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					}
    
    					System.out.println("t1 试图占有安妮");
    					System.out.println("t1 等待中 。。。。");
    					synchronized (annie) {
    						System.out.println("do something");
    					}
    				}
    
    			}
    		};
    		t1.start();
    		Thread t2 = new Thread() {
    			public void run() {
    				// 占有安妮
    				synchronized (annie) {
    					System.out.println("t2 已占有安妮");
    					try {
    
    						// 停顿1000秒,另一个线程有足够的时间占有暂用九尾妖狐
    						Thread.sleep(1000);
    					} catch (InterruptedException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					}
    					System.out.println("t2 试图占有李青");
    					System.out.println("t2 等待中 。。。。");
    					synchronized (leqing) {
    						System.out.println("do something");
    					}
    				}
    
    			}
    		};
    		t2.start();
    		
    		Thread t3 = new Thread(){
    			public void run() {
    				synchronized (leqing) {
    					System.out.println("t3已占有李青");
    					try {
    						Thread.sleep(1000);
    					} catch (InterruptedException e) {
    						
    						e.printStackTrace();
    					}
    					System.out.println("t3 试图占有九尾妖狐");
    					System.out.println("t3 等待中 。。。。");
    				}
    				synchronized (ahri) {
    					System.out.println("do something");
    				}
    				
    			};
    		};
    		t3.start();
    	}
    }
    
    

    正确做法为:

    package multiplethread;
     
    public class TestThread {
           
        public static void main(String[] args) {
            Object a = new Object();
            Object b = new Object();
            Object c = new Object();
     
            Thread t1 =new Thread(){
                public void run(){
                    synchronized (a) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        synchronized (b) {
                            synchronized (c) {
                                 
                            }
                        }
                    }  
                }
            };
            Thread t2 =new Thread(){
                public void run(){
                    synchronized (c) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        synchronized (a) {
                            synchronized (b) {
                                 
                            }
                        }
                    }  
                }
            };
            Thread t3 =new Thread(){
                public void run(){
                    synchronized (b) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        synchronized (c) {
                            synchronized (a) {
                                 
                            }
                        }
                    }  
                }
            };
     
            t1.start();
            t2.start();
            t3.start();
            
       }
             
    }
    

    ================

    使用wait和notify进行线程交互

    this.wait()表示 让占有this的线程等待,并临时释放占有
    this.notify() 表示通知那些等待在this的线程,可以苏醒过来了。

    public synchronized void recover() {
    		hp = hp + 1;
    		 System.out.printf("%s 回血1点,增加血后,%s的血量是%.0f%n", name, name, hp);
    		// 通知那些等待在this对象上的线程,可以醒过来了,如第20行,等待着的减血线程,苏醒过来
    		this.notify();
    	}
    
    	// 掉血  同步方式和上面效果一样 这个方式
    	public void hurt() {
    		synchronized (this) {
    			if (hp == 1) {
    				// 让占有this的减血线程,暂时释放对this的占有,并等待
    				try {
    					this.wait();
    				} catch (InterruptedException e) {
    
    					e.printStackTrace();
    				}
    			}
    			hp = hp - 1;
    			System.out.printf("%s 减血1点,减少血后,%s的血量是%.0f%n", name, name, hp);
    
    		}
    	}
    

    别忘了Thread 父类也是Object

    题目--线程交互

    假设加血线程运行得更加频繁,英雄的最大血量是1000

    设计加血线程和减血线程的交互,让回血回满之后,加血线程等待,直到有减血线程减血

    	// 回血
    	public synchronized void recover() {
    		
    		if(hp>=1000){
    			try {
    				this.wait(); //后加的
    			} catch (InterruptedException e) {
    				
    				e.printStackTrace();
    			}
    		}
    		
    		hp = hp + 1;
    		System.out.printf("%s 回血1点,增加血后,%s的血量是%.0f%n", name, name, hp);
    		// 通知那些等待在this对象上的线程,可以醒过来了,如第73行,等待着的减血线程,苏醒过来
    		this.notify(); 
    	}
    
    	// 掉血  同步方式和上面效果一样 这个方式
    	public void hurt() {
    		synchronized (this) {
    			if (hp == 1) {
    				// 让占有this的减血线程,暂时释放对this的占有,并等待
    				try {
    					this.wait();
    				} catch (InterruptedException e) {
    
    					e.printStackTrace();
    				}
    			}
    			hp = hp - 1;
    			System.out.printf("%s 减血1点,减少血后,%s的血量是%.0f%n", name, name, hp);
    			
    			//notify() 的意思是,通知一个等待在这个同步对象上的线程,你可以苏醒过来了,有机会重新占用当前对象了。
    			//掉血之后,唤醒等待的线程
    			this.notify();//后加的
    		}
    	}
    

    题目--多线程交互

    在上面的练习的基础上,增加回血线程到2条,减血线程到5条,同时运行。

    运行一段时间,观察会发生的错误,分析错误原因,并考虑解决办法

    题目--生产者消费者问题

    生产者消费者问题是一个非常典型性的线程交互的问题。

    1. 使用栈来存放数据
      1.1 把栈改造为支持线程安全
      1.2 把栈的边界操作进行处理,当栈里的数据是0的时候,访问pull的线程就会等待。 当栈里的数据时200的时候,访问push的线程就会等待
    2. 提供一个生产者(Producer)线程类,生产随机大写字符压入到堆栈
    3. 提供一个消费者(Consumer)线程类,从堆栈中弹出字符并打印到控制台
    4. 提供一个测试类,使两个生产者和三个消费者线程同时运行,结果类似如下 :

    ==================

    Lock

    题目

    在练习-线程安全的MyStack 练习中,使用synchronized把MyStack修改为了线程安全的类。

    接下来,借助Lock把MyStack修改为线程安全的类

    把synchronized去掉
    使用lock占用锁
    使用unlock释放锁
    必须放在finally执行,万一heros.addLast抛出异常也会执行

    package multiplethread;
        
    import java.util.LinkedList;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
     
    import charactor.Hero;
        
    public class MyStack {
        
        LinkedList<Hero> heros = new LinkedList<Hero>();
     
        Lock lock = new ReentrantLock();
         
        //把synchronized去掉
        public  void push(Hero h) {
            try{
                //使用lock占用锁
                lock.lock();
                heros.addLast(h);          
            }
            finally{
                //使用unlock释放锁
                //必须放在finally执行,万一heros.addLast抛出异常也会执行
                lock.unlock();
            }
         
        }
        
        //把synchronized去掉
        public  Hero pull() {
            try{
                //使用lock占用锁
                lock.lock();
                return heros.removeLast();         
            }
            finally{
                //使用unlock释放锁
                //必须放在finally执行,万一heros.removeLast();抛出异常也会执行          
                lock.unlock();
            }
        }
        
        public Hero peek() {
            return heros.getLast();
        }
            
        public static void main(String[] args) {
     
        }
        
    }
    

    题目--借助tryLock 解决死锁问题

    当多个线程按照不同顺序占用多个同步对象的时候,就有可能产生死锁现象。

    死锁之所以会发生,就是因为synchronized 如果占用不到同步对象,就会苦苦的一直等待下去,借助tryLock的有限等待时间,解决死锁问题

    package multiplethread;
      
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
      
    public class TestThread {
      
        public static void main(String[] args) throws InterruptedException {
            Lock lock_ahri = new ReentrantLock();
            Lock lock_annie = new ReentrantLock();
      
            Thread t1 = new Thread() {
                public void run() {
                    // 占有九尾妖狐
                    boolean ahriLocked = false;
                    boolean annieLocked = false;
                      
                    try {
                        ahriLocked = lock_ahri.tryLock(10, TimeUnit.SECONDS);
                        if (ahriLocked) {
                            System.out.println("t1 已占有九尾妖狐");
                            // 停顿1000秒,另一个线程有足够的时间占有安妮
                            Thread.sleep(1000);
                            System.out.println("t1 试图在10秒内占有安妮");
                            try {
                                annieLocked = lock_annie.tryLock(10, TimeUnit.SECONDS);
                                if (annieLocked)
                                    System.out.println("t1 成功占有安妮,开始啪啪啪");
                                else{
                                    System.out.println("t1 老是占用不了安妮,放弃");
                                }
      
                            } finally {
                                if (annieLocked){
                                    System.out.println("t1 释放安妮");
                                    lock_annie.unlock();
                                }
                            }
      
                        }
                    } catch (InterruptedException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    } finally {
                        if (ahriLocked){
                            System.out.println("t1 释放九尾狐");
                            lock_ahri.unlock();
                        }
                    }
      
                }
            };
            t1.start();
              
            Thread.sleep(100);
              
            Thread t2 = new Thread() {
                public void run() {
                    boolean annieLocked = false;
                    boolean ahriLocked = false;
                                      
                    try {annieLocked = lock_annie.tryLock(10, TimeUnit.SECONDS);
                      
                    if (annieLocked){
                          
                            System.out.println("t2 已占有安妮");
                            // 停顿1000秒,另一个线程有足够的时间占有安妮
                            Thread.sleep(1000);
                            System.out.println("t2 试图在10秒内占有九尾妖狐");
                            try {
                                ahriLocked = lock_ahri.tryLock(10, TimeUnit.SECONDS);
                                if (ahriLocked)
                                    System.out.println("t2 成功占有九尾妖狐,开始啪啪啪");
                                else{
                                    System.out.println("t2 老是占用不了九尾妖狐,放弃");
                                }
                            }
                            finally {
                                if (ahriLocked){
                                    System.out.println("t2 释放九尾狐");
                                    lock_ahri.unlock();
                                }
                                      
                            }
      
                        }
                    } catch (InterruptedException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    } finally {
                        if (annieLocked){
                            System.out.println("t2 释放安妮");
                            lock_annie.unlock();
                        }
                              
                    }
                }
            };
            t2.start();
          
        }
    }
    

    题目--生产者消费者问题

    在练习-生产者消费者问题这个练习中,是用wait(), notify(), notifyAll实现了。

    接下来使用Condition对象的:await, signal,signalAll 方法实现同样的效果

    package multiplethread;
     
    import java.util.LinkedList;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
     
    public class MyStack<T> {
     
        LinkedList<T> values = new LinkedList<T>();
     
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
     
        public void push(T t) {
            try {
                lock.lock();
                while (values.size() >= 200) {
                    try {
                        condition.await();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                condition.signalAll();
                values.addLast(t);
            } finally {
                lock.unlock();
            }
     
        }
     
        public T pull() {
            T t=null;
            try {
                lock.lock();
                while (values.isEmpty()) {
                    try {
                        condition.await();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                condition.signalAll();
                t= values.removeLast();
            } finally {
                lock.unlock();
            }
            return t;
        }
     
        public T peek() {
            return values.getLast();
        }
    }
    

    题目--使用AtomicInteger来替换Hero类中的synchronized

    在给Hero的方法加上修饰符synchronized 这个知识点中,通过给hurt和 recover方法加上synchronized来达到线程安全的效果。

    这一次换成使用AtomicInteger来解决这个问题

    提示:int基本类型对应的是AtomicInteger,但是float基本类型没有对应的AtomicFloat。 所以在这个练习中,把hp改为AtomicInteger即可。

  • 相关阅读:
    指针和数组的关系
    深入学习数组
    const关键字与指针
    野指针是什么
    指针带来的一些符号的理解
    指针的本质
    内存管理之堆
    内存管理之栈
    元类
    断点调式和面向对象进阶
  • 原文地址:https://www.cnblogs.com/czy16/p/8996587.html
Copyright © 2020-2023  润新知