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

个人主页:Dawn黎明开始

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

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


 【Java】详解多线程同步的三种方式_第1张图片


 

文章目录

一.线程安全

1.1案例引入

1.1.1问题

1.1.2实例操作

1.2说明

二.同步代码块

2.1语法格式

2.2全局锁

实例练习1

2.3任意锁

实例练习2

2.4局部锁

实例练习3

2.5this对象作为锁

实例练习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();
	}
}

运行结果

【Java】详解多线程同步的三种方式_第2张图片

总结:该方法不可以,有重票和漏票和多票问题。 

1.2说明

           线程安全问题其实就是由多个线程同时处理共享资源所导致的。要想解决线程安全问题,必须得保证处理共享资源的代码在任意时刻只能有一个线程访问。为此,Java中提供了线程同步机制。

【Java】详解多线程同步的三种方式_第3张图片

二.同步代码块

【Java】详解多线程同步的三种方式_第4张图片

2.1语法格式

【Java】详解多线程同步的三种方式_第5张图片

原理

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

运行结果

【Java】详解多线程同步的三种方式_第6张图片

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{
	//任意类
}

运行结果

【Java】详解多线程同步的三种方式_第7张图片

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
			}
		}
				
			}
		}
	}

运行结果

【Java】详解多线程同步的三种方式_第8张图片

总结:该方法不可以,有重票和漏票问题。

2.5this对象作为锁

实例练习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
			}
		}
				
			}
		}
	}

运行结果

【Java】详解多线程同步的三种方式_第9张图片

说明:项目开发中一般使用this关键字作为锁对象。

2.6注意

【Java】详解多线程同步的三种方式_第10张图片

三.同步方法

3.1语法格式

【Java】详解多线程同步的三种方式_第11张图片

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

运行结果

【Java】详解多线程同步的三种方式_第12张图片

3.3思考

     (1).同步方法的格式及锁对象问题?

           把同步关键字加在方法上

     (2).同步代码块的锁对象是谁?

           任意全局对象   一般使用this作为锁对象

     (3).同步方法的锁是谁?

           this,它是隐含的

四.同步锁(重入锁)

4.1语法格式

【Java】详解多线程同步的三种方式_第13张图片

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

运行结果

【Java】详解多线程同步的三种方式_第14张图片


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

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

你可能感兴趣的:(Java,java,eclipse,多线程)