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的有限等待时间,解决死锁问题

猜你喜欢

转载自www.cnblogs.com/czy16/p/8996587.html