多线程编程(五)---synchronized关键字

一、synchronized原理

在Java中每一个对象有且仅有一个同步锁,就是说同步锁是依赖对象存在的。

当我们调用某个对象的同步方法时,就获取了该对象的同步锁。

不同线程对同步锁的访问是互斥的。也就是说某个时间点,对象的同步锁只能被一个线程获取到。通过同步锁,我们就能在多线程中实现对对象或方法的互斥访问。譬如现在有两个线程窗口1和窗口2,它们都会访问对象obj的同步锁、。假如,在同一时刻窗口1获取到obj的同步锁并执行一些操作。而同时窗口2也企图获取对象obj的同步锁那么窗口2会获取失败,它必须等待直到窗口1释放了该obj对象的同步锁之后窗口2才能获取对象obj的同步锁从而进行对应的操作。

底层语义原理:

Java 虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现, 无论是显式同步(有明确的 monitorenter 和 monitorexit 指令,即同步代码块)还是隐式同步都是如此。在 Java 语言中,同步用的最多的地方可能是被 synchronized 修饰的同步方法。同步方法 并不是由 monitorenter 和 monitorexit 指令来实现同步的,而是由方法调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的

在JVM中,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充。

实例变量:存放类的属性数据信息,包括父类的属性信息,如果是数组的实例部分还包括数组的长度,这部分内存按4字节对齐。

填充数据:由于虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐,这点了解即可。

对象头:对于顶部,则是Java头对象,它实现synchronized的锁对象的基础,这点我们重点分析它,一般而言,synchronized使用的锁对象是存储在Java对象头里的,jvm中采用2个字来存储对象头(如果对象是数组则会分配3个字,多出来的1个字记录的是数组长度),其主要结构是由Mark Word 和 Class Metadata Address 组成。

二、synchronized 三种应用方式

synchronized关键字最主要有以下3种应用方式,下面分别介绍

a.修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁

b.修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁

c.修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁


2.1修饰代码块

在某些情况下,我们编写的方法体可能比较大,同时存在一些比较耗时的操作,而需要同步的代码又只有一小部分,如果直接对整个方法进行同步操作,可能会得不偿失,此时我们可以使用同步代码块的方式对需要同步的代码进行包裹,这样就无需对整个方法进行同步操作了.

格式:synchronized (锁对象){需要同步 的代码}

注意:锁对象--------对象任意对象  。需要同步的代码--把多条语句操作的共享数据。同步可以解决安全问题的根本原因就在那个锁对象上,该对象如同锁的功能。

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

synchronized代码块可以更精确的控制冲突限制访问区域,有时候表现更高效率。

package cn.itcast_12;

import java.io.SyncFailedException;

public class SellTicketRunnable implements Runnable {
	private int ticketNum=100;
	//创建同一把锁
	Object obj=new Object();
	@Override
	public void run() {
		while(true){
			synchronized(obj){
				if(ticketNum>0){
					//为了模拟更真实的场景我们稍作休息
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticketNum--)+"张票");
				}
			}

		}
	}
/*	@Override
	public void run() {
		while(true){
			synchronized(new Object()){
				if(ticketNum>0){
					//为了模拟更真实的场景我们稍作休息
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticketNum--)+"张票");
				}
			}

		}
	}*/

}

测试类:

package cn.itcast_12;

/**
 * 实现Runnable接口的方式实现
 * @author jack
 *
 */
public class SellTicketRunnableDemo {

	public static void main(String[] args) {
		//创建资源对象
		SellTicketRunnable st=new SellTicketRunnable();
		
		//创建三个线程对象
		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正在出售第100张票
窗口1正在出售第99张票
窗口1正在出售第98张票
窗口1正在出售第97张票
窗口1正在出售第96张票
窗口1正在出售第95张票
窗口1正在出售第94张票
窗口1正在出售第93张票
窗口1正在出售第92张票
窗口1正在出售第91张票
窗口1正在出售第90张票
窗口1正在出售第89张票
窗口1正在出售第88张票
窗口1正在出售第87张票
窗口1正在出售第86张票
窗口1正在出售第85张票
窗口1正在出售第84张票
窗口1正在出售第83张票
窗口3正在出售第82张票
窗口3正在出售第81张票
窗口3正在出售第80张票
窗口3正在出售第79张票
窗口3正在出售第78张票
窗口3正在出售第77张票
窗口3正在出售第76张票
窗口3正在出售第75张票
窗口3正在出售第74张票
窗口3正在出售第73张票
窗口3正在出售第72张票
窗口3正在出售第71张票
窗口3正在出售第70张票
窗口3正在出售第69张票
窗口3正在出售第68张票
窗口3正在出售第67张票
窗口3正在出售第66张票
窗口3正在出售第65张票
窗口3正在出售第64张票
窗口3正在出售第63张票
窗口3正在出售第62张票
窗口3正在出售第61张票
窗口3正在出售第60张票
窗口3正在出售第59张票
窗口3正在出售第58张票
窗口3正在出售第57张票
窗口3正在出售第56张票
窗口3正在出售第55张票
窗口3正在出售第54张票
窗口3正在出售第53张票
窗口3正在出售第52张票
窗口3正在出售第51张票
窗口3正在出售第50张票
窗口3正在出售第49张票
窗口3正在出售第48张票
窗口3正在出售第47张票
窗口3正在出售第46张票
窗口3正在出售第45张票
窗口3正在出售第44张票
窗口3正在出售第43张票
窗口3正在出售第42张票
窗口3正在出售第41张票
窗口3正在出售第40张票
窗口3正在出售第39张票
窗口3正在出售第38张票
窗口3正在出售第37张票
窗口3正在出售第36张票
窗口3正在出售第35张票
窗口3正在出售第34张票
窗口3正在出售第33张票
窗口3正在出售第32张票
窗口3正在出售第31张票
窗口3正在出售第30张票
窗口3正在出售第29张票
窗口3正在出售第28张票
窗口3正在出售第27张票
窗口3正在出售第26张票
窗口3正在出售第25张票
窗口3正在出售第24张票
窗口3正在出售第23张票
窗口3正在出售第22张票
窗口3正在出售第21张票
窗口3正在出售第20张票
窗口3正在出售第19张票
窗口3正在出售第18张票
窗口3正在出售第17张票
窗口3正在出售第16张票
窗口3正在出售第15张票
窗口3正在出售第14张票
窗口3正在出售第13张票
窗口3正在出售第12张票
窗口3正在出售第11张票
窗口3正在出售第10张票
窗口3正在出售第9张票
窗口3正在出售第8张票
窗口3正在出售第7张票
窗口3正在出售第6张票
窗口3正在出售第5张票
窗口3正在出售第4张票
窗口3正在出售第3张票
窗口3正在出售第2张票
窗口3正在出售第1张票
再次运行:
窗口2正在出售第100张票
窗口2正在出售第99张票
窗口2正在出售第98张票
窗口2正在出售第97张票
窗口2正在出售第96张票
窗口2正在出售第95张票
窗口2正在出售第94张票
窗口2正在出售第93张票
窗口2正在出售第92张票
窗口2正在出售第91张票
窗口2正在出售第90张票
窗口2正在出售第89张票
窗口2正在出售第88张票
窗口2正在出售第87张票
窗口2正在出售第86张票
窗口2正在出售第85张票
窗口2正在出售第84张票
窗口2正在出售第83张票
窗口2正在出售第82张票
窗口2正在出售第81张票
窗口2正在出售第80张票
窗口2正在出售第79张票
窗口2正在出售第78张票
窗口2正在出售第77张票
窗口2正在出售第76张票
窗口3正在出售第75张票
窗口3正在出售第74张票
窗口3正在出售第73张票
窗口3正在出售第72张票
窗口3正在出售第71张票
窗口3正在出售第70张票
窗口3正在出售第69张票
窗口3正在出售第68张票
窗口1正在出售第67张票
窗口1正在出售第66张票
窗口1正在出售第65张票
窗口1正在出售第64张票
窗口1正在出售第63张票
窗口1正在出售第62张票
窗口1正在出售第61张票
窗口1正在出售第60张票
窗口1正在出售第59张票
窗口1正在出售第58张票
窗口1正在出售第57张票
窗口1正在出售第56张票
窗口1正在出售第55张票
窗口1正在出售第54张票
窗口1正在出售第53张票
窗口1正在出售第52张票
窗口1正在出售第51张票
窗口1正在出售第50张票
窗口1正在出售第49张票
窗口1正在出售第48张票
窗口1正在出售第47张票
窗口1正在出售第46张票
窗口1正在出售第45张票
窗口1正在出售第44张票
窗口1正在出售第43张票
窗口1正在出售第42张票
窗口1正在出售第41张票
窗口1正在出售第40张票
窗口1正在出售第39张票
窗口1正在出售第38张票
窗口1正在出售第37张票
窗口1正在出售第36张票
窗口1正在出售第35张票
窗口1正在出售第34张票
窗口1正在出售第33张票
窗口1正在出售第32张票
窗口1正在出售第31张票
窗口1正在出售第30张票
窗口1正在出售第29张票
窗口1正在出售第28张票
窗口1正在出售第27张票
窗口1正在出售第26张票
窗口1正在出售第25张票
窗口1正在出售第24张票
窗口1正在出售第23张票
窗口1正在出售第22张票
窗口1正在出售第21张票
窗口1正在出售第20张票
窗口1正在出售第19张票
窗口1正在出售第18张票
窗口1正在出售第17张票
窗口1正在出售第16张票
窗口1正在出售第15张票
窗口1正在出售第14张票
窗口1正在出售第13张票
窗口1正在出售第12张票
窗口1正在出售第11张票
窗口1正在出售第10张票
窗口1正在出售第9张票
窗口1正在出售第8张票
窗口1正在出售第7张票
窗口1正在出售第6张票
窗口1正在出售第5张票
窗口1正在出售第4张票
窗口1正在出售第3张票
窗口1正在出售第2张票
窗口1正在出售第1张票

换一把锁对象试试

package cn.itcast_13;

import java.io.SyncFailedException;

public class SellTicketRunnable implements Runnable {
	private int ticketNum=100;
	//创建同一把锁
	Object obj=new Object();
	Demo d=new Demo();
	
	private int x=0;
	@Override
	public void run() {
		while(true){
			if(x%2==0){
				synchronized(d){
					if(ticketNum>0){
						//为了模拟更真实的场景我们稍作休息
						try {
							Thread.sleep(100);
						} catch (InterruptedException e) {
						
							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticketNum--)+"张票");
					}
				}
			}else{
				synchronized(d){
					if(ticketNum>0){
						//为了模拟更真实的场景我们稍作休息
						try {
							Thread.sleep(100);
						} catch (InterruptedException e) {
						
							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticketNum--)+"张票");
					}
				}
			}

			x++;
		}
	}


}

class Demo{}

测试类:

package cn.itcast_13;

/**
 * 实现Runnable接口的方式实现
 * @author jack
 *
 */
public class SellTicketRunnableDemo {

	public static void main(String[] args) {
		//创建资源对象
		SellTicketRunnable st=new SellTicketRunnable();
		
		//创建三个线程对象
		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正在出售第100张票
窗口1正在出售第99张票
窗口1正在出售第98张票
窗口1正在出售第97张票
窗口1正在出售第96张票
窗口1正在出售第95张票
窗口1正在出售第94张票
窗口1正在出售第93张票
窗口1正在出售第92张票
窗口1正在出售第91张票
窗口1正在出售第90张票
窗口1正在出售第89张票
窗口1正在出售第88张票
窗口1正在出售第87张票
窗口1正在出售第86张票
窗口1正在出售第85张票
窗口1正在出售第84张票
窗口1正在出售第83张票
窗口1正在出售第82张票
窗口1正在出售第81张票
窗口1正在出售第80张票
窗口1正在出售第79张票
窗口1正在出售第78张票
窗口1正在出售第77张票
窗口1正在出售第76张票
窗口1正在出售第75张票
窗口1正在出售第74张票
窗口1正在出售第73张票
窗口1正在出售第72张票
窗口1正在出售第71张票
窗口1正在出售第70张票
窗口1正在出售第69张票
窗口1正在出售第68张票
窗口1正在出售第67张票
窗口1正在出售第66张票
窗口1正在出售第65张票
窗口1正在出售第64张票
窗口1正在出售第63张票
窗口1正在出售第62张票
窗口1正在出售第61张票
窗口1正在出售第60张票
窗口1正在出售第59张票
窗口1正在出售第58张票
窗口1正在出售第57张票
窗口1正在出售第56张票
窗口1正在出售第55张票
窗口1正在出售第54张票
窗口1正在出售第53张票
窗口1正在出售第52张票
窗口1正在出售第51张票
窗口1正在出售第50张票
窗口3正在出售第49张票
窗口3正在出售第48张票
窗口3正在出售第47张票
窗口3正在出售第46张票
窗口3正在出售第45张票
窗口3正在出售第44张票
窗口3正在出售第43张票
窗口3正在出售第42张票
窗口3正在出售第41张票
窗口3正在出售第40张票
窗口3正在出售第39张票
窗口3正在出售第38张票
窗口3正在出售第37张票
窗口3正在出售第36张票
窗口3正在出售第35张票
窗口3正在出售第34张票
窗口3正在出售第33张票
窗口3正在出售第32张票
窗口3正在出售第31张票
窗口3正在出售第30张票
窗口3正在出售第29张票
窗口3正在出售第28张票
窗口3正在出售第27张票
窗口3正在出售第26张票
窗口3正在出售第25张票
窗口3正在出售第24张票
窗口3正在出售第23张票
窗口3正在出售第22张票
窗口3正在出售第21张票
窗口3正在出售第20张票
窗口3正在出售第19张票
窗口3正在出售第18张票
窗口2正在出售第17张票
窗口2正在出售第16张票
窗口2正在出售第15张票
窗口2正在出售第14张票
窗口2正在出售第13张票
窗口2正在出售第12张票
窗口2正在出售第11张票
窗口2正在出售第10张票
窗口2正在出售第9张票
窗口2正在出售第8张票
窗口2正在出售第7张票
窗口2正在出售第6张票
窗口2正在出售第5张票
窗口2正在出售第4张票
窗口2正在出售第3张票
窗口2正在出售第2张票
窗口2正在出售第1张票

第二次运行:

窗口1正在出售第100张票
窗口1正在出售第99张票
窗口2正在出售第98张票
窗口2正在出售第97张票
窗口2正在出售第96张票
窗口2正在出售第95张票
窗口2正在出售第94张票
窗口2正在出售第93张票
窗口2正在出售第92张票
窗口2正在出售第91张票
窗口2正在出售第90张票
窗口2正在出售第89张票
窗口2正在出售第88张票
窗口2正在出售第87张票
窗口3正在出售第86张票
窗口3正在出售第85张票
窗口3正在出售第84张票
窗口3正在出售第83张票
窗口3正在出售第82张票
窗口3正在出售第81张票
窗口3正在出售第80张票
窗口3正在出售第79张票
窗口3正在出售第78张票
窗口3正在出售第77张票
窗口3正在出售第76张票
窗口3正在出售第75张票
窗口3正在出售第74张票
窗口3正在出售第73张票
窗口3正在出售第72张票
窗口3正在出售第71张票
窗口3正在出售第70张票
窗口3正在出售第69张票
窗口3正在出售第68张票
窗口3正在出售第67张票
窗口3正在出售第66张票
窗口3正在出售第65张票
窗口3正在出售第64张票
窗口3正在出售第63张票
窗口3正在出售第62张票
窗口3正在出售第61张票
窗口3正在出售第60张票
窗口3正在出售第59张票
窗口3正在出售第58张票
窗口3正在出售第57张票
窗口3正在出售第56张票
窗口3正在出售第55张票
窗口3正在出售第54张票
窗口3正在出售第53张票
窗口3正在出售第52张票
窗口3正在出售第51张票
窗口3正在出售第50张票
窗口3正在出售第49张票
窗口3正在出售第48张票
窗口3正在出售第47张票
窗口3正在出售第46张票
窗口3正在出售第45张票
窗口3正在出售第44张票
窗口3正在出售第43张票
窗口3正在出售第42张票
窗口3正在出售第41张票
窗口3正在出售第40张票
窗口3正在出售第39张票
窗口3正在出售第38张票
窗口3正在出售第37张票
窗口3正在出售第36张票
窗口3正在出售第35张票
窗口3正在出售第34张票
窗口3正在出售第33张票
窗口3正在出售第32张票
窗口3正在出售第31张票
窗口3正在出售第30张票
窗口3正在出售第29张票
窗口3正在出售第28张票
窗口3正在出售第27张票
窗口3正在出售第26张票
窗口3正在出售第25张票
窗口3正在出售第24张票
窗口3正在出售第23张票
窗口3正在出售第22张票
窗口3正在出售第21张票
窗口3正在出售第20张票
窗口3正在出售第19张票
窗口3正在出售第18张票
窗口3正在出售第17张票
窗口3正在出售第16张票
窗口3正在出售第15张票
窗口3正在出售第14张票
窗口3正在出售第13张票
窗口3正在出售第12张票
窗口3正在出售第11张票
窗口3正在出售第10张票
窗口3正在出售第9张票
窗口3正在出售第8张票
窗口3正在出售第7张票
窗口3正在出售第6张票
窗口3正在出售第5张票
窗口3正在出售第4张票
窗口3正在出售第3张票
窗口3正在出售第2张票
窗口3正在出售第1张票

同步的特点:

   前提:多个线程

      注意:多个线程使用的是同一个锁对象

好处:同步的出现解决了线程安全的问题

弊端:当线程相当多时,因为每个线程都会去判断同步上的锁。这是很耗资源的,无形中降低程序的运行效率。

2.2 修饰实例方法

所谓的实例对象锁就是用synchronized修饰实例对象中的实例方法,注意是实例方法不包括静态方法

格式:同步关键字加在方法上

Synchronized 访问修饰符  方法返回值 方法名(){ }

实例方法的中锁对象是this.

package cn.itcast_14;

import java.io.SyncFailedException;

public class SellTicketRunnable implements Runnable {
	private int ticketNum = 100;
	// 创建同一把锁
	Object obj = new Object();
	Demo d = new Demo();

	private int x = 0;

	@Override
	public void run() {
		while (true) {
			if (x % 2 == 0) {
				synchronized (d) {
					if (ticketNum > 0) {
						// 为了模拟更真实的场景我们稍作休息
						try {
							Thread.sleep(100);
						} catch (InterruptedException e) {

							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName() + "正在出售第" + (ticketNum--) + "张票");
					}
				}
			} else {
				/*
				 * synchronized(d){ if(ticketNum>0){ //为了模拟更真实的场景我们稍作休息 try {
				 * Thread.sleep(100); } catch (InterruptedException e) {
				 * 
				 * e.printStackTrace(); }
				 * System.out.println(Thread.currentThread().getName()+"正在出售第"+(
				 * ticketNum--)+"张票"); } }
				 */
				sellTicket();
			}

			x++;
		}
	}
	//如果某个方法一进来就是看到代码被同步,那么能不能把这个同步加在方法上呢?
/*	private void sellTicket() {
		synchronized (d) {
			if (ticketNum > 0) {
				// 为了模拟更真实的场景我们稍作休息
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {

					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在出售第" + (ticketNum--) + "张票");
			}
		}

	}*/
	private synchronized void sellTicket() {
			if (ticketNum > 0) {
				// 为了模拟更真实的场景我们稍作休息
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {

					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在出售第" + (ticketNum--) + "张票");
			}
		
	}

}

class Demo {
}

测试:

package cn.itcast_14;

/**
 * 实现Runnable接口的方式实现
 * @author jack
 *
 */
public class SellTicketRunnableDemo {

	public static void main(String[] args) {
		//创建资源对象
		SellTicketRunnable st=new SellTicketRunnable();
		
		//创建三个线程对象
		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正在出售第100张票
窗口1正在出售第99张票
窗口2正在出售第98张票
窗口2正在出售第97张票
窗口3正在出售第96张票
窗口2正在出售第95张票
窗口3正在出售第94张票
窗口2正在出售第93张票
窗口3正在出售第92张票
窗口2正在出售第91张票
窗口2正在出售第90张票
窗口3正在出售第89张票
窗口1正在出售第88张票
窗口2正在出售第87张票
窗口1正在出售第86张票
窗口3正在出售第85张票
窗口1正在出售第84张票
窗口3正在出售第83张票
窗口1正在出售第82张票
窗口1正在出售第81张票
窗口3正在出售第80张票
窗口2正在出售第79张票
窗口3正在出售第78张票
窗口2正在出售第77张票
窗口3正在出售第76张票
窗口2正在出售第75张票
窗口2正在出售第74张票
窗口3正在出售第73张票
窗口1正在出售第72张票
窗口2正在出售第71张票
窗口3正在出售第70张票
窗口1正在出售第69张票
窗口2正在出售第68张票
窗口3正在出售第67张票
窗口1正在出售第66张票
窗口1正在出售第65张票
窗口3正在出售第64张票
窗口2正在出售第63张票
窗口1正在出售第62张票
窗口1正在出售第61张票
窗口2正在出售第60张票
窗口3正在出售第59张票
窗口1正在出售第58张票
窗口2正在出售第57张票
窗口3正在出售第56张票
窗口1正在出售第55张票
窗口2正在出售第54张票
窗口3正在出售第53张票
窗口1正在出售第52张票
窗口2正在出售第51张票
窗口1正在出售第50张票
窗口2正在出售第49张票
窗口1正在出售第48张票
窗口2正在出售第47张票
窗口1正在出售第46张票
窗口2正在出售第45张票
窗口1正在出售第44张票
窗口2正在出售第43张票
窗口1正在出售第42张票
窗口2正在出售第41张票
窗口1正在出售第40张票
窗口2正在出售第39张票
窗口1正在出售第38张票
窗口2正在出售第37张票
窗口2正在出售第36张票
窗口1正在出售第35张票
窗口2正在出售第34张票
窗口3正在出售第33张票
窗口2正在出售第32张票
窗口3正在出售第31张票
窗口2正在出售第30张票
窗口2正在出售第29张票
窗口3正在出售第28张票
窗口1正在出售第27张票
窗口3正在出售第26张票
窗口1正在出售第25张票
窗口3正在出售第24张票
窗口1正在出售第23张票
窗口3正在出售第22张票
窗口1正在出售第21张票
窗口1正在出售第20张票
窗口3正在出售第19张票
窗口2正在出售第18张票
窗口1正在出售第17张票
窗口2正在出售第16张票
窗口3正在出售第15张票
窗口2正在出售第14张票
窗口3正在出售第13张票
窗口2正在出售第12张票
窗口3正在出售第11张票
窗口2正在出售第10张票
窗口3正在出售第9张票
窗口2正在出售第8张票
窗口3正在出售第7张票
窗口2正在出售第6张票
窗口2正在出售第5张票
窗口3正在出售第4张票
窗口1正在出售第3张票
窗口3正在出售第2张票
窗口1正在出售第1张票
窗口3正在出售第0张票
 

结果出现了0票的情况。。

把锁对象换成this关键字

package cn.itcast_14;

import java.io.SyncFailedException;

public class SellTicketRunnable implements Runnable {
	private int ticketNum = 100;
	// 创建同一把锁
	Object obj = new Object();
	Demo d = new Demo();

	private int x = 0;

	@Override
	public void run() {
		while (true) {
			if (x % 2 == 0) {
				synchronized (this) {
					if (ticketNum > 0) {
						// 为了模拟更真实的场景我们稍作休息
						try {
							Thread.sleep(100);
						} catch (InterruptedException e) {

							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName() + "正在出售第" + (ticketNum--) + "张票");
					}
				}
			} else {
				/*
				 * synchronized(d){ if(ticketNum>0){ //为了模拟更真实的场景我们稍作休息 try {
				 * Thread.sleep(100); } catch (InterruptedException e) {
				 * 
				 * e.printStackTrace(); }
				 * System.out.println(Thread.currentThread().getName()+"正在出售第"+(
				 * ticketNum--)+"张票"); } }
				 */
				sellTicket();
			}

			x++;
		}
	}
	//如果某个方法一进来就是看到代码被同步,那么能不能把这个同步加在方法上呢?
/*	private void sellTicket() {
		synchronized (d) {
			if (ticketNum > 0) {
				// 为了模拟更真实的场景我们稍作休息
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {

					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在出售第" + (ticketNum--) + "张票");
			}
		}

	}*/
	private synchronized void sellTicket() {
			if (ticketNum > 0) {
				// 为了模拟更真实的场景我们稍作休息
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {

					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在出售第" + (ticketNum--) + "张票");
			}
		
	}

}

class Demo {
}

测试:

package cn.itcast_14;

/**
 * 实现Runnable接口的方式实现
 * @author jack
 *
 */
public class SellTicketRunnableDemo {

	public static void main(String[] args) {
		//创建资源对象
		SellTicketRunnable st=new SellTicketRunnable();
		
		//创建三个线程对象
		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正在出售第100张票
窗口3正在出售第99张票
窗口3正在出售第98张票
窗口3正在出售第97张票
窗口3正在出售第96张票
窗口3正在出售第95张票
窗口3正在出售第94张票
窗口3正在出售第93张票
窗口3正在出售第92张票
窗口3正在出售第91张票
窗口3正在出售第90张票
窗口3正在出售第89张票
窗口1正在出售第88张票
窗口1正在出售第87张票
窗口1正在出售第86张票
窗口1正在出售第85张票
窗口1正在出售第84张票
窗口1正在出售第83张票
窗口3正在出售第82张票
窗口2正在出售第81张票
窗口2正在出售第80张票
窗口2正在出售第79张票
窗口2正在出售第78张票
窗口2正在出售第77张票
窗口2正在出售第76张票
窗口2正在出售第75张票
窗口2正在出售第74张票
窗口2正在出售第73张票
窗口2正在出售第72张票
窗口2正在出售第71张票
窗口2正在出售第70张票
窗口2正在出售第69张票
窗口2正在出售第68张票
窗口2正在出售第67张票
窗口2正在出售第66张票
窗口2正在出售第65张票
窗口2正在出售第64张票
窗口2正在出售第63张票
窗口2正在出售第62张票
窗口2正在出售第61张票
窗口2正在出售第60张票
窗口2正在出售第59张票
窗口2正在出售第58张票
窗口2正在出售第57张票
窗口2正在出售第56张票
窗口2正在出售第55张票
窗口2正在出售第54张票
窗口2正在出售第53张票
窗口2正在出售第52张票
窗口2正在出售第51张票
窗口2正在出售第50张票
窗口2正在出售第49张票
窗口2正在出售第48张票
窗口2正在出售第47张票
窗口2正在出售第46张票
窗口2正在出售第45张票
窗口2正在出售第44张票
窗口2正在出售第43张票
窗口2正在出售第42张票
窗口2正在出售第41张票
窗口2正在出售第40张票
窗口2正在出售第39张票
窗口2正在出售第38张票
窗口2正在出售第37张票
窗口2正在出售第36张票
窗口2正在出售第35张票
窗口2正在出售第34张票
窗口2正在出售第33张票
窗口2正在出售第32张票
窗口2正在出售第31张票
窗口2正在出售第30张票
窗口2正在出售第29张票
窗口2正在出售第28张票
窗口2正在出售第27张票
窗口2正在出售第26张票
窗口2正在出售第25张票
窗口2正在出售第24张票
窗口2正在出售第23张票
窗口2正在出售第22张票
窗口2正在出售第21张票
窗口2正在出售第20张票
窗口2正在出售第19张票
窗口2正在出售第18张票
窗口2正在出售第17张票
窗口2正在出售第16张票
窗口2正在出售第15张票
窗口2正在出售第14张票
窗口2正在出售第13张票
窗口2正在出售第12张票
窗口2正在出售第11张票
窗口2正在出售第10张票
窗口2正在出售第9张票
窗口2正在出售第8张票
窗口2正在出售第7张票
窗口2正在出售第6张票
窗口2正在出售第5张票
窗口2正在出售第4张票
窗口2正在出售第3张票
窗口2正在出售第2张票
窗口2正在出售第1张票

2.3修饰静态方法

当synchronized作用于静态方法时,其锁就是当前类的class对象锁。由于静态成员不专属于任何一个实例对象,是类成员,因此通过class对象锁可以控制静态 成员的并发操作。

如果一个线程窗口1调用一个实例对象的非static synchronized方法,而窗口2需要调用这个实例对象所属类的静态 synchronized方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的class对象,而访问非静态 synchronized 方法占用的锁是当前实例对象锁。

package cn.itcast_15;

import java.io.SyncFailedException;

public class SellTicketRunnable implements Runnable {
	private static int ticketNum = 100;
	// 创建同一把锁
	Object obj = new Object();
	Demo d = new Demo();

	private int x = 0;

	@Override
	public void run() {
		while (true) {
			if (x % 2 == 0) {
				synchronized (this) {
					if (ticketNum > 0) {
						// 为了模拟更真实的场景我们稍作休息
						try {
							Thread.sleep(100);
						} catch (InterruptedException e) {

							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName() + "正在出售第" + (ticketNum--) + "张票");
					}
				}
			} else {
				/*
				 * synchronized(d){ if(ticketNum>0){ //为了模拟更真实的场景我们稍作休息 try {
				 * Thread.sleep(100); } catch (InterruptedException e) {
				 * 
				 * e.printStackTrace(); }
				 * System.out.println(Thread.currentThread().getName()+"正在出售第"+(
				 * ticketNum--)+"张票"); } }
				 */
				sellTicket();
			}

			x++;
		}
	}
	//如果某个方法一进来就是看到代码被同步,那么能不能把这个同步加在方法上呢?
/*	private void sellTicket() {
		synchronized (d) {
			if (ticketNum > 0) {
				// 为了模拟更真实的场景我们稍作休息
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {

					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在出售第" + (ticketNum--) + "张票");
			}
		}

	}*/
	private static synchronized void sellTicket() {
			if (ticketNum > 0) {
				// 为了模拟更真实的场景我们稍作休息
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {

					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在出售第" + (ticketNum--) + "张票");
			}
		
	}

}

class Demo {
}


测试结果:

package cn.itcast_15;

/**
 * 实现Runnable接口的方式实现
 * @author jack
 *
 */
public class SellTicketRunnableDemo {

	public static void main(String[] args) {
		//创建资源对象
		SellTicketRunnable st=new SellTicketRunnable();
		
		//创建三个线程对象
		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正在出售第100张票
窗口3正在出售第99张票
窗口2正在出售第98张票
窗口1正在出售第97张票
窗口2正在出售第96张票
窗口1正在出售第95张票
窗口2正在出售第94张票
窗口1正在出售第93张票
窗口2正在出售第92张票
窗口1正在出售第91张票
窗口2正在出售第90张票
窗口1正在出售第89张票
窗口2正在出售第88张票
窗口1正在出售第87张票
窗口2正在出售第86张票
窗口1正在出售第85张票
窗口2正在出售第84张票
窗口1正在出售第83张票
窗口2正在出售第82张票
窗口1正在出售第81张票
窗口1正在出售第80张票
窗口2正在出售第79张票
窗口3正在出售第78张票
窗口1正在出售第77张票
窗口3正在出售第76张票
窗口2正在出售第75张票
窗口3正在出售第74张票
窗口3正在出售第73张票
窗口2正在出售第72张票
窗口1正在出售第71张票
窗口2正在出售第70张票
窗口1正在出售第69张票
窗口2正在出售第68张票
窗口1正在出售第67张票
窗口2正在出售第66张票
窗口1正在出售第65张票
窗口2正在出售第64张票
窗口1正在出售第63张票
窗口2正在出售第62张票
窗口1正在出售第61张票
窗口2正在出售第60张票
窗口1正在出售第59张票
窗口1正在出售第58张票
窗口2正在出售第57张票
窗口3正在出售第56张票
窗口3正在出售第55张票
窗口2正在出售第54张票
窗口1正在出售第53张票
窗口3正在出售第52张票
窗口3正在出售第51张票
窗口1正在出售第50张票
窗口2正在出售第49张票
窗口3正在出售第48张票
窗口1正在出售第47张票
窗口2正在出售第46张票
窗口3正在出售第45张票
窗口2正在出售第44张票
窗口1正在出售第43张票
窗口1正在出售第42张票
窗口2正在出售第41张票
窗口3正在出售第40张票
窗口1正在出售第39张票
窗口2正在出售第38张票
窗口3正在出售第37张票
窗口3正在出售第36张票
窗口2正在出售第35张票
窗口1正在出售第34张票
窗口3正在出售第33张票
窗口2正在出售第32张票
窗口3正在出售第31张票
窗口1正在出售第30张票
窗口3正在出售第29张票
窗口1正在出售第28张票
窗口3正在出售第27张票
窗口3正在出售第26张票
窗口2正在出售第25张票
窗口3正在出售第24张票
窗口2正在出售第23张票
窗口1正在出售第22张票
窗口1正在出售第21张票
窗口2正在出售第20张票
窗口2正在出售第19张票
窗口1正在出售第18张票
窗口3正在出售第17张票
窗口2正在出售第16张票
窗口1正在出售第15张票
窗口1正在出售第14张票
窗口2正在出售第13张票
窗口3正在出售第12张票
窗口1正在出售第11张票
窗口2正在出售第10张票
窗口3正在出售第9张票
窗口1正在出售第8张票
窗口2正在出售第7张票
窗口3正在出售第6张票
窗口1正在出售第5张票
窗口2正在出售第4张票
窗口3正在出售第3张票
窗口3正在出售第2张票
窗口1正在出售第1张票
窗口2正在出售第0张票

当修饰静态对象且锁对象为this出现了0票。。。。

package cn.itcast_15;

import java.io.SyncFailedException;

public class SellTicketRunnable implements Runnable {
	private static int ticketNum = 100;
	// 创建同一把锁
	Object obj = new Object();
	Demo d = new Demo();

	private int x = 0;

	@Override
	public void run() {
		while (true) {
			if (x % 2 == 0) {
				synchronized (this.getClass()) {
					if (ticketNum > 0) {
						// 为了模拟更真实的场景我们稍作休息
						try {
							Thread.sleep(100);
						} catch (InterruptedException e) {

							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName() + "正在出售第" + (ticketNum--) + "张票");
					}
				}
			} else {
				/*
				 * synchronized(d){ if(ticketNum>0){ //为了模拟更真实的场景我们稍作休息 try {
				 * Thread.sleep(100); } catch (InterruptedException e) {
				 * 
				 * e.printStackTrace(); }
				 * System.out.println(Thread.currentThread().getName()+"正在出售第"+(
				 * ticketNum--)+"张票"); } }
				 */
				sellTicket();
			}

			x++;
		}
	}
	//如果某个方法一进来就是看到代码被同步,那么能不能把这个同步加在方法上呢?
/*	private void sellTicket() {
		synchronized (d) {
			if (ticketNum > 0) {
				// 为了模拟更真实的场景我们稍作休息
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {

					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在出售第" + (ticketNum--) + "张票");
			}
		}

	}*/
	private static synchronized void sellTicket() {
			if (ticketNum > 0) {
				// 为了模拟更真实的场景我们稍作休息
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {

					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在出售第" + (ticketNum--) + "张票");
			}
		
	}

}

class Demo {
}

测试类:

package cn.itcast_15;

/**
 * 实现Runnable接口的方式实现
 * @author jack
 *
 */
public class SellTicketRunnableDemo {

    public static void main(String[] args) {
        //创建资源对象
        SellTicketRunnable st=new SellTicketRunnable();
        
        //创建三个线程对象
        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正在出售第100张票
窗口1正在出售第99张票
窗口1正在出售第98张票
窗口1正在出售第97张票
窗口2正在出售第96张票
窗口2正在出售第95张票
窗口1正在出售第94张票
窗口1正在出售第93张票
窗口1正在出售第92张票
窗口1正在出售第91张票
窗口3正在出售第90张票
窗口3正在出售第89张票
窗口1正在出售第88张票
窗口1正在出售第87张票
窗口1正在出售第86张票
窗口1正在出售第85张票
窗口2正在出售第84张票
窗口2正在出售第83张票
窗口2正在出售第82张票
窗口2正在出售第81张票
窗口2正在出售第80张票
窗口2正在出售第79张票
窗口2正在出售第78张票
窗口2正在出售第77张票
窗口2正在出售第76张票
窗口2正在出售第75张票
窗口2正在出售第74张票
窗口2正在出售第73张票
窗口2正在出售第72张票
窗口2正在出售第71张票
窗口2正在出售第70张票
窗口2正在出售第69张票
窗口2正在出售第68张票
窗口2正在出售第67张票
窗口1正在出售第66张票
窗口1正在出售第65张票
窗口1正在出售第64张票
窗口1正在出售第63张票
窗口1正在出售第62张票
窗口1正在出售第61张票
窗口1正在出售第60张票
窗口1正在出售第59张票
窗口1正在出售第58张票
窗口1正在出售第57张票
窗口1正在出售第56张票
窗口1正在出售第55张票
窗口1正在出售第54张票
窗口1正在出售第53张票
窗口1正在出售第52张票
窗口1正在出售第51张票
窗口1正在出售第50张票
窗口1正在出售第49张票
窗口1正在出售第48张票
窗口1正在出售第47张票
窗口1正在出售第46张票
窗口1正在出售第45张票
窗口1正在出售第44张票
窗口1正在出售第43张票
窗口1正在出售第42张票
窗口1正在出售第41张票
窗口1正在出售第40张票
窗口1正在出售第39张票
窗口1正在出售第38张票
窗口1正在出售第37张票
窗口1正在出售第36张票
窗口1正在出售第35张票
窗口1正在出售第34张票
窗口1正在出售第33张票
窗口1正在出售第32张票
窗口1正在出售第31张票
窗口1正在出售第30张票
窗口1正在出售第29张票
窗口1正在出售第28张票
窗口1正在出售第27张票
窗口1正在出售第26张票
窗口1正在出售第25张票
窗口1正在出售第24张票
窗口1正在出售第23张票
窗口1正在出售第22张票
窗口1正在出售第21张票
窗口1正在出售第20张票
窗口1正在出售第19张票
窗口1正在出售第18张票
窗口1正在出售第17张票
窗口1正在出售第16张票
窗口1正在出售第15张票
窗口1正在出售第14张票
窗口1正在出售第13张票
窗口1正在出售第12张票
窗口1正在出售第11张票
窗口1正在出售第10张票
窗口1正在出售第9张票
窗口1正在出售第8张票
窗口1正在出售第7张票
窗口1正在出售第6张票
窗口1正在出售第5张票
窗口1正在出售第4张票
窗口1正在出售第3张票
窗口1正在出售第2张票
窗口1正在出售第1张票


三、synchronized的基本原则

3.1 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞

3.2 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块

3.3 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。


第一条

当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。

package cn.itcast_16;

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		synchronized (this) {
			for(int i=1;i<10;i++){
				try {
					Thread.sleep(100);
					System.out.println(Thread.currentThread().getName() + ":" + i);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} // 休眠100ms
				
			}
		}

	}

}

测试类:

package cn.itcast_16;

public class MyRunableTest {

	public static void main(String[] args) {
		MyRunnable mr=new MyRunnable();
		
		Thread t1 = new Thread(mr, "t1");  // 新建“线程t1”, t1是基于mr这个Runnable对象
		Thread t2 = new Thread(mr, "t2");  // 新建“线程t2”, t2是基于mr这个Runnable对象
		
		t1.start();
		t2.start();
	}

}


运行结果:

t1:1
t1:2
t1:3
t1:4
t1:5
t1:6
t1:7
t1:8
t1:9
t2:1
t2:2
t2:3
t2:4
t2:5
t2:6
t2:7
t2:8
t2:9

第二条

当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块。

package cn.itcast_17;

public class SynchronizeDemo {
	// 含有synchronized同步块的方法
	 public void synMethod() {
			synchronized (this) {
				for(int i=1;i<10;i++){
					try {
						Thread.sleep(100);
						System.out.println(Thread.currentThread().getName() + "synchronized同步:" + i);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} // 休眠100ms
					
				}
			}		 
	 }
	 // 非同步的方法
	 public void nonSynMethod() {
		 try {
			 for(int i=1;i<10;i++){
				 Thread.sleep(100);
				 System.out.println(Thread.currentThread().getName() + "nosynchronized不同步:" + i);
			 } 
		 }catch (InterruptedException ie) { 
			 
		 }
	 }
}
package cn.itcast_17;

public class SynchronizeTest {

	public static void main(String[] args) {
		SynchronizeDemo sd=new SynchronizeDemo();
		
		 Thread t1 = new Thread(
				 new Runnable() {
					 @Override
					 public void run() {
						 sd.synMethod();
					 }
				 }, "t1");
		 Thread t2 = new Thread(
				 new Runnable() {
					 @Override
					 public void run() {
						 sd.nonSynMethod();
					 }
				 }, "t2");
		 
		 t1.start();
		 t2.start();
		 
	}

}

运行结果:

t2nosynchronized不同步:1
t1synchronized同步:1
t2nosynchronized不同步:2
t2nosynchronized不同步:3
t1synchronized同步:2
t2nosynchronized不同步:4
t1synchronized同步:3
t2nosynchronized不同步:5
t2nosynchronized不同步:6
t1synchronized同步:4
t2nosynchronized不同步:7
t1synchronized同步:5
t2nosynchronized不同步:8
t1synchronized同步:6
t2nosynchronized不同步:9
t1synchronized同步:7
t1synchronized同步:8
t1synchronized同步:9

第三条:

当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。

package cn.itcast_18;

public class SynchronizeDemo {
	// 含有synchronized同步块的方法
	 public void synMethod() {
			synchronized (this) {
				for(int i=1;i<10;i++){
					try {
						Thread.sleep(100);
						System.out.println(Thread.currentThread().getName() + "synMethod()中同步:" + i);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} // 休眠100ms
					
				}
			}		 
	 }
	 // 另一个同步的方法
	 public void nonSynMethod() {
		 synchronized (this) {
			 try {
				 for(int i=1;i<10;i++){
					 Thread.sleep(100);
					 System.out.println(Thread.currentThread().getName() + "nonSynMethod中同步:" + i);
				 } 
			 }catch (InterruptedException ie) { 
				 
			 }
		 }

	 }
}


测试类:

package cn.itcast_18;

public class SynchronizeTest {

	public static void main(String[] args) {
		SynchronizeDemo sd=new SynchronizeDemo();
		
		 Thread t1 = new Thread(
				 new Runnable() {
					 @Override
					 public void run() {
						 sd.synMethod();
					 }
				 }, "t1");
		 Thread t2 = new Thread(
				 new Runnable() {
					 @Override
					 public void run() {
						 sd.nonSynMethod();
					 }
				 }, "t2");
		 
		 t1.start();
		 t2.start();
		 
	}

}

运行结果:

t1synMethod()中同步:1
t1synMethod()中同步:2
t1synMethod()中同步:3
t1synMethod()中同步:4
t1synMethod()中同步:5
t1synMethod()中同步:6
t1synMethod()中同步:7
t1synMethod()中同步:8
t1synMethod()中同步:9
t2nonSynMethod中同步:1
t2nonSynMethod中同步:2
t2nonSynMethod中同步:3
t2nonSynMethod中同步:4
t2nonSynMethod中同步:5
t2nonSynMethod中同步:6
t2nonSynMethod中同步:7
t2nonSynMethod中同步:8
t2nonSynMethod中同步:9

猜你喜欢

转载自blog.csdn.net/qq_35558797/article/details/80013041