文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java线程创建(卖票),线程同步(卖包子)的实现示例

2024-04-02 19:55

关注

1.线程两种创建方式:new Thread(new Runnable() {})

在这里插入图片描述

如下FileOutputStream源码中抛出异常,为了让写代码人自己写try catch异常提示信息。

在这里插入图片描述


package com.itheim07.thread;

public class ThreadDemo {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    System.out.println("播放音乐...");
                }
            }
        }).start();  //.start()不能改成.run()

        boolean result = true;
        while(result){ 
            System.out.println("下载电影...");
        }
       
    }
}

如下线程第一种创建方式。

在这里插入图片描述


package com.itheima01.thread;

public class ThreadDemo02 {
    public static void main(String[] args) {
//        Thread thread = Thread.currentThread();
//        String name = thread.getName(); 
//        System.out.println(name); // main
        //下面一行等同于上面
        System.out.println("主:" + Thread.currentThread().getName());

        YourThread yt = new YourThread();
        yt.start(); //子:Thread-0        
        YourThread yt2 = new YourThread();
		yt.run(); //子:main。  因为子线程YourThread还未执行起飞	,被main飞机拖着走	
        YourThread yt3 = new YourThread();
        yt3.start(); //子:Thread-2。  不是Thread-1是因为yt2未起飞但依旧new了yt2

//        Person p = new Person(); //执行空参构造
//        System.out.println(p.number); //0 
//        Person p2 = new Person();
//        System.out.println(p2.number); //1
    }
}
class YourThread extends Thread{
    @Override
    public void run() {
        System.out.println("子:" + Thread.currentThread().getName());
    }
}
class Person{
    static int number=-1;
    public Person(){
        number++;
    }
}

package com.itheima02.runnable;

public class RunnableDemo {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable(); // 分配该类的实例
        Thread t = new Thread(mr);
        t.start();  //Thread-0
    }
}
class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

package com.itheima02.runnable;
//用匿名内部类简化上面代码
public class RunnableDemo02 {
    public static void main(String[] args) {		
     

//111111111111111111111111111111111111111111111111111111111111111111111111111111
       new Thread(new Runnable() {
           @Override
           public void run() { //主要关注run
               System.out.println(Thread.currentThread().getName());
           }
       }).start();
	   
//new Thread(() -> System.out.println(Thread.currentThread().getName())).start();
    }
}

2.卖票:原子性


package com.itheima03.ticket;

public class TicketDemo01 {
    public static void main(String[] args) {
        MyWindow mw1 = new MyWindow(); //堆中开一块空间,不加static,number=100进堆
        mw1.setName("窗口壹");

        MyWindow mw2 = new MyWindow(); //同上
        mw2.setName("窗口222");

        MyWindow mw3 = new MyWindow(); //同上
        mw3.setName("窗口三三三");        
        mw1.start();
        mw2.start();
        mw3.start();
    }
}

//11111111111111111111111111111111111111111111111111111111111111111111111111111
class MyWindow extends Thread{
    static int number = 100; //去掉static,每创建一个MyWindow窗口在堆里开辟一块空间,三个窗口各卖100张
    @Override
    public void run() {
       while(number > 0){
           System.out.println(Thread.currentThread().getName() + "正在卖出第" + number + "张票");
           number--;
       }
    }
}


如下线程同步问题分析:两种创建方式3个窗口都总卖出102张票,而不是100张。原因:三个窗口同时卡在打印正在卖出第100张票。解决:t1在卖第100张票时,cpu可能会切到t3和t2,可以控制t2和t3不动,等t1的number- -完再动。

在这里插入图片描述

3.线程同步:synchronized关键字,Lock接口,ThreadLocal


package com.itheima04.synchronizedd;
import java.io.IOException;

public class TicketDemo02 {
    public static void main(String[] args) {
        MyTask mt = new MyTask();        
        //上面只new了一个,可以用this
        Thread t1 = new Thread(mt);
        t1.setName("窗口壹");
        
        Thread t2 = new Thread(mt);
        t2.setName("窗口222");
        
        Thread t3 = new Thread(mt);
        t3.setName("窗口三三三");           
        t1.start();
        t2.start();
        t3.start();
    }
}

class MyTask implements Runnable{ 
    int number = 100;
//   Object obj = new Object();  //锁对象
    @Override
    public void run() {
        while(number > 0){

//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111        
            synchronized(MyTask.class){ //MyTask.class也可以换成this
                if(number <= 0){
                    break;  //跳出while大循环
                }
                System.out.println(Thread.currentThread().getName() + "正在卖出第" + number + "张票");
                number--;
            }  

//111111111111111111111111111111111111111111111111111111111111111111111111111111111111             
//这边只能try catch不能throws,原因:父类Runnable中run方法没有声明抛出编译异常,所以子类也不能throws        
            try {
                Thread.sleep(1); //线程啥事也不干,暂停1ms,cpu有空闲切换其他线程
            } catch (InterruptedException e) { 
                e.printStackTrace();
            }
        } //while里
    }
}

在这里插入图片描述

如下t2卖到0张时出while,而t1和t3还在while里,此时number=0,所以变为0和-1。

在这里插入图片描述

如下把synchronized拖到外面也不行。

在这里插入图片描述

如下加if(number <= 0),没有加浪费时间代码,所以看不到交替效果,但不会出现0和-1。

在这里插入图片描述

obj是锁对象即钥匙,如下钥匙不能进run方法(每个线程一把即三把钥匙了),只能在成员位置。

在这里插入图片描述

用this,不用new object(),可以节约内存。

在这里插入图片描述


package com.itheima05.method;

public class TicketDemo02 {
    public static void main(String[] args) {
        MyTask mt = new MyTask();        
        Thread t1 = new Thread(mt);
        t1.setName("窗口壹");
        
        Thread t2 = new Thread(mt);
        t2.setName("窗口222");
        
        Thread t3 = new Thread(mt);
        t3.setName("窗口三三三");        
        t1.start();
        t2.start();
        t3.start();
    }
}

class MyTask implements Runnable{
    static int number = 100;
    @Override
    public void run() { 
        while(number > 0){
            method(); //非静态方法可以调用静态方法       
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
       
    private static synchronized void method() { //静态方法不能和对象关键字如this相关  //同步方法效果  等价于 同步代码块
        if(number <= 0){
            return;  //break只能写在循环和switch里
        }
        System.out.println(Thread.currentThread().getName() + "正在卖出第" + number + "张票");
        number--;
    }
}

package com.itheima06.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TicketDemo02 {
    public static void main(String[] args) {
        MyTask mt = new MyTask();
        Thread t1 = new Thread(mt);
        t1.setName("窗口壹");
        
        Thread t2 = new Thread(mt);
        t2.setName("窗口222");
        
        Thread t3 = new Thread(mt);   
        t3.setName("窗口三三三"); 
        t1.start();
        t2.start();
        t3.start();
    }
}

class MyTask implements Runnable{
    int number = 100;
    Lock lock = new ReentrantLock(); //创建lock对象
    @Override
    public void run() {
        while(number > 0){

//1111111111111111111111111111111111111111111111111111111111111111111111111
            lock.lock();
            if(number <= 0){
//                System.out.println(Thread.currentThread().getName());
                lock.unlock(); // 注意: lock提供了锁的可视化操作(线程执行结束,要记得手动释放。厕所上完不能带走钥匙)//同步代码块return或break后是jvm自动释放锁。//这里不加lock.unlock()程序停不下来。
                break;
            }
            System.out.println(Thread.currentThread().getName() + "正在卖出第" + number + "张票");
            number--;
            lock.unlock();
        }
    }
}

如下ThreadLocal相当于一个map,key就是当前的线程,value就是需要存储的对象。

在这里插入图片描述

t1(…,User),如下情况可将User放入ThreadLocal中,每次通过.get拿到线程的User。

在这里插入图片描述

在这里插入图片描述

4.卖包子:wait,notify


package com.itheima07.bz;

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();       
// obj.wait(); //IllegalMonitorStateException : 非法的监视状态异常,因为.wait()必须锁对象调用如下
        synchronized (obj){  //对象变成锁对象
            obj.wait(); //不会报错,一直等待。在锁对象中
        }
    }
}

如下两个方法wait和notify不是给线程调用的,而是给锁对象【锁对象可以是任意对象】调用的如上所示。BaoZi只能一个线程对其操作。

在这里插入图片描述


package com.itheima07.bz;

public class BaoZi {
    boolean isHave=false; //默认没有包子
}

package com.itheima07.bz;

public class BaoziPu extends Thread {
    BaoZi bz;
    public BaoziPu(BaoZi bz){
        this.bz = bz;
    }
    @Override
    public void run() {       
        while(true){ //不停生产包子 

//111111111111111111111111111111111111111111111111111111111111111111111111111111           
            synchronized (bz){ //加锁: 同步代码,生产包子时不让别人打扰我。注意下面wait和notify
                if(bz.isHave){
                    try {
                        bz.wait(); //包子铺有包子就等待(此时吃货正在吃包子)
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("包子铺生产包子..."); //没包子
                bz.isHave = true;
                bz.notify(); //唤醒吃货
            }
        }  //while里
    }
}

package com.itheima07.bz;

public class ChiHuo  extends Thread{
    BaoZi bz;
    public ChiHuo(BaoZi bz){
        this.bz = bz;
    }
    @Override
    public void run() {
        while(true){ //不停吃包子

//1111111111111111111111111111111111111111111111111111111111111111111111111111               
            synchronized (bz){
                if(!bz.isHave){
                    try {
                        bz.wait(); //吃货没有包子就等待(此时包子铺正在生产包子)
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("吃货吃包子"); //有包子
                bz.isHave = false;       
                bz.notify(); //唤醒包子铺
            }
        }
    }
}

package com.itheima07.bz;

public class BzDemo {
    public static void main(String[] args) {
        BaoZi bz = new BaoZi();
        BaoziPu bzp = new BaoziPu(bz); //和下面一行共同操作一个包子对象
        ChiHuo ch = new ChiHuo(bz);        
        bzp.start();
        ch.start();
    }
}

在这里插入图片描述

如下第一次没有包子,所以绕过2中if到1。运行完1后就有包子了,1时间很短,cpu不切换线程,切换了也没用,因为2中syn…(bz)包子被锁住,就算切换到吃货线程进不去syn…(bz)里,所以1中notify唤不醒吃货线程。

1和2都在sy…(bz)里,bzp线程bz.wait()【有3个好处】进入等待状态即进入监视队列即等待包子被吃,吃货线程的synchronized锁被打开,有包子不会wait,执行3。

一个线程wait把自己停下来放入堆(监视队列)中,来年开春,另一个线程中3叫我起来干活。2和3对应,1和4对应。3唤醒了2中wait,但2没钥匙(锁)动不了(鬼压床),钥匙在吃货手上,所以3往后4执行释放锁,1234不停循环执行。

在这里插入图片描述

生产消费者模型:用户发请求来相当于包子铺生产包子即生产者服务器24小时开着相当于消费者一天24小时等包子吃。不会让消费者线程空转浪费cpu资源,所以没包子设置消费者线程为wait状态不占用cpu资源


package com.atguigu.test14;
// 线程通信是用来解决生产者与消费者问题。
public class Test14 {
	public static void main(String[] args) {
		Workbench tai = new Workbench(); //相当于包子	
		Cook c = new Cook("崔志恒", tai); //生产者
		Waiter w = new Waiter("翠花", tai);	//消费者
		c.start();
		w.start();
	}
}

//11111111111111111111111111111111111111111111111111111111111111111111111111
class Workbench{
	private static final int MAX = 10; //假设工作台上最多能够放10盘
	private int count; //count是共用的,要考虑线程安全	

	public synchronized void put(){ //同步方法,非静态方法来说,锁对象就是this //往工作台上放一盘菜
		if(count >= MAX){
			try {
				//生产者停下来,等待
				wait();//默认是this.wait(),所以上面必须加锁对象synchronized
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//上面是安全校验
		count++;		
		System.out.println(Thread.currentThread().getName() + "放了一盘菜,剩余:" + count);
		this.notify(); // 包子/工作台.notify()  //唤醒消费者
	}	

//1111111111111111111111111111111111111111111111111111111111111111111111111111
	public synchronized void take(){//从工作台上取走一盘菜
		if(count<=0){
			try {				
				wait(); //工作台没有菜,消费者应该停下来
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//上面是安全校验
		count--;
		System.out.println(Thread.currentThread().getName() + "取走一盘菜,剩余:" + count);
		this.notify();  //唤醒生产者
	}
}

//1111111111111111111111111111111111111111111111111111111111111111111111111
class Cook extends Thread{
	private Workbench tai;
	public Cook(String name, Workbench tai) {
		super(name);
		this.tai = tai;
	}
	public void run(){
		while(true){
			tai.put(); //封装了
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

//111111111111111111111111111111111111111111111111111111111111111111111
class Waiter extends Thread{
	private Workbench tai;	
	public Waiter(String name, Workbench tai) {
		super(name); //name属性在父类中已声明
		this.tai = tai;
	}
	public void run(){
		while(true){
			tai.take();
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

如下一直交替运行,不停。

在这里插入图片描述

如下线程6态:锁就是钥匙上厕所,限时等待就是sleep,记住下面三个红色。

在这里插入图片描述

如下B进不去不执行

在这里插入图片描述

到此这篇关于Java线程创建(卖票),线程同步(卖包子)的实现示例的文章就介绍到这了,更多相关Java线程创建同步内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯