java多线程之synchronized

首先来看下一个场景,某电影院某个时间4个窗口同时在卖票,本场电影总共票只有100张,卖完为止。看下实际代码。

package cn.com.thread;

public class TestThread {
	public static void main(String[] args) {
		SellTicketThread t=new SellTicketThread();
		new Thread(t,"窗口1").start();
		new Thread(t,"窗口2").start();
		new Thread(t,"窗口3").start();
		new Thread(t,"窗口4").start();
	}
}
package cn.com.thread;

public class SellTicketThread extends Thread {
	private int ticket = 100;

	@Override
	public void run() {
		while (true) {
			if(sellTicket()){
				break;
			}
		}
	}

	private boolean sellTicket() {
		try {
			if (ticket <= 0) {
				return true;
			}
			Thread.sleep(30);
		} catch (Exception e) {
		}
		System.out.println(Thread.currentThread().getName()+":"+(ticket--));
		return false;
	}
}

从运行结果来看,发现出现了负数。在实际业务中是不可以的。

从而我们得出一个结论:多个线程访问一个对象中的实例变量,可能会出现`非线程安全`。

synchronized方法

如果在方法上加关键字synchronized,那么就不会出现上面的那个问题。

package cn.com.thread;

public class SellTicketThread extends Thread {
	private int ticket = 100;

	@Override
	public void run() {
		while (true) {
			if(sellTicket()){
				break;
			}
		}
	}

	private synchronized boolean sellTicket() {
		try {
			if (ticket <= 0) {
				return true;
			}
			Thread.sleep(30);
		} catch (Exception e) {
		}
		System.out.println(Thread.currentThread().getName()+":"+(ticket--));
		return false;
	}
}

看到这里有些人就要问了,既然加了锁,我们的代码就只能被一个线程调用,这样岂不是降低了效率,在同步代码的部分并没有多线程并发的情况出现呀?如果你能想到这一点,就说明你对锁的机制了解的差不多了,的确,情况的确如此,因为我们要尽量的缩小同步锁的范围,有什么原则么?所以有时间同步方法并不适合。

synchronized代码块

假如在卖票之前,我们还要去做相关的事。比如我们在美团买了一张票,可能在电影院的系统中,需要去效验下,是不是你在美团买了票,在决定是否出票。这个过程相当耗时,而每个人都是单独的对象去访问,所以是线程安全,但是我们要是用synchronized方法,是不适合的。

package cn.com.thread;

public class SellTicketThread extends Thread {
	private int ticket = 100;
        private Object lock=new Object();
	@Override
	public void run() {
		while (true) {
			if (sellTicket()) {
				break;
			}
		}
	}

	private boolean sellTicket() {
		System.out.println("效验逻辑,耗时10秒");
		try {
			synchronized (lock) {
				if (ticket <= 0) {
					return true;
				}
				System.out.println(Thread.currentThread().getName() + ":"+ (ticket--));
			}
			Thread.sleep(30);
		} catch (Exception e) {
		}

		return false;
	}
}

从上面的例子我们可以看出,这样也是达到我们要的结果。那么我们不禁要问该如何定义一个锁?

  • 所谓加锁,就是为了防止多个线程同时操作一份数据,如果多个线程操作的数据都是各自的,那么就没有加锁的必要
  • 共享数据的锁对于访问他们的线程来说必须是同一份,否则锁只能私有的锁,各锁个的,起不到保护共享数据的目的,试想一下将 Object lock 的定义放到 run 方法里面,每次都会实例化一个 lock,每个线程获取的锁都是不一样的,也就没有争抢可言,说的在通俗一点甲楼有一个门上了锁,A 要进门,乙楼有一个门上了锁 B 要进门,A 和 B 抢的不是一个门,因此不存在数据保护或者共享;
  • 锁的定义可以是任意的一个对象,该对象可以不参与任何运算,只要保证在访问的多个线程看来他是唯一的即可;

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-07 16:48:24

java多线程之synchronized的相关文章

JAVA多线程之Synchronized关键字--对象锁的特点

一,介绍 本文介绍JAVA多线程中的synchronized关键字作为对象锁的特点. 二,分析 synchronized可以修饰实例方法,如下形式: 1 public class MyObject { 2 3 synchronized public void methodA() { 4 //do something.... 5 } 这里,synchronized 关键字锁住的是当前对象.这也是称为对象锁的原因. 为啥锁住当前对象?因为 methodA()是个实例方法,要想执行methodA(),

Java多线程之synchronized关键字

一.synchronized锁住的不是代码块,是对象. 1 /** 2 * synchronized 对某个对象加锁 3 */ 4 public class SynchronizedTest { 5 6 private int count = 10; 7 private Object o = new Object(); 8 9 private void method() { 10 synchronized (o) { //任何线程想执行下面这段代码都需要拿到o这把锁 11 count--; 12

Java多线程之synchronized和volatile的比较

概述 在做多线程并发处理时,经常需要对资源进行可见性访问和互斥同步操作.有时候,我们可能从前辈那里得知我们需要对资源进行 volatile 或是 synchronized 关键字修饰处理.可是,我们却不知道这两者之间的区别,我们无法分辨在什么时候应该使用哪一个关键字.本文就针对这个问题,展开讨论. 版权说明 著作权归作者所有. 商业转载请联系作者获得授权,非商业转载请注明出处. 本文作者:Coding-Naga 发表日期: 2016年4月5日 本文链接:http://blog.csdn.net/

Java多线程之synchronized及其优化

Synchronized和同步阻塞synchronized是jvm提供的同步和锁机制,与之对应的是jdk层面的J.U.C提供的基于AbstractQueuedSynchronizer的并发组件.synchronized提供的是互斥同步,互斥同步是指在多个线程并发访问共享数据时,保证共享数据在同一时刻只有一个线程访问. 在jvm中,被synchronized修饰的代码块经javac编译之后,会在代码块前后分别生成一条monitorenter和moniterexit字节码指令,这两个字节码都需要一个

Java多线程之synchronized(一)

在上节中已经说过了“非线程安全”是如何出现的,链接如下:http://www.cnblogs.com/chentong/p/5650137.html,那么怎么解决“非线程安全”问题呢,只需要在两个线程都需要同时访问的方法前面加上synchronized关键字即可,我只贴出需要修改的这个方法的代码,具体修改如下: public static class GetNum { private int num = 0; //两个线程访问同一个对象中的同步方法时一定是线程安全的 synchronized p

Java多线程之synchronized(二)

为了解决“非线程安全”带来的问题,上一节中使用的办法是用关键字synchronized修饰多个线程可能同时访问到的方法,但是这样写是存在一定的弊端的,比如线程A调用一个用synchronized修饰的同步方法,这个方法要执行很长时间,那么其它的线程必须无条件的等线程A执行完释放掉对象锁,当然前提是其他的线程也要访问这个同步方法.这种情况就可以用synchronized代码块来解决.在解决之前我先附上一段没优化之前的方法,这样就可以直观的看到效果差异. 证明synchronized方法的弊端,代码

Java多线程之synchronized(五)

上篇介绍了用synchronized修饰static方式来实现“Class 锁”,今天要介绍另一种实现方式,synchronized(class)代码块,写法不一样但是作用是一样的.下面我附上一段代码来看一下synchronized(class)代码块的基本用法,如下: public static void main(String[] args) { Service4 s1 = new Service4(); Service4 s2 = new Service4(); ThreadA a = n

Java多线程之synchronized线程锁

1 package org.study2.javabase.ThreadsDemo.sync; 2 3 /** 4 * @Auther:GongXingRui 5 * @Date:2018/9/18 6 * @Description:synchronized线程锁 7 **/ 8 public class TicketApp { 9 public static void main(String args[]) { 10 Ticket ticket = new Ticket(); 11 Threa

JAVA多线程之volatile 与 synchronized 的比较

一,volatile关键字的可见性 要想理解volatile关键字,得先了解下JAVA的内存模型,Java内存模型的抽象示意图如下: 从图中可以看出: ①每个线程都有一个自己的本地内存空间--线程栈空间???线程执行时,先把变量从主内存读取到线程自己的本地内存空间,然后再对该变量进行操作 ②对该变量操作完后,在某个时间再把变量刷新回主内存 关于JAVA内存模型,更详细的可参考: 深入理解Java内存模型(一)——基础 因此,就存在内存可见性问题,看一个示例程序:(摘自书上) 1 public c