Java学习 -- 线程(3)

这一节主要讲解一个问题:线程的同步。其中主要的难点就是买票、生产和消费

线程同步产生的原因

if (票数大于零) { 卖一张票; 票数减1; }
现在如果有A、B、C三位顾客同时买票,假如最后剩下一张票,当A顾客去买票时,判断票数大于零,票数还没有减1,系统又检测顾客B票数大于零,让B也去买票了,C顾客也是。这样就导致了一张票同时卖给了A、B、C三个顾客。这主要是线程同步问题导致的,因为计算机的CPU速度很快在A还没买完票的同时又同时让B、C进入了线程导致的。CPU可以在任何时间点切换线程。

class A implements Runnable {
	public static int tickets = 100;
	public void run() {
		while(true) {
			if (tickets > 0) {
				System.out.printf("%s线程正在卖出第%d张票\n", 
					Thread.currentThread().getName(), tickets);
					--tickets;
			} else {
				break;
			}
		}
	}
}
public class TestTickets {
	public static void main(String[] args) {
		A aa1 = new A();
		A aa2 = new A();
		Thread t1 = new Thread(aa1);
		Thread t2 = new Thread(aa2);
		t1.start();
		t2.start();
	}
}

在这里插入图片描述
总结:他们处理的是同一个资源,所以就有线程同步和互斥的问题

买票例子改进 — synchronized

  • synchronized可以用来修饰:
    1. 一个方法
    2. 一个方法内部的某个代码块
Synchronized修饰代码块
  • 格式:
synchronized(类对象名aa) // 1行
{
	同步代码 // 3行
} // 4行
  • 功能:synchronized(类对象名aa)的含义是:判断aa是否已经被其他线程霸占,如果发现已经被其他线程霸占,则当前线程陷入等待中,如果发现aa没有被其他线程霸占,则当前线程霸占住aa对象,并执行第3行的同步代码块,在当前线程执行第3行代码块时,其他线程将无法再执行第3行的代码(因为当前线程已经霸占了aa对象),当前线程执行完第3行的代码块后,会自动释放对aa对象的霸占,此时其他线程会相互竞争对aa的霸占,最终CPU会选择其中某一个线程执行。
  • 最终导致的结果是:一个线程正在操作某资源的时候,将不允许其他线程操作该资源,即一次只允许一个线程处理该资源
Synchronized修饰方法
  • Synchronized修饰一个方法时,实际霸占的是该方法的this指针所指向的对象
  • 即Synchronized修饰一个方法时,实际霸占的是正在调用该方法的对象
  • 注意:霸占的专业术语叫锁定,霸占住的那个对象专业术语叫做监听器
class A implements Runnable {
	public int tickets = 100;
	String str = new String("哈哈");
	public void run() {
		while(true) {
			synchronized (str) {
				if (tickets > 0) {
				System.out.printf("%s线程正在卖出第%d张票\n", 
					Thread.currentThread().getName(), tickets);
					--tickets;
				} else {
					break;
				}
			}
		}
	}
}
public class TestTickets {
	public static void main(String[] args) {
		A aa = new A();

		Thread t1 = new Thread(aa);
		Thread t2 = new Thread(aa);
		t1.start();
		t2.start();
	}
}

本程序开启了两个线程,访问的是同一个资源,并且用synchronized 锁定同一对象,达到多线程互斥执行。

class A extends Thread {
	public static int tickets = 100;
	public static String str = new String("哈哈"); // 一定要加static关键字
	public void run() {
		while(true) {
			synchronized (str) {
				if (tickets > 0) {
				System.out.printf("%s线程正在卖出第%d张票\n", 
					Thread.currentThread().getName(), tickets);
					--tickets;
				} else {
					break;
				}
			}
		}
	}
}
public class TestTickets {
	public static void main(String[] args) {
		A aa1 = new A();
		A aa2 = new A();
		aa1.start();
		aa2.start();
	}
}

第二种方式:创建了两个线程,那么如何保证两个线程访问的是同一个资源,这个时候我们必须在str对象(监听器)前面加一个static关键字,保证synchronized霸占的是同一个对象。虽然是多线程在进行买票,但是当一个线程进入synchronized,此时CPU只允许一个线程运行synchronized代码块的内容,所有达到了线程互斥的效果。

class A implements Runnable {
	public int tickets = 100;
	String str = new String("哈哈");
	public synchronized  void run() {
		while(true) {
			// synchronized (str) {
				if (tickets > 0) {
				System.out.printf("%s线程正在卖出第%d张票\n", 
					Thread.currentThread().getName(), tickets);
					--tickets;
				} else {
					break;
				}
			}
		// }
	}
}
public class TestTickets {
	public static void main(String[] args) {
		A aa = new A();

		Thread t1 = new Thread(aa);
		Thread t2 = new Thread(aa);
		t1.start();
		t2.start();
	}
}

这个程序说明,只有一个线程在买票,因为一旦进入线程的while循环,要等到买完票才能退出循环。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值