Java学习-java中多线程的同步方法

news/2024/5/17 18:15:31 标签: java, 多线程同步, volatile, synchronized

java_0">java中多线程的同步方法


版权声明:本文为博主原创文章,转载请注明出处。
https://mp.csdn.net/mdeditor/84573042

1、为什么使用同步?

Java允许多线程并发控制,当多个线程同时操作一个可共享资源变量时(如数据的增删改查),将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。

2、同步的方式

不同步方式:

/*
 * 项目:售票问题,模拟火车站售票系统中的多个线程同时对某一个车次车票的购买操作。
 * 在主程序中首先生成3个线程,然后启动它们,共定义5张车票,每个线程都对车票进行购买操作。

 */
package xiancheng;
//非同步
class MyThread4 implements Runnable
{
	//假设一共5张票
	private int ticket=5;

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i=0;i<100;i++)
		{
			if(ticket>0)//还有票
			{
				try {
					Thread.sleep(300);//加入延迟
				}catch(InterruptedException e)
				{
					/*InterruptedException:当线程在活动之前或活动期间处于正在等待、
					休眠或占用状态且该线程被中断时,抛出该异常。
					*/
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"卖票:ticket="+ticket--);
			}
		}
	}
	
}
public class SyncTest1 {

	public static void main(String[] args) {
		MyThread4 mt=new MyThread4();//定义线程对象
		Thread t1=new Thread(mt,"师范学院");
		Thread t2=new Thread(mt,"中北大学");
		Thread t3=new Thread(mt,"太原工业学院");
		t1.start();
		t2.start();
		t3.start();
	}
}
-------------------------
输出结果:
中北大学卖票:ticket=4
师范学院卖票:ticket=3
太原工业学院卖票:ticket=5
太原工业学院卖票:ticket=2
中北大学卖票:ticket=0
师范学院卖票:ticket=1
  1. 同步方法:

即有synchronized关键字修改的方法。由于Java的每个对象都有一个内置锁,当用此关键字修饰时,内置锁会保护整个方法。在调用该方法前,需要获取内置锁,否则就处于阻塞状态。

/*
 * 项目:售票问题,模拟火车站售票系统中的多个线程同时对某一个车次车票的购买操作。
 * 在主程序中首先生成3个线程,然后启动它们,共定义5张车票,每个线程都对车票进行购买操作。

 */
package xiancheng;
//synchronized同步方法

public class SyncTest2 {
	public static void main(String args[]) {
		MyThread5 mt = new MyThread5(); // 定义线程对象
		Thread t1 = new Thread(mt, "师范学院"); // 定义Thread对象
		Thread t2 = new Thread(mt, "中北大学"); // 定义Thread对象
		Thread t3 = new Thread(mt, "太原工业学院"); // 定义Thread对象
		t2.start();
		t1.start();
		t3.start();
	}
}

class MyThread5 implements Runnable {
	private int ticket = 5; // 假设一共有5张票

	public void run() {
		for (int i = 0; i < 100; i++) {
			this.sale(); // 调用同步方法
		}
	}

	public synchronized void sale() { // 声明同步方法
		if (ticket > 0) { // 还有票
			try {
				Thread.sleep(30); // 加入延迟
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()
					+ "卖票:ticket = " + ticket--);
		}
	}
}
----------------
输出结果:
中北大学卖票:ticket = 5
太原工业学院卖票:ticket = 4
太原工业学院卖票:ticket = 3
太原工业学院卖票:ticket = 2
太原工业学院卖票:ticket = 1
------------------------------
修饰方法范围是整个函数。
  1. 同步代码块(同步对象):

即有synchronized关键字修饰的语句块。被该关键字修饰的语句块会自动被加上内置锁,从而实现同步。

/*
 * 项目:售票问题,模拟火车站售票系统中的多个线程同时对某一个车次车票的购买操作。
 * 在主程序中首先生成3个线程,然后启动它们,共定义5张车票,每个线程都对车票进行购买操作。

 */
package xiancheng;
//synchronized同步对象

public class SyncTest3 {
	public static void main(String args[]) {
		MyThread6 mt = new MyThread6(); // 定义线程对象
		Thread t1 = new Thread(mt, "师范学院"); // 定义Thread对象
		Thread t2 = new Thread(mt, "中北大学"); // 定义Thread对象
		Thread t3 = new Thread(mt, "太原工业学院"); // 定义Thread对象
		t1.start();
		t2.start();
		t3.start();
	}
}

class MyThread6 implements Runnable {
	private int ticket = 5; // 假设一共有5张票

	public void run() {
		for (int i = 0; i < 10; i++) {
			synchronized (this) { // 要对当前对象进行同步
				if (ticket > 0) { // 还有票
					try {
						Thread.sleep(300); // 加入延迟
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()
							+ "卖票:ticket = " + ticket--);
				}
			}
		}
	}
}
----------------
输出结果:
师范学院卖票:ticket = 5
师范学院卖票:ticket = 4
师范学院卖票:ticket = 3
太原工业学院卖票:ticket = 2
太原工业学院卖票:ticket = 1
-------------------
synchronized (this) 相当于给ticket加了锁,在一个进程正在访问ticket时,此时锁锁上,其他进程将不能访问ticket。
  1. 使用特殊域变量(volatile)实现线程同步:

a.volatile关键字为域变量的访问提供了一种免锁机制;
b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新;
c.因此每次使用该域就要重新计算,而不是使用寄存器中的值;
d.volatile不会提供任何原子操作,它也不能用来修改final类型的变量。

/*
 * 项目:售票问题,模拟火车站售票系统中的多个线程同时对某一个车次车票的购买操作。
 * 在主程序中首先生成3个线程,然后启动它们,共定义5张车票,每个线程都对车票进行购买操作。

 */
package xiancheng;

class MyThread7 implements Runnable
{
	//假设一共5张票
	private volatile int ticket=5;

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i=0;i<100;i++)
		{
			if(ticket>0)//还有票
			{
				try {
					Thread.sleep(300);//加入延迟
				}catch(InterruptedException e)
				{
					/*InterruptedException:当线程在活动之前或活动期间处于正在等待、
					休眠或占用状态且该线程被中断时,抛出该异常。
					*/
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"卖票:ticket="+ticket--);
			}
		}
	}
	
}
public class SyncTest4 {

	public static void main(String[] args) {
		MyThread7 mt=new MyThread7();//定义线程对象
		Thread t1=new Thread(mt,"师范学院");
		Thread t2=new Thread(mt,"中北大学");
		Thread t3=new Thread(mt,"太原工业学院");
		t1.start();
		t2.start();
		t3.start();
	}
}
-------------------
输入结果:
师范学院卖票:ticket=5
中北大学卖票:ticket=4
太原工业学院卖票:ticket=3
师范学院卖票:ticket=2
太原工业学院卖票:ticket=1
中北大学卖票:ticket=1
师范学院卖票:ticket=0
-------------------
注意:volatile是非原子操作,它**不一定**会改变原子的值。例如 a=1是原子性操作,但是a++和a +=1就不是原子性操作。使用volatile关键字,多线程不具备线程安全。
  1. 使用重入锁实现线程同步:

在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和块具有相同的基本行为和语义,并且扩展了其能力。
ReenreantLock类的常用方法有:
ReentrantLock() : 创建一个ReentrantLock实例
lock() : 获得锁
unlock() : 释放锁

package xiancheng;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//使用重入锁
class MyThread8 implements Runnable
{
	//假设一共5张票
	private int ticket=5;
	private Lock lock = new ReentrantLock();
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i=0;i<100;i++)
		{
			lock.lock();//获得锁
			if(ticket>0)//还有票
			{
				try {
					Thread.sleep(300);//加入延迟
				}catch(InterruptedException e)
				{
					/*InterruptedException:当线程在活动之前或活动期间处于正在等待、
					休眠或占用状态且该线程被中断时,抛出该异常。
					*/
					e.printStackTrace();
				}
				finally {
					lock.unlock();//释放锁
				}
				System.out.println(Thread.currentThread().getName()+"卖票:ticket="+ticket--);
			}
		}
	}
	
}
public class SyncTest5 {

	public static void main(String[] args) {
		MyThread8 mt=new MyThread8();//定义线程对象
		Thread t1=new Thread(mt,"师范学院");
		Thread t2=new Thread(mt,"中北大学");
		Thread t3=new Thread(mt,"太原工业学院");
		t1.start();
		t2.start();
		t3.start();
	}
}
-------------------
运行结果:
中北大学卖票:ticket=5
师范学院卖票:ticket=4
太原工业学院卖票:ticket=3
中北大学卖票:ticket=2
师范学院卖票:ticket=1
-----------------------------------------
// 注:关于Lock对象和synchronized关键字的选择:

// a.最好两个都不用,使用一种java.util.concurrent包提供的机制,能够帮助用户处理所有与锁相关的代码。

// b.如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码

// c.如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁

  1. 使用局部变量实现线程同步:

如果使用ThreadLocal(线程局部变量,继承自ThreadLocal)管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。
ThreadLocal类型的常用方法 :
ThreadLocal() : 创建线程本地变量
get() : 返回此线程局部变量的当前线程副本中的值
remove() :移除此线程局部变量当前线程的值。
*注意:在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。
此同步方法具体实现参考:https://blog.csdn.net/sun1021873926/article/details/77898665

参考自:
Think in java
java JDK API 1.6.0中文版
Java多线程同步方法参考: https://blog.csdn.net/sun1021873926/article/details/77898665
Java中Synchronized的用法参考: https://blog.csdn.net/luoweifu/article/details/46613015
volatile关键字的总结参考: https://blog.csdn.net/u012723673/article/details/80682208


http://www.niftyadmin.cn/n/855417.html

相关文章

迈入网络世界

很早就学会了上网&#xff0c;在网上真的可以说是无所不可&#xff0c;从刚开始的浏览网页&#xff0c;到后来的网上消费、网上实战&#xff0c;让自己喜欢上了这个虚拟的世界&#xff0c;更让我对网络有了很大的兴趣&#xff0c;很想去发掘它其中的秘密所在。在这种心理的驱使…

设计模式的核心知识

终于把23种设计模式给整理完了&#xff0c;通通理解了一遍。虽然看得懂&#xff0c;能够随着里面代码和传递参数的方法绕来绕去&#xff0c;一层一层拔下去而不会“头晕”&#xff0c;但离实际运用它们还是有很大的距离的。 这23种设计模式涵盖了所有软件架构师和设计师前辈们的…

Java学习-接口

Java学习-接口 版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请注明出处。 https://mp.csdn.net/mdeditor/84779687 1、为什么使用接口&#xff1f; Java语言不支持多重继承&#xff0c;即一个子类只能有一个父类&#xff0c;若子类需要继承多个父类&#xff0c;这…

Html+css出现问题集合

Htmlcss出现问题集合 转载请注明出处https://blog.csdn.net/qq_35779070/article/details/104278038 1.怎么写进度条 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style type"text/css"…

CSS中div垂直居中以及div中元素/文字居中

转载请注明出处&#xff1a;https://blog.csdn.net/qq_35779070/article/details/104908080 一、div垂直居中 &#xff08;一&#xff09;当width和height大小为百分比时&#xff0c;实现方法&#xff1a; 使用CSS的transform属性&#xff1a; <!DOCTYPE html> <h…

Cornell University Designing with Microcontrollers

http://instruct1.cit.cornell.edu/courses/ee476/转载于:https://www.cnblogs.com/stoneresearch/archive/2008/10/21/4336756.html

微信小程序遍历循环中更改某一item的css样式

微信小程序遍历循环中更改某一item样式&#xff1a; 转载请注明出处&#xff1a;https://blog.csdn.net/qq_35779070/article/details/105856484 问题情境&#xff1a; 做小程序时要设置每个item的下划线&#xff0c;但是最后一个item不需要。 做成样式&#xff1a; 其实只…

信息安全管理知识大全升级

ISO27000标准知识大全V3.0发布信息&#xff1a;<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />软件名称&#xff1a;ISO/IEC27000标准信息安全管理知识大全软件版本&#xff1a;V3.0软件大小&#xff1a;16.1 M授权形式&#x…