【Java】详解多线程同步的三种方式

🌺个人主页:Dawn黎明开始

🎀系列专栏:Java
每日一句:等风来,不如追风去

📢欢迎大家:关注🔍+点赞👍+评论📝+收藏⭐️


 


 

文章目录

一.🔐线程安全

1.1🔓案例引入

1.1.1🔑问题

1.1.2🔑实例操作

1.2🔓说明

二.🔐同步代码块

2.1🔓语法格式

2.2🔓全局锁

🚩实例练习1

2.3🔓任意锁

🚩实例练习2

2.4🔓局部锁

🚩实例练习3

2.5🔓this对象作为锁

🚩实例练习4

2.6🔓注意

三.🔐同步方法

3.1🔓语法格式

3.2🔓实例练习

3.3🔓思考

四.🔐同步锁(重入锁)

4.1🔓语法格式

4.2🔓实例练习


一.🔐线程安全

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,新线程才能进入同步代码块执行其中的代码,这样循环往复,直到共享资源被处理完为止。

 同步代码块:

 * synchronized(对象){

 * 需要同步的代码;

 * }

 *

 * (1).对象是什么呢?

 * 我们可以随便创建一个对象试试。

 * (2).需要同步的代码是哪些呢?

 * 把多条语句操作共享数据的代码的部分给包起来

 *

 * 注意

 * (1).同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。

 * (2).多个线程必须是同一把锁。

2.2🔓全局锁

注:以下四种方法的测试类都一样(只有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🔓语法格式

 Lock:

 * void lock(): 获取锁。

 * void unlock():释放锁。  

 * ReentrantLock是Lock的实现类.

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();
	}
}

运行结果👇🏻


​🌺建议大家亲自动手操作,学编程,多实践练习是提升编程技能的必经之路。

🌺欢迎大家在评论区进行讨论和指正!

  • 16
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dawn黎明开始

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值