java多线程详解(三)-----线程的同步
今天参照大牛的博客,自己写了一下多线程实现窗口卖票的程序,很久没写多线程了,记得第一家公司就是做的这个,今天记录一下,重写一下。
利用多线程模拟 3 个窗口卖票
继承 Thread 类
package Thread;
public class SellThread extends Thread {
//定义一共有 50 张票,注意声明为 static,表示几个窗口共享
private static int num = 50;
//调用父类构造方法,给线程命名
public SellThread(String string) {
super(string);
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
synchronized (this.getClass()) {
if (num > 0) {
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.currentThread().getName() + "卖出一张票,剩余" + (--num) + "张");
}
}
}
}
}
实现runnable接口:
package Thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SellRunnable implements Runnable {
//定义一共有 50 张票,继承机制开启线程,资源是共享的,所以不用加 static
private int num = 50;
// @Override
// public void run() {
// //票分 50 次卖完
// for(int i = 0 ; i < 50 ;i ++){
// sell();
// }
// }
//
// private synchronized void sell(){
// if(num > 0){
// try {
// //模拟卖一次票所需时间
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余"+(--num)+"张");
// }
// }
//创建一个锁对象
Lock l = new ReentrantLock();
@Override
public void run() {
//票分 50 次卖完
for (int i = 0; i < 50; i++) {
//获取锁
l.lock();
try {
if (num > 0) {
//模拟卖一次票所需时间
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "卖出一张票,剩余" + (--num) + "张");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放锁
l.unlock();
}
}
}
}
main函数
package Thread;
public class Main {
public static void main(String[] args) {
// SellThread sellThread1 = new SellThread("A窗口");
// SellThread sellThread2 = new SellThread("B窗口");
// SellThread sellThread3 = new SellThread("C窗口");
// sellThread1.start();
// sellThread2.start();
// sellThread3.start();
SellRunnable r = new SellRunnable();
Thread t1 = new Thread(r,"A窗口");
Thread t2 = new Thread(r,"B窗口");
Thread t3 = new Thread(r,"C窗口");
t1.start();
t2.start();
t3.start();
}
}
这里用了三种方法做线程同步:
1,使用同步代码块
2,使用同步方法
3,使用锁机制
为什么要使用线程同步,如果不使用线程同步,可能两个线程会同时进入代码块,导致窗口间卖了同一张票,不能使票保持独占。
其中第一个继承thread线程的方式用了同步代码块,第二种实现runnable接口用了同步方法和锁机制,当然实现runnable接口也可以使用同步代码块。但是继承thread使用同步方法和锁机制时会出现问题,因为new了三个对象,他们的三个对象的sell方法和lock对象不同,到最后会出现负票情况。