JavaSE多线程与线程同步以及对象锁

本文探讨了Java多线程中遇到的共享数据访问问题,以及如何通过线程同步和对象锁来解决这个问题。介绍了未加锁时可能出现的并发问题,并详细讲解了如何使用`synchronized`关键字进行同步代码块的实现。此外,还提到了更灵活的显示锁机制,如`java.util.concurrent.locks.Lock`接口和`ReentrantLock`类的应用。
摘要由CSDN通过智能技术生成

在使用java多线程的时候如果着多个线程访问的是同一个共享数据;

例如售票, 多个售票点线程各自售票, 但都是从一个地方取票, 这就会造成操作系统让多个线程能够"同时"访问这个共享数据, 所谓同时并不是真正意义上的同时,而是很短间隔;

只是这个线程不能一次性执行完它的线程体, 比如说它现在只拿到了共享变量的值, 但是还没有做修改,

但是这时很可能会由于cpu分配给自己的时间片用完了而进入阻塞状态, 这时候另一个线程开始, 并且成功的拿到了并且修改了这个共享变量,的值;

很显然他们俩拿到的是同一个值,这就是问题所在.
于是线程同步就提出来了;
而线程同步又是基于对象锁的;
一般用对象锁更灵活;
未加线程代码:

package MultiThread;

public class TicketSellTest{
	public static void main(String[] args) {
		System.out.println("main线程开始");
		TicketOffice tick = new TicketOffice();
		Thread s1 = new Thread(tick);
		Thread s2 = new Thread(tick);
		Thread s3 = new Thread(tick);
		s1.setName("售票点1 ");
		s2.setName("售票点2 ");
		s3.setName("售票点3 ");
		s1.start();
		s2.start();
		s3.start();
		System.out.println("main线程结束");
	}
}

class TicketOffice implements Runnable{
	// 售出的票计数器
	private int tickets = 0;
	// 不管用这个类创立多少个线程, 每个线程执行start()的时候只执行run()方法
	public void run() {
		boolean flag = true;
		while(flag) {
			flag = sell();
		}
	}
	// 售票方法
	private boolean sell() {
		boolean flag = true;
		if (tickets < 10) {
			tickets = tickets + 1;
			// 获取正在执行这里的线程对象的引用的名字, 即是获取正在执行这里的线程名字
			System.out.println(Thread.currentThread().getName() + "卖出第 "+tickets +" 张票");
		}else {
			System.out.println("无票可卖!!");
			flag = false;
		}
		// 为了增大出错几率, 让线程停留一会儿
		try {
			Thread.sleep(300);
		}catch(InterruptedException e) {
			System.out.println("售卖过程中出现InterruptedException");
			e.printStackTrace();
		}
		// 返回True表示有票可卖
		return flag;
	}
}
// 执行结果
// 可以看到这里就是由于不加限制访问共享变量造成的后果, 就是一个线程没有完成一个完整的操作, 就被另一个线程介入了
main线程开始
售票点1 卖出第 2 张票
main线程结束
售票点2 卖出第 2 张票
售票点3 卖出第 3 张票
售票点1 卖出第 4 张票
售票点2 卖出第 6 张票
售票点3 卖出第 6 张票
售票点1 卖出第 7 张票
售票点2 卖出第 8 张票
售票点3 卖出第 9 张票
售票点1 卖出第 10 张票
无票可卖!!
无票可卖!!
无票可卖!!

然后解决办法是进行线程同步,此处有两个方法:
第一个

//将sell()方法变成一个同步方法,即在方法定义的时候加入synchronized修饰符
//如下
private synchronized boolean sell(){}
//这会使得此方法为同步方法,作用是每次线程执行此方法不会被打断,一定要执行完

还有一个方法是将操作共享变量的那一块代码变成同步代码块,即用synchronized(this){}将操作共享变量的那段代码包围起来;
其中括号里的参数表示获取制定对象的对象锁;
如下:


	// 售票方法
	private  boolean sell() {
		boolean flag = true;
		// 加入同步代码块,这个this表示调用此方法的对象的引用;获取此对象的对象锁
		synchronized(this) {
			if (tickets < 10) {
				tickets = tickets + 1;
				// 获取正在执行这里的线程对象的引用的名字, 即是获取正在执行这里的线程名字
				System.out.println(Thread.currentThread().getName() + "卖出第 "+tickets +" 张票");
			}else {
				System.out.println("无票可卖!!");
				flag = false;
				}
		}
		// 为了增大出错几率, 让线程停留一会儿
		try {
			Thread.sleep(300);
		}catch(InterruptedException e) {
			System.out.println("售卖过程中出现InterruptedException");
			e.printStackTrace();
		}
		// 返回True表示有票可卖
		return flag;
	}
//这样的运行结果就不会有冲突,如下
main线程开始
售票点1 卖出第 1 张票
售票点2 卖出第 2 张票
main线程结束
售票点3 卖出第 3 张票
售票点1 卖出第 4 张票
售票点2 卖出第 5 张票
售票点3 卖出第 6 张票
售票点1 卖出第 7 张票
售票点2 卖出第 8 张票
售票点3 卖出第 9 张票
售票点1 卖出第 10 张票
无票可卖!!
无票可卖!!
无票可卖!!

对象锁

同步机制的实现就是利用了对象锁;

JVM对每一个对象都有一个对象锁; 这个对象锁代表任何时候只允许一个线程能够有访问权限;

即是如果线程获取了这个对象的对象锁, 那么在它释放之前这段时间里面其他线程都不能访问;

其作用和synchronized同步机制是一样的,但是这个对象锁用起来更为灵活;

java提供了一种显示加锁机制, 使用java.util.concurrent.locks.Lock接口提供的lock()方法来获取锁;

用unlock()方法解锁,通常使用ReentrantLock这个类来实现, 上面更改之处的示例如下:

// 对象锁的实现所依赖的包
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

	// 售票方法
	// 创建Lock锁实例
	private Lock lock = new ReentrantLock();
	private boolean sell() {
		boolean flag = true;
		// 获取对象锁
		lock.lock();
		if (tickets < 10) {
			tickets = tickets + 1;
			// 获取正在执行这里的线程对象的引用的名字, 即是获取正在执行这里的线程名字
			System.out.println(Thread.currentThread().getName() + "卖出第 "+tickets +" 张票");
		}else {
			System.out.println("无票可卖!!");
			flag = false;
			}
		// 释放锁
		lock.unlock();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值