java进阶打卡19

等待与唤醒机制:线程之间的通信(续)

  • 资源类
public class BaoZi {
    String pi;
    String xian;
    // 包子的状态:设置初始值为false没有包子
    boolean flag = false;
}
  • 生产者(包子铺)类
注意:
    包子铺线程和包子线程关系-->通信(互斥)
    必须使用同步技术保证两个线程只能有一个在执行
    锁对象必须保证唯一,可以使用包子对象作为锁对象
    包子铺类和吃货的类就需要把包子对象作为参数传递进来
        1. 需要在成员位置创建一个包子变量
        2. 使用带参数构造方法,为这个包子变量赋值

public class BaoZiPu extends Thread{
    private BaoZi bz;
    public BaoZiPu(BaoZi bz) {
        this.bz = bz;
    }

    @Override
    public void run() {
        int count = 0;
		// 让包子铺一直生成包子
        while(true){
            synchronized (bz){
                if(bz.flag == true){
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                // 增加一些趣味性:交替生成两种包子
                if(count % 2 == 0){
                    bz.pi = "薄皮";
                    bz.xian = "三鲜馅";
                }else{
                    bz.pi = "冰皮";
                    bz.xian = "牛肉大葱馅";
                }
                count++;
                System.out.println("包子铺正在生成:" + bz.pi + bz.xian + "包子");
                //生成包子需要3秒钟
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                
                //修改包子的状态为true有
                bz.flag = true;
                bz.notify();
                System.out.println("包子铺已经生产好了:" + bz.pi + bz.xian + "包子,吃货可以开始吃了");
            }
        }
    }
}
  • 消费者(吃货)类
public class ChiHuo extends Thread{
    private BaoZi bz;
    public ChiHuo(BaoZi bz) {
        this.bz = bz;
    }

    @Override
    public void run() {
        //使用死循环,让吃货一直吃包子
        while(true){
            synchronized (bz){
                if(bz.flag == false){
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                System.out.println("吃货正在吃:" + bz.pi + bz.xian + "的包子");
                
                //修改包子的状态为false没有
                bz.flag = false;
                bz.notify();
                System.out.println("吃货已经把" + bz.pi + bz.xian + "的包子吃完了,包子铺开始生产包子");
                System.out.println("====================================================");
            }
        }
    }
}
  • 测试类
public class Demo {
    public static void main(String[] args) {
        BaoZi bz = new BaoZi();
        new BaoZiPu(bz).start();
        new ChiHuo(bz).start();
    }
}

线程池

  • 原理
线程池:容器-->集合(ArrayList,HashSet,【LinkedList<Thread>】,HashMap)

集合:
add(new Thread(xxx));
add(new Thread(xxx));	类似队列
add(new Thread(xxx));
...

当程序第一次启动的时候,创建多个线程,保存到一个集合中,当我们想要使用线程的时候,就可以从集合中取出来线程使用
Thread t = list.remove(0); 返回的是被移除的元素(线程只能被一个任务使用)
Thread t = linked.removeFist(); 
当我们使用完毕线程,需要把线程归还给线程池
list.add(t);
linked.addLast(t);

在JDK1.5之后,JDK内置了线程池,我们可以直接使用

线程池:			 		线程3	线程2	线程2
						【任务1、任务2、任务3获得线程对象执行任务】
入口-->	任务5	任务4	任务3	任务2	任务1	出口-->
【线程池中已无空闲线程,任务4、任务5等待执行,等待其他某个任务执行完毕后,归还线程到线程池,再从线程池中获取线程,执行任务】

合理利用线程池能够带来三个好处:
1. 降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
2. 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
3. 提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线程的数目,防止因为消耗过多内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。
  • 代码实现
线程池:JDK1.5之后提供的
java.util.concurrent.Executors:线程池的工厂类,用来生产线程池
Executors类中的静态方法:
    static ExecutorService newFixedThreadPool(int nThreads) 创建一个可重用固定线程数的线程池
    参数:
        int nThreads:创建线程池中包含的线程数量
    返回值:
        ExecutorService接口,返回的是ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接收(面向接口编程)
java.util.concurrent.ExecutorService:线程池接口
    用来从线程池中获取线程,调用start方法,执行线程任务
        submit(Runnable task) 提交一个Runnable任务用于执行
    关闭/销毁线程池的方法
        void shutdown()
        
线程池的使用步骤:
1. 使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
例如:	ExecutorService es = Executors.newFixedThreadPool(2);
2. 创建一个类,实现Runnable接口,重写run方法,设置线程任务
例如:	public class RunnableImpl implements Runnable{
		 	@Override
    		public void run() {
    		...
    		}
    	}	
3. 调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
例如:	es.submit(new RunnableImpl());

注意:线程池会一直开启,使用完了线程,会自动把线程归还给线程池,线程可以继续使用
 	es.submit(new RunnableImpl()); // pool-1-thread-1创建了一个新的线程执行
    es.submit(new RunnableImpl()); // pool-1-thread-1创建了一个新的线程执行
    es.submit(new RunnableImpl()); // pool-1-thread-2创建了一个新的线程执行
4. 调用ExecutorService中的方法shutdown()销毁线程池(不建议执行)
例如:	es.shutdown();

注意:线程池没有了继续获取线程会抛异常
	es.submit(new RunnableImpl()); // 抛异常
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java员工打卡签到代码的实现方式有很多种,以下是其中一种可能的实现方式: ```java import java.util.Date; public class Employee { private String name; private Date lastSignIn; public Employee(String name) { this.name = name; } public void signIn() { Date now = new Date(); System.out.println(name + "签到成功,时间:" + now); lastSignIn = now; } public void signOut() { Date now = new Date(); System.out.println(name + "签退成功,时间:" + now); } public void checkInStatus() { if (lastSignIn == null) { System.out.println(name + "尚未签到"); } else { System.out.println(name + "上次签到时间:" + lastSignIn); } } } ``` 上面的代码定义了一个`Employee`类,其中包含了员工的姓名和上次签到时间。类中有三个方法:`signIn()`、`signOut()`和`checkInStatus()`。`signIn()`方法表示员工签到,会打印出员工姓名和当前时间,并将当前时间记录为上次签到时间;`signOut()`方法表示员工签退,会打印出员工姓名和当前时间;`checkInStatus()`方法表示查询员工的签到状态,会打印出员工姓名和上次签到时间(如果已经签到过),否则会提示尚未签到。 如果要使用这段代码,可以在其他类中创建`Employee`对象,并调用其中的方法来完成打卡签到功能。例如: ```java public class Main { public static void main(String[] args) { Employee emp1 = new Employee("张三"); emp1.signIn(); emp1.checkInStatus(); emp1.signOut(); } } ``` 这段代码创建了一个名为`emp1`的`Employee`对象,姓名为“张三”。接着调用了`signIn()`方法进行签到,`checkInStatus()`方法查询签到状态,最后调用了`signOut()`方法进行签退。运行这段代码后,会打印出以下结果: ``` 张三签到成功,时间:Thu Jul 22 14:47:23 CST 2021 张三上次签到时间:Thu Jul 22 14:47:23 CST 2021 张三签退成功,时间:Thu Jul 22 14:47:28 CST 2021 ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值