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即可。

posted @ 2018-05-05 22:46  Pororo  阅读(571)  评论(0编辑  收藏  举报