多线程案例 Java

本文通过售票系统的实例,对比分析了线程安全问题及其解决方案。首先展示了一个不安全的售票程序,其中多个线程并发操作导致了数据错误。随后,引入synchronized关键字来确保线程安全,有效避免了数据竞争。此外,深入探讨了wait()和notify()方法的使用,以实现线程间的精细控制和同步,适用于生产者消费者模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、购票(不安全策略)

代码片段:

public class Main {
    private static int ticket = 10;
    public static void main(String[] args) {
    	Runnable r = new Runnable() {
    		public void run() {
    			while(ticket > 0) {
    				try {
    					ticket--;
    					Thread.sleep(1000);
    					System.out.println("卖出了一张票,还剩下"+ticket+"张票");
    				}catch (Exception e) {
    					e.printStackTrace();
    				}
    				
    			}
    		}
    	};
    	new Thread(r).start();
    	new Thread(r).start();
    	new Thread(r).start();
    	new Thread(r).start();
    }
}

显示结果:

卖出了一张票,还剩下6张票
卖出了一张票,还剩下5张票
卖出了一张票,还剩下5张票
卖出了一张票,还剩下5张票
卖出了一张票,还剩下2张票
卖出了一张票,还剩下2张票
卖出了一张票,还剩下2张票
卖出了一张票,还剩下2张票
卖出了一张票,还剩下0张票
卖出了一张票,还剩下0张票

2、购票(安全策略)

  • 给方法加入synchronized解决线程不安全问题

代码片段:

public class Main {
    private static int ticket = 10;
    public static void main(String[] args) {
    	Runnable r = new Runnable() {
    		public synchronized void run() {
    			while(ticket > 0) {
    				try {
    					ticket--;
    					Thread.sleep(1000);
    					System.out.println("卖出了一张票,还剩下"+ticket+"张票");
    				}catch (Exception e) {
    					e.printStackTrace();
    				}
    				
    			}
    		}
    	};
    	new Thread(r).start();
    	new Thread(r).start();
    	new Thread(r).start();
    	new Thread(r).start();
    }
}

显示结果:

卖出了一张票,还剩下9张票
卖出了一张票,还剩下8张票
卖出了一张票,还剩下7张票
卖出了一张票,还剩下6张票
卖出了一张票,还剩下5张票
卖出了一张票,还剩下4张票
卖出了一张票,还剩下3张票
卖出了一张票,还剩下2张票
卖出了一张票,还剩下1张票
卖出了一张票,还剩下0张票

注:第一个线程进入run()后,其他进程无法进入,直到第一个线程执行完毕才允许其他进程进入,而此时ticket已经为0,其他进程不能进入循环体,因此,退出。

其中,形如synchronized (Main.class) {}包含代码块的结构也能达到同样的效果。

3、wait()和notify()

代码片段:

public class Main {
	public static void main(String[] args) {
		new Thread() {
			public void run() {
				synchronized (Main.class) {
					System.out.println("T1 start!");
					try {
						Main.class.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println("T1 end!");
				}
			}
		}.start();
		new Thread() {
			public void run() {
				synchronized (Main.class) {
					System.out.println("T2 start!");
					Main.class.notify(); //唤醒
					System.out.println("T2 end!");
				}
			}
		}.start();
	}
}

结果显示:

T1 start!
T2 start!
T2 end!
T1 end!

解析:wait()和notify()一系列的方法,是属于对象的,不是属于线程的。它们用在线程同步时,synchronized语句块中。

wait()方法可以使线程进入等待状态,而notify()可以使等待的状态唤醒。这样的同步机制十分适合生产者、消费者模式:消费者消费某个资源,而生产者生产该资源。当该资源缺失时,消费者调用wait()方法进行自我阻塞,等待生产者的生产;生产者生产完毕后调用notify/notifyAll()唤醒消费者进行消费。

4、wait()notify()精细控制

代码片段:

public class Main {
	public static void main(String[] args) {
		final Object object = new Object();
		new Thread() {
			public void run() {
				synchronized (object) {
					System.out.println("T1 start!");
					try {
						object.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println("T1 end!");
				}
			}
		}.start();
		new Thread() {
			public void run() {
				synchronized (object) {
					System.out.println("T2 start!");
					object.notify();
					System.out.println("T2 end!");
				}
			}
		}.start();;
		new Thread() {
			public void run() {
				synchronized (object) {
					System.out.println("T3 start!");
					object.notify();
					System.out.println("T3 end!");
				}
			}
		}.start();;
		new Thread() {
			public void run() {
				synchronized (object) {
					System.out.println("T4 start!");
					try {
						object.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println("T4 end!");
				}
			}
		}.start();
	}
}

 

结果显示:

T1 start!
T2 start!
T2 end!
T1 end!
T3 start!
T3 end!
T4 start!

很容易看出来,T4,它启动了,但是wait了,后面已经没有线程了,它的wait永远不会有线程帮它notify了!于是,T4 (T4 end!没法执行到)它就这么等着!
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值