Day24 多线程2 线程通信、线程组和线程池

线程通信

应用场景:生产者和消费者问题
假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费
如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止
如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止
这里写图片描述
没有实现线程通信的解决方案:

/**
 * 商品类
 * @author Administrator
 *
 */
public class Product {

    private String name;//名称   馒头  玉米饼
    private String color;//颜色  白色  黄色


    public Product() {
        super();
    }
    public Product(String name, String color) {
        super();
        this.name = name;
        this.color = color;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    @Override
    public String toString() {
        return "Prodcut [name=" + name + ", color=" + color + "]";
    }
}


/**
 * 消费者线程
 * @author Administrator
 *
 */
public class ConsumeRunnable implements Runnable{

    private Product product;


    public ConsumeRunnable(Product product) {
        super();
        this.product = product;
    }

    @Override
    public void run() {
        while(true){
            System.out.println("消费者消费商品:"+product.getName()+"  "+product.getColor());
        }       
    }

}


/**
 * 生产者线程
 * @author Administrator
 *
 */
public class ProduceRunnable implements Runnable {
    private Product product;


    public ProduceRunnable(Product product) {
        super();
        this.product = product;
    }


    @Override
    public void run() {
        int i = 0;

        while(true){        

            if(i%2==0){
                product.setName("馒头");
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                product.setColor("白色");
            }else{
                product.setName("玉米饼");
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                product.setColor("黄色");
            }

            System.out.println("生产者生成商品:"+product.getName()+"  "+product.getColor());

            i++;
        }
    }

}



/**
 * 功能:生产者-消费者问题
 * 技能:线程通信
 * 
  * 业务分析:
  * 1.生产和消费交替进行
 *  2.生产者交替的生产白色馒头和黄色的玉米饼
 *  3.生产者------仓库-------消费者(仓库中只能存放一件商品)
 *  
 * 类的设计
 *  1.定义一个商品Product类
 *  2.定义两个线程:生产者线程,消费者线程
 *  3.定义测试类:创建线程并启动
 *  
 * 注意
 *  1.生产者线程和消费者线程是两个不同的线程
 *  2.之前的取款线程、选手线程只要执行代码,不同线程之间没有交流
 *  3.现在的生产者线程、消费者线程需要相互通信(还有商品吗?没有,告诉生产者,生产吧)
 *  4.同样也需要线程同步,保证安全性(线程通信的前提是线程同步) 
 * 
 * 
 * 缺点1:没有实现交替的生成和消费
 * 
 * 
 * 缺点2:没有出现真正的商品
 * 解决:引入商品类
 * 
 * 缺点3:消费者消费的怎么不是生成者生产的
 * 原因:生产者和消费者的商品不是同一个商品
 * 解决:保证生产者和消费者的商品是同一个商品
 * 
 * 
 * 
 * @author Administrator
 *
 */
public class Test {

    public static void main(String[] args) {
        Product product = new Product();
        //创建一个生产者线程和消费者线程
        ProduceRunnable pr = new ProduceRunnable(product);
        Thread t1 = new Thread(pr);

        ConsumeRunnable cr = new ConsumeRunnable(product);
        Thread t2 = new Thread(cr);

        //启动生产者线程和消费者线程开始生产和消费
        t1.start();
        t2.start();

    }

}

分析
这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件

对于生产者,没有生产产品之前,要通知消费者等待。而生产了产品之后,又需要马上通知消费者消费
对于消费者,在消费之后,要通知生产者已经消费结束,需要继续生产新产品以供消费

在生产者消费者问题中,仅有synchronized是不够的
synchronized可阻止并发更新同一个共享资源,实现了同步
synchronized不能用来实现不同线程之间的消息传递(通信)
这里写图片描述

使用同步代码块和线程通信解决:

/**
 * 商品类
 * @author Administrator
 *
 */
public class Product {

    private String name;//名称   馒头  玉米饼
    private String color;//颜色  白色  黄色
    boolean flag = false;//没有商品

    public Product() {
        super();
    }
    public Product(String name, String color) {
        super();
        this.name = name;
        this.color = color;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    @Override
    public String toString() {
        return "Prodcut [name=" + name + ", color=" + color + "]";
    }

}


/**
 * 生产者线程
 * @author Administrator
 *
 */
public class ProduceRunnable implements Runnable {
    private Product product;


    public ProduceRunnable(Product product) {
        super();
        this.product = product;
    }


    @Override
    public void run() {
        int i = 0;

        while(true){        
            synchronized(product){
                //如果有商品,就等待
                if(product.flag){
                    try {
                        product.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }


                //生产
                if(i%2==0){
                    product.setName("馒头");
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    product.setColor("白色");
                }else{
                    product.setName("玉米饼");
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    product.setColor("黄色");
                }

                System.out.println("生产者生成商品:"+product.getName()+"  "+product.getColor());

                //修改flag的状态
                product.flag = true;

                //生产完毕,唤醒消费者
                product.notify();

            }           
            i++;
        }
    }
}


/**
 * 消费者线程
 * @author Administrator
 *
 */
public class ConsumeRunnable implements Runnable{

    private Product product;

    public ConsumeRunnable(Product product) {
        super();
        this.product = product;
    }



    @Override
    public void run() {
        while(true){
            synchronized(product){
                //如果没有商品,就等待
                if(!product.flag){
                    try {
                        product.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //消费
                System.out.println("消费者消费商品:"+product.getName()+"  "+product.getColor());

                //修改flag的状态
                product.flag = false;

                //消费完毕,唤醒生产者
                product.notify();

            }
        }

    }

}


/**
 * 功能:生产者-消费者问题
 * 技能:线程通信
 * 
  * 业务分析:
  * 1.生产和消费交替进行
 *  2.生产者交替的生产白色馒头和黄色的玉米饼
 *  3.生产者------仓库-------消费者(仓库中只能存放一件商品)
 *  
 * 类的设计
 *  1.定义一个商品Product类
 *  2.定义两个线程:生产者线程,消费者线程
 *  3.定义测试类:创建线程并启动
 *  
 * 注意
 *  1.生产者线程和消费者线程是两个不同的线程
 *  2.之前的取款线程、选手线程只要执行代码,不同线程之间没有交流
 *  3.现在的生产者线程、消费者线程需要相互通信(还有商品吗?没有,告诉生产者,生产吧)
 *  4.同样也需要线程同步,保证安全性(线程通信的前提是线程同步) 
 * 
 * 
 * 缺点1:没有实现交替的生成和消费
 * 
 * 
 * 缺点2:没有出现真正的商品
 * 解决:引入商品类
 * 
 * 缺点3:消费者消费的怎么不是生成者生产的
 * 原因:生产者和消费者的商品不是同一个商品
 * 解决:保证生产者和消费者的商品是同一个商品
 *      创建一个Product对象,通过两个线程的构造方法传入
 * 
 * 缺点4:消费者消费了白色的玉米饼和黄色的馒头
 * 原因:生产者在生产商品过程中出现了线程的切换,例如修改了name后还没有修改color,就sleep了
 *  然后消费者获取CPU并执行,出现错误
 * 解决:线程同步
 * 强调:1.不仅生产者要加锁,而且消费者也要加锁,并且还必须是同一把锁
 *       2.保证不会出现黄色的馒头和白色的玉米饼,保证了线程安全,但是还是不能实现生产和消费的交替
 * 
 * 缺点1:没有实现交替的生成和消费
 * 解决:使用线程通信,让两个不同功能的线程实现交替执行
 *      obj.wait();
        obj.wait(timeout);
        obj.notify();
        obj.notifyAll();


 * @author Administrator
 *
 */
public class Test {

    public static void main(String[] args) {
        Product product = new Product();
        //创建一个生产者线程和消费者线程
        ProduceRunnable pr = new ProduceRunnable(product);
        Thread t1 = new Thread(pr);

        ConsumeRunnable cr = new ConsumeRunnable(product);
        Thread t2 = new Thread(cr);


        //启动生产者线程和消费者线程开始生产和消费
        t1.start();
        t2.start();

    }
}

线程同步的细节:

线程同步的细节

细节1:进行线程通信的多个线程,要使用同一个同步监视器(product),还必须要调用该同步监视器
的wait()、notify()、notifyAll(); *

细节2:线程通信的三个方法
wait() 等待
在【其他线程】调用【此对象】的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
换句话说,此方法的行为就好像它仅执行 wait(0) 调用一样。
当前线程必须拥有此对象监视器。
wait(time) 等待
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。
当前线程必须拥有此对象监视器。

notify() 通知 唤醒 *
唤醒在【此对象监视器】上等待的【单个】线程。
如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。【选择是任意性的】,并在对实现做出决定时发生 *
notifyAll() 通知所有 唤醒所有
唤醒在【此对象监视器】上等待的【所有】线程。
被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程【进行竞争】;

细节3:完整的线程生命周期
阻塞状态有三种
普通的阻塞 sleep,join,Scanner input.next()
同步阻塞(锁池队列) 没有获取同步监视器的线程的队列
等待阻塞(阻塞队列) 被调用了wait()后释放锁,然后进行该队列
这里写图片描述
细节4:sleep()和yield()的区别;sleep()和wait()的区别
区别1:sleep() 线程会让出CPU进入阻塞状态,但不会释放对象锁
wait() 线程会让出CPU进入阻塞状态, 也会放弃对象锁,进入等待此对象的等待锁定池
进入的阻塞状态也是不同的队列
区别2:wait只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用

修改商品类,用同步方法解决:

/**
 * 商品类
 * @author Administrator
 *
 */
public class Product {

    private String name;//名称   馒头  玉米饼
    private String color;//颜色  白色  黄色
    boolean flag = false;//没有商品

    public Product() {
        super();
    }
    public Product(String name, String color) {
        super();
        this.name = name;
        this.color = color;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    @Override
    public String toString() {
        return "Prodcut [name=" + name + ", color=" + color + "]";
    }

    public synchronized void produce(String name,String color){//this
        //如果有商品,就等待
        if(flag){ 
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }       

        //生产        

        this.name = name;
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.color =color;      

        System.out.println("生产者生成商品:"+name+"  "+color);

        //修改flag的状态
        flag = true;

        //生产完毕,唤醒消费者
        notify();
    }


    public synchronized void consume(){//this
        //如果没有商品,就等待
        if(!this.flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //消费
        System.out.println("消费者消费商品:"+name+"  "+color);

        //修改flag的状态
        flag = false;

        //消费完毕,唤醒生产者
        this.notify();
    }
}

给生产者和消费者之间添加传递者:

/**
 * 商品类
 * @author Administrator
 *
 */
public class Product {

    private String name;//名称   馒头  玉米饼
    private String color;//颜色  白色  黄色
    //boolean flag = false;//没有商品
    private int flag = 1;//1 没有商品   2 有商品要传递  3 有传递要消费 
    public Product() {
        super();
    }
    public Product(String name, String color) {
        super();
        this.name = name;
        this.color = color;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    @Override
    public String toString() {
        return "Prodcut [name=" + name + ", color=" + color + "]";
    }

    public synchronized void produce(String name,String color){//this
        //如果有商品,就等待
        while(flag !=1 ){ //3
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }       

        //生产            
        this.name = name;
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.color =color;      

        System.out.println("生产者生成商品:"+name+"  "+color);

        //修改flag的状态
        flag = 2;

        //生产完毕,唤醒消费者
        notifyAll();
    }

    public synchronized void deliver() {
        //如果有商品,就等待
        while(flag !=2 ){ 
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }       

        //传递                
        System.out.println("传递者传递商品:"+name+"  "+color);

        //修改flag的状态
        flag = 3;

        //生产完毕,唤醒消费者
        notifyAll();

    }

    public synchronized void consume(){//this
        //如果没有商品,就等待
        while( flag !=3 ){//2
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //消费
        System.out.println("消费者消费商品:"+name+"  "+color);

        //修改flag的状态
        flag = 1;

        //消费完毕,唤醒生产者
        this.notifyAll();
    }

}


public class DeliverRunnable implements Runnable {

    private Product product;

    public DeliverRunnable(Product product) {
        super();
        this.product = product;
    }

    @Override
    public void run() {
        while(true){
            product.deliver();
        }       
    }
}


public class Test {

    public static void main(String[] args) {
        Product product = new Product();
        //创建一个生产者线程和消费者线程
        ProduceRunnable pr = new ProduceRunnable(product);
        Thread t1 = new Thread(pr);

        DeliverRunnable dr = new DeliverRunnable(product);
        Thread t3 = new Thread(dr);

        ConsumeRunnable cr = new ConsumeRunnable(product);
        Thread t2 = new Thread(cr);


        //启动生产者线程和消费者线程开始生产和消费
        t1.start();
        t2.start();
        t3.start();

    }
}


线程组

线程组
线程组表示一个线程的集合。
线程组也可以包含其他线程组。线程组构成一棵树。在树中,除了初始线程组外,每个线程组都有一个父线程组。
顶级线程组名system,线程的默认线程组名称是main
在创建之初,线程被限制到一个组里,而且不能改变到一个不同的组

这里写图片描述

线程组的作用
统一管理:便于对一组线程进行批量管理线程或线程组对象
安全隔离:允许线程访问有关自己的线程组的信息,但是不允许它访问有关其线程组的父线程组或其他任何线程组的信息

查看ThreadGroup、Thread构造方法代码,观察默认线程组的情况
测试代码:

/**
 * 线程组
 * @author Administrator
 *
 */
public class TestGroup {

    public static void main(String[] args) {
        //创建一个线程组
        ThreadGroup tg = new ThreadGroup("乌龟线程组");
        //给该线程组指定两个线程
        Thread thread1 = new TortoiseThread(tg, "乌龟线程1");
        Thread thread2 = new TortoiseThread(tg, "乌龟线程2");

        //获取线程组信息
        System.out.println(tg.getName());
        System.out.println(tg.getParent().getName());
        System.out.println(tg.getParent().getParent().getName());


        System.out.println(thread1.getName());
        System.out.println(thread1.getThreadGroup().getName());
        System.out.println(thread1.getThreadGroup().getParent().getName());

        tg.setDaemon(true);
//      thread1.setDaemon(true);
//      thread2.setDaemon(true);
        tg.setMaxPriority(9);

        thread1.start();
        thread2.start();

        System.out.println(tg.activeCount());
    }


    public void method1(){
        //创建两个线程
        Thread thread1 = new TortoiseThread();
        Thread thread2 = new TortoiseThread("乌龟线程");
        //获取该线程的线程组信息
        System.out.println(thread1.getName());
        System.out.println(thread2.getName());

        System.out.println(thread1.getThreadGroup().getName());
        System.out.println(thread2.getThreadGroup().getName());


        System.out.println(thread1.getThreadGroup().getParent().getName());
        System.out.println(thread1.getThreadGroup().getParent().getParent().getName());
    }

}


class TortoiseThread extends Thread{

    public TortoiseThread() {
        super();
    }

    public TortoiseThread(String name) {
        super(name);
    }



    public TortoiseThread(ThreadGroup group, String name) {
        super(group, name);
    }

    /**
     * 线程体,线程要完成的任务
     */
    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("乌龟领先了,加油!!!,线程名称:"+this.getName()
                        +",线程优先级别:"+this.getPriority());
        }

    }
}



线程池

什么是线程池
创建和销毁对象是非常耗费时间的
创建对象:需要分配内存等资源
销毁对象:虽然不需要程序员操心,但是垃圾回收器会在后台一直跟踪并销毁

对于经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
思路:创建好多个线程,放入线程池中,使用时直接获取引用,不使用时放回池中。可以避免频繁创建销毁、实现重复利用

生活案例:在尚学堂借用和归还电脑,共享单车
技术案例:线程池、数据库连接池

JDK1.5起,提供了内置线程池

线程池的好处
提高响应速度(减少了创建新线程的时间)
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
提高线程的可管理性:避免线程无限制创建、从而销耗系统资源,降低系统稳定性,甚至内存溢出或者CPU耗尽

线程池的应用场合
需要大量线程,并且完成任务的时间端
对性能要求苛刻
接受突发性的大量请求

使用线程池执行大量的Runnable命令

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 使用线程池执行大量的Runnable命令
 * @author Administrator
 *
 */
public class TestPool1 {

    public static void main(String[] args) {
        //创建一个线程池
        //线程数量浮动变化的线程池  
        //ExecutorService pool1 = Executors.newCachedThreadPool();
        //固定数量线程的线程池10
        //ExecutorService pool2 = Executors.newFixedThreadPool(10);
        //只有一个线程,保证一直有一个
        ExecutorService pool1 = Executors.newSingleThreadExecutor();

        //使用线程池执行多个命令
        for(int i=0;i<20;i++){
            final int n = i;
            //创建一个线程
            Runnable command = new Runnable(){
                @Override
                public void run() {                 
                    System.out.println("线程开始"+n);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程结束"+n);
                }               
            };
            //交个线程池执行
            pool1.execute(command);
        }

        //关闭线程池
        pool1.shutdown();


    }

}

使用线程池执行大量的Callable任务

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 使用线程池执行大量的Callable任务
 * Executor  线程池的顶级接口  只有一个方法 execute
 * ExecutorService:线程池的真正接口   shutdown  submit(task)...
 * Executors :工具类 
 * ThreadPoolExecutor 线程池的实现类
 * 了解ThreadPoolExecutor构造方法的七个参数的含义
 * @author Administrator
 *
 */
public class TestPool2 {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        //创建线程池
        ExecutorService  pool = Executors.newCachedThreadPool();
        //使用线程池执行多个任务
        List<Future> futures = new ArrayList<Future>();
        for(int i=0;i<20;i++){
            //创建一个任务
            Callable<Integer> task = new RandomCallable();
            //使用线程执行该任务
            Future<Integer>  future = pool.submit(task);
            //输出结果
            //System.out.println(future.get());//需要等线程结束有了结果才执行
            futures.add(future);
        }

        for(Future future :futures){
            System.out.println(future.get());
        }

        //关闭线程池
        pool.shutdown();

    }

}

class RandomCallable implements Callable<Integer>{
    /**
     * 线程体,相当于之前的run()
     */
    @Override
    public Integer call() throws Exception {
        Thread.sleep(2000);
        return new Random().nextInt(10);
    }
}

线程池的类结构

Executor:线程池顶级接口,只有一个方法
ExecutorService:真正的线程池接口
void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable
Future submit(Callable task):执行任务,有返回值,一般又来执行Callable
void shutdown() :关闭连接池
AbstractExecutorService:基本实现了ExecutorService的所有方法
ThreadPoolExecutor:默认的线程池实现类
ScheduledThreadPoolExecutor:实现周期性任务调度的线程池

Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池
Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
这里写图片描述

理解线程池的构造方法

    public ThreadPoolExecutor(
        int corePoolSize,
          int maximumPoolSize,
          long keepAliveTime,
          TimeUnit unit,
          BlockingQueue<Runnable> workQueue,
          ThreadFactory threadFactory,
          RejectedExecutionHandler handler) {

    }

corePoolSize:核心池的大小
默认情况下,创建了线程池后,线程数为0,当有任务来之后,就会创建一个线程去执行任务。
但是当线程池中线程数量达到corePoolSize,就会把到达的任务放到队列中等待。
maximumPoolSize:最大线程数。
corePoolSize和maximumPoolSize之间的线程数会自动释放,小于等于corePoolSize的不会释放。
当大于了这个值就会将任务由一个丢弃处理机制来处理。
keepAliveTime:线程没有任务时最多保持多长时间后会终止
默认只限于corePoolSize和maximumPoolSize之间的线程
TimeUnit:
keepAliveTime的时间单位
BlockingQueue:
存储等待执行的任务的阻塞队列,有多中选择,可以是顺序队列、链式队列等。
ThreadFactory
线程工厂,默认是DefaultThreadFactory,Executors的静态内部类
RejectedExecutionHandler:
拒绝处理任务时的策略。如果线程池的线程已经饱和,并且任务队列也已满,对新的任务应该采取什么策略。
比如抛出异常、直接舍弃、丢弃队列中最旧任务等,默认是直接抛出异常。
1、CallerRunsPolicy:如果发现线程池还在运行,就直接运行这个线程
2、DiscardOldestPolicy:在线程池的等待队列中,将头取出一个抛弃,然后将当前线程放进去。
3、DiscardPolicy:什么也不做
4、AbortPolicy:java默认,抛出一个异常:

小结

线程通信
线程组
线程池

多线程编码题练习:
1.编写两个线程,一个线程打印1-52的整数,另一个线程打印字母A-Z。打印顺序为12A34B56C….5152Z。即按照整数和字母的顺序从小到大打印,并且每打印两个整数后,打印一个字母,交替循环打印,直到打印到整数52和字母Z结束。
要求:
1)编写打印类Printer,声明私有属性index,初始值为1,用来表示是第几次打印。
2)在打印类Printer中编写打印数字的方法print(int i),3的倍数就使用wait()方法等待,否则就输出i,使用notifyAll()进行唤醒其它线程。
3)在打印类Printer中编写打印字母的方法print(char c),不是3的倍数就等待,否则就打印输出字母c,使用notifyAll()进行唤醒其它线程。
4)编写打印数字的线程NumberPrinter继承Thread类,声明私有属性private Printer p;在构造方法中进行赋值,实现父类的run方法,调用Printer类中的输出数字的方法。
5)编写打印字母的线程LetterPrinter继承Thread类,声明私有属性private Printer p;在构造方法中进行赋值,实现父类的run方法,调用Printer类中的输出字母的方法。
6)编写测试类Test,创建打印类对象,创建两个线程类对象,启动线程。

打印类(共享资源)
public class Printer {
    private int index=1;//设为1,方便计算3的倍数  
    //打印数字的方法,每打印两个数字,等待打印一个字母
    public synchronized void print(int i){
        while(index%3==0){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.print(""+i);
        index++;
        notifyAll();
    }
    //打印字母,每打印一个字母,等待打印两个数字
    public synchronized void print(char c){
        while(index%3!=0){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.print(""+c);
        index++;
        notifyAll();
    }
}
打印字母线程
public class LetterPrinter extends Thread {
    private Printer p;
    public LetterPrinter(Printer p){
        this.p=p;
    }
    public void run(){
        for(char c='A';c<='Z';c++){
            p.print(c);
        }
    }
}
打印数字线程
 public class NumberPrinter extends Thread {
    private Printer p;
    public NumberPrinter(Printer p){
        this.p=p;
    }
    public void run(){
        for(int i=1;i<=52;i++){
            p.print(i);
        }
    }
}
测试类
public class Test {
    public static void main(String[] args) {
        Printer p=new Printer();     //创建打印机对象
        Thread t1=new NumberPrinter(p);  //创建线程对象
        Thread t2=new LetterPrinter(p); //创建线程对象
        t1.start();  //启动线程
        t2.start(); //启动线程
    }
}

2.设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。
要求:使用内部类实现线程,对j增减的时候不考虑顺序问题.

public class ThreadTest {                    //创建类
    private int j;                           //声明成员变量j
    public static void main(String[] args) { //程序主入口
        ThreadTest tt=new ThreadTest();      //创建ThreadTest的对象
        //创建内部类对象
        Inc inc=tt.new Inc();                //创建内部线程类对象
        Dec dec=tt.new Dec();                //创建内部线程类对象
        for(int i=0;i<2;i++){
            // 创建线程对象,启动线程
            Thread t=new Thread(inc);
            t.start();
             // 创建线程对象,启动线程
            t=new Thread(dec);
            t.start();
        }
    }
    //实现同步对j的值加
    private synchronized void inc(){
        j++;                                  //j++
        System.out.println(Thread.currentThread().getName()+"-inc:"+j);
    }
//实现同步对j的值减
    private synchronized void dec(){
        j--;
        System.out.println(Thread.currentThread().getName()+"-dec:"+j);
    }
    //内部类
      //内部类定义正确并实现run方法
    class Inc implements Runnable{
        public void run() {
            for (int i = 0; i <100; i++) {
                inc();                      //调用加的方法
            }
        }
    }
    //内部类定义正确并实现run 方法
    class Dec implements Runnable{
        public void run() {
            for (int i = 0; i <100; i++) {
                dec();                         //调用减的方法
            }
        }
    }
}

3.编写多线程程序,模拟多个人通过一个山洞的模拟。这个山洞每次只能通过一个人,每个人通过山洞的时间为5秒,有10个人同时准备过此山洞,显示每次通过山洞人的姓名和顺序。

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.omg.CORBA.PUBLIC_MEMBER;

/**
 * 2.编写多线程程序,模拟多个人通过一个山洞的模拟。这个山洞每次只能通过一个人,
 * 每个人通过山洞的时间为5秒,有10个人同时准备过此山洞,显示每次通过山洞人的姓名和顺序。
 * @author zhangpeng
 *
 */
public class Test {

    public static void main(String[] args) {
        Cave cave=new Cave();
        for (int i = 0; i < 10; i++) {
            new Thread(cave).start();
        }


    }

}

import java.util.concurrent.Callable;

public class Cave implements Runnable {
     int count=0;
    @Override
    public void run() {
        synchronized (this) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            count++;
            System.out.println(Thread.currentThread().getName()+"通过山洞,你是第"+count+"位");
        }
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值