
文章目录
一.🔐线程安全
1.1🔓案例引入
1.1.1🔑问题
电影院上映一部电影,共有三个窗口,请你设计一个模拟电影院卖票的程序。
1.1.2🔑实例操作
代码如下👇🏻
package Process3;
public class SellTicket implements Runnable {
	
	private  int tickets = 20;//总票数
	private int ticketId = 1;//票号
	@Override
	public void run() {
		while (tickets>0) {
            try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		
				System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
				tickets--; //剩余票数--
				ticketId++; //票号++
			}
		}
	}
package Process3;
public class SellTicketDemo {
	public static void main(String[] args) {
		// 创建资源对象
		SellTicket st = new SellTicket();
		// 创建三个线程对象
		Thread t1 = new Thread(st, "窗口1");
		Thread t2 = new Thread(st, "窗口2");
		Thread t3 = new Thread(st, "窗口3");
		// 启动线程
		t1.start();
		t2.start();
		t3.start();
	}
}
运行结果👇🏻
 
总结:该方法不可以,有重票和漏票和多票问题。
1.2🔓说明
线程安全问题其实就是由多个线程同时处理共享资源所导致的。要想解决线程安全问题,必须得保证处理共享资源的代码在任意时刻只能有一个线程访问。为此,Java中提供了线程同步机制。
 
二.🔐同步代码块

2.1🔓语法格式
 
原理
(1).当线程执行同步代码块时,首先会检查lock锁对象的标志位。
(2).默认情况下标志位为1,此时线程会执行Synchronized同步代码块,同时将锁对象的标志位置为0。
(3).当一个新的线程执行到这段同步代码块时,由于锁对象的标志位为0,新线程会发生阻塞,等待当前线程执行完同步代码块后。
(4).锁对象的标志位被置为1,新线程才能进入同步代码块执行其中的代码,这样循环往复,直到共享资源被处理完为止。
2.2🔓全局锁
🚩实例练习1
代码如下👇🏻
package Sell;
public class SellTicket implements Runnable {
	
	private   int tickets = 20;//总票数
	private int ticketId = 1;//票号
	 Object o =new Object();//全局锁
	
	@Override
	public void run() {
		while (tickets>0) {
			synchronized (o) {
			if(tickets>0) {
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
				tickets--; //剩余票数--
				ticketId++; //票号++
			}
		}
				
			}
		}
	}
package Sell;
public class SellTicketDemo1 {
	public static void main(String[] args) {
		// 创建资源对象
		SellTicket1 st = new SellTicket1();
		// 创建三个线程对象
		Thread t1 = new Thread(st, "窗口1");
		Thread t2 = new Thread(st, "窗口2");
		Thread t3 = new Thread(st, "窗口3");
		// 启动线程
		t1.start();
		t2.start();
		t3.start();
	}
}运行结果👇🏻
 
2.3🔓任意锁
🚩实例练习2
代码如下👇🏻
package Sell;
public class SellTicket1 implements Runnable {
	
	private int tickets = 20;//总票数
	private int ticketId = 1;//票号
	 Demo lock =new Demo();//任意锁
	
	@Override
	public void run() {
		while (tickets>0) {
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized (lock) {
		    //  为什么有if判断?
	        //  如果没有if,t1执行结束,tickets=0,这样t2,t3再执行同步代码块之后,票会变成负数,票会多卖
			if(tickets>0) {
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
				tickets--; //剩余票数--
				ticketId++; //票号++  // 多卖21,22
			}
		}
				
			}
		}
	}
class Demo{
	//任意类
}
运行结果👇🏻

2.4🔓局部锁
🚩实例练习3
代码如下👇🏻
package Sell;
public class SellTicket1 implements Runnable {
	
	private int tickets = 20;//总票数
	private int ticketId = 1;//票号
	@Override
	public void run() {
		Object lock =new Object();//局部锁锁不住
		while (tickets>0) {
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized (lock) {
		    //  为什么有if判断?
	        //  如果没有if,t1执行结束,tickets=0,这样t2,t3再执行同步代码块之后,票会变成负数,票会多卖
			if(tickets>0) {
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
				tickets--; //剩余票数--
				ticketId++; //票号++  // 多卖21,22
			}
		}
				
			}
		}
	}
运行结果👇🏻
 
总结:该方法不可以,有重票和漏票问题。
2.5🔓this对象作为锁
🚩实例练习4
代码如下👇🏻
package Sell;
public class SellTicket1 implements Runnable {
	
	private int tickets = 20;//总票数
	private int ticketId = 1;//票号
	@Override
	public void run() {
		while (tickets>0) {
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized (this) {
		    //  为什么有if判断?
	        //  如果没有if,t1执行结束,tickets=0,这样t2,t3再执行同步代码块之后,票会变成负数,票会多卖
			if(tickets>0) {
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
				tickets--; //剩余票数--
				ticketId++; //票号++  // 多卖21,22
			}
		}
				
			}
		}
	}
运行结果👇🏻
 
说明:项目开发中一般使用this关键字作为锁对象。
2.6🔓注意

三.🔐同步方法
3.1🔓语法格式
 
3.2🔓实例练习
代码如下👇🏻
package Sell;
public class SellTicket1 implements Runnable {
	
	private int tickets = 20;//总票数
	private int ticketId = 1;//票号
	@Override
	public void run() {
		while (tickets>0) {
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			Selltickets ();
				
			}
		}
	//方法抽取:
	 //同步方法
	public synchronized void Selltickets () {
		if(tickets>0) {
	
			System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
			tickets--; //剩余票数--
			ticketId++; //票号++  // 多卖21,22
		}
	}
	}
package Sell;
public class SellTicketDemo1 {
	public static void main(String[] args) {
		// 创建资源对象
		SellTicket1 st = new SellTicket1();
		// 创建三个线程对象
		Thread t1 = new Thread(st, "窗口1");
		Thread t2 = new Thread(st, "窗口2");
		Thread t3 = new Thread(st, "窗口3");
		// 启动线程
		t1.start();
		t2.start();
		t3.start();
	}
}运行结果👇🏻
 
3.3🔓思考
(1).同步方法的格式及锁对象问题?
把同步关键字加在方法上
(2).同步代码块的锁对象是谁?
任意全局对象 一般使用this作为锁对象
(3).同步方法的锁是谁?
this,它是隐含的
四.🔐同步锁(重入锁)
4.1🔓语法格式
 
4.2🔓实例练习
代码如下👇🏻
package Sell;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SellTicket1 implements Runnable {
	
	private int tickets = 20;//总票数
	private int ticketId = 1;//票号
	Lock lock =new ReentrantLock();//同步锁(重入锁)
	@Override
	public void run() {
		while (tickets>0) {
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			lock.lock();//上锁
			if(tickets>0) {
				
				System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
				tickets--; //剩余票数--
				ticketId++; //票号++  
			}
			lock.unlock();//开锁
			}
		}
	}
package Sell;
public class SellTicketDemo1 {
	public static void main(String[] args) {
		// 创建资源对象
		SellTicket1 st = new SellTicket1();
		// 创建三个线程对象
		Thread t1 = new Thread(st, "窗口1");
		Thread t2 = new Thread(st, "窗口2");
		Thread t3 = new Thread(st, "窗口3");
		// 启动线程
		t1.start();
		t2.start();
		t3.start();
	}
}运行结果👇🏻











