不多说,直接上代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 铁道部发布了一个售票任务,要求销售1000张票,要求有5个窗口来进行销售,请编写多线程程序来模拟这个效果
*
*问题1:出现部分重票
*原因:语句未加锁,整体未执行完,被下个线程访问
*
*解决:加锁---锁的范围:锁打印和++
* ---锁的对象:同一个对象
*锁的分类:
*1.同步代码块2.同步方法3.对象互斥锁
*
*问题二:票数超出范围
*解决:在锁内加判断
*
*问题三:每个线程应该都有同一个出口
*解决:使用while(true)循环
*/
class Task1 implements Runnable{
private int ticket = 0 ;
private Lock lock = new ReentrantLock();
public void run() {
//1.同步代码块
//save1();
/* //2.同步方法
while(true){
if(save2())
break;
}*/
//方法3:互斥对象锁---容易死锁 alt+shift+m 封装方法快捷键
//1.同一把锁 2.锁的范围
while(true){
try{
lock.lock();//加锁
if(ticket<10){
ticket++;
System.out.println("窗口"+Thread.currentThread().getName()+"正在销售第"+ticket+"张票");
}else {
System.out.println(Thread.currentThread().getName()+"已售完。。");
break;
}
}finally {
lock.unlock();
}
}
}
//1.同步代码块 ---1.锁的对象2.锁的范围
private void save1() {
while(true){
synchronized (this) {//"lock" obj---同一个对象
if(ticket<10){
ticket++;
System.out.println("窗口"+Thread.currentThread().getName()+"正在销售第"+ticket+"张票");
}else {
System.out.println(Thread.currentThread().getName()+"已售完。。");
break;
}
}
}
}
//2.同步方法
//1,锁的对象2.锁的范围
private synchronized boolean save2() {
if(ticket<10){
ticket++;
System.out.println("窗口"+Thread.currentThread().getName()+"正在销售第"+ticket+"张票");
return false;
}else {
System.out.println(Thread.currentThread().getName()+"已售完。。");
return true;
}
}
}
public class Test1 {
public static void main(String[] args) {
Task1 task1 = new Task1();
for(int i=1;i<=5;i++){
new Thread(task1,(Integer.toString(i))).start();
}
//----------回顾线程安全-----------
//StringBuffer--用在多线程
StringBuffer sb = new StringBuffer();
sb.append("hello");
//StringBuilder--用在单线程
StringBuilder sb2 = new StringBuilder();
sb2.append("hello");
//我们所学的集合是线程安全的吗? ArrayList---不安全
List<String> list = new ArrayList<>();
list.add("hello");
//---集合的线程安全:---------
list = Collections.synchronizedList(new ArrayList<>());
list.add("hello");
}
}
下面是继承Thread实现
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 问题1:会重复卖5000张票
* 原因:有5个成员属性ticket---每个对象都有一份独立的成员属性
* 解决:加static
*
*问题2:有部分重票
*原因:锁的不是同一个对象
*解决:锁字符串或静态对象
*
*/
class MyThread extends Thread{
private static int ticket = 1;
private static Object obj = new Object();
private static Lock lock = new ReentrantLock();
public MyThread(String name) {
super(name);
}
@Override
public void run() {
//1.同步代码块
//save1();
//2.同步方法
while(true){
if(save2())
break;
}
//3.对象互斥锁
//save3();
}
//1.同步代码块
private void save1() {
while(true){
synchronized (obj) {//"lock" 静态对象obj
if(ticket<=1000){
System.out.println("窗口"+getName()+"正在销售第"+ticket+"张票");
ticket++;
}else {
System.out.println("窗口"+getName()+"已售完。。");
break;
}
}
}
}
//2.同步方法
private static synchronized boolean save2(){
if(ticket<=10){
System.out.println("窗口"+Thread.currentThread().getName()+"正在销售第"+ticket+"张票");
ticket++;
return false;
}else {
System.out.println("窗口"+Thread.currentThread().getName()+"已售完。。");
return true;
}
}
//3.对象互斥锁
private void save3() {
while(true){
try {
lock.lock();
if(ticket<=1000){
System.out.println("窗口"+getName()+"正在销售第"+ticket+"张票");
ticket++;
}else {
System.out.println("窗口"+getName()+"已售完。。");
break;
}
} finally {
lock.unlock();
}
}
}
}
public class TicketTest2 {
public static void main(String[] args) throws InterruptedException {
for(int i=1;i<=5;i++){
new MyThread(Integer.toString(i)).start();
}
}
}
总结:
加锁牢记两点:1.锁的对象2.锁的范围
实现runnable接口 VS 继承Thread类
runnable接口: 不用锁静态对象,同一个任务 能确定是同一个对象
继承Thread类:需要锁静态对象,才能确保是同一个对象