同步

同步

多线程并发访问同一资源时,就会形成"抢"的现象。
由于线程切换时机不确定,可能导致执行代码顺序的混乱,严重时会导致系统瘫痪。

同步方法

class Table{
	//桌子上有20个豆子
	private int beans = 20;
	/**
	 * 当一个方法被synchronized修饰后,该方法为同步方法,
	 * 即:多个线程不能同时进入方法内部执行。
	 * 对于成员方法而言,synchronized会在一个线程调用该方法时将该方法所属对象加锁,
	 * 其他线程在执行该方法时由于执行方法的线程没有释放锁,
	 * 所以只能在方法外阻塞,直到持有方法锁的线程将方法执行完毕。
	 * 所以,解决多线程并发执行安全问题的方法就是将"抢"变为"排队"
	 * @return
	 */
	public synchronized int getBean() {
		if(beans==0) {
			throw new RuntimeException("没有豆子了!");
		}
		Thread.yield();//暂停当前执行的线程,执行其它线程
		return beans--;
	}
}
public static void main(String[] args) {
	final Table table = new Table();
	Thread t1 = new Thread() {
		public void run() {
			while(true) {
				int bean = table.getBean();
				Thread.yield();
				System.out.println(getName()+":"+bean);
			}
		}
	};
	
	Thread t2 = new Thread() {
		public void run() {
			while(true) {
				int bean = table.getBean();
				Thread.yield();
				System.out.println(getName()+":"+bean);
			}
		}
	};
	t1.start();
	t2.start();
}
运行结果:
Thread-0:20
Thread-1:19
Thread-0:18
Thread-1:17
Thread-0:16
Thread-1:15
Thread-0:14
Thread-1:13
Thread-0:12
Thread-1:11
Thread-0:10
Thread-1:9
Thread-0:8
Thread-1:7
Thread-0:6
Thread-1:5
Thread-0:4
Thread-1:3
Thread-0:2
Thread-1:1
Exception in thread "Thread-1" Exception in thread "Thread-0" java.lang.RuntimeException: 没有豆子了!
	at day10.Table.getBean(SyncDemo.java:51)
	at day10.SyncDemo$1.run(SyncDemo.java:15)
java.lang.RuntimeException: 没有豆子了!
	at day10.Table.getBean(SyncDemo.java:51)
	at day10.SyncDemo$2.run(SyncDemo.java:25)

同步块

有效的缩小同步范围可以在保证并发安全的同时尽可能提高并发率。

class Shop{
	public void buy() {
		//获取运行buy方法的线程
		Thread t = Thread.currentThread();
		try {
			System.out.println(t.getName()+":"+"正在挑衣服..");
			Thread.sleep(5000);
			/**
			 * 同步块可以要求多个线程对该块内的代码排队执行,
			 * 但是前提条件是同步监视器对象即(上锁的对象)要求多个线程看到的必须是同一个。
			 * synchronized(同步监视器对象){
			 * 		需要同步的代码
			 * }
			 * 所谓同步执行即:多个线程必须排队执行
			 * 所谓异步执行即:多个线程可以同时执行
			 */
			synchronized(this) {
				System.out.println(t.getName()+":"+"正在试衣服..");
				Thread.sleep(5000);
			}
			
			System.out.println(t.getName()+":"+"结账离开");
			Thread.sleep(5000);
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
}
public static void main(String[] args) {
	final Shop shop = new Shop();
	Thread t1 = new Thread() {
		public void run() {
			shop.buy();
		}
	};
	Thread t2 = new Thread() {
		public void run() {
			shop.buy();
		}
	};
	t1.start();
	t2.start();
}
运行结果:
Thread-0:正在挑衣服..
Thread-1:正在挑衣服..
Thread-0:正在试衣服..
Thread-1:正在试衣服..
Thread-0:结账离开
Thread-1:结账离开

静态方法的同步

当一个静态方法被synchronized修饰后,那么该方法即为同步方法,由于静态方法从属类,全局就一份,所以同步的静态方法一定具有同步效果,与对象无关。

class Foo{
	public synchronized static void dosome() {
		try {
			Thread t = Thread.currentThread();
			System.out.println(t.getName()+":正在执行dosome方法...");
			Thread.sleep(5000);
			System.out.println(t.getName()+":执行dosome方法完毕!");
		}catch(Exception e) {
			e.printStackTrace();
		}
	}	
}
public static void main(String[] args) {
	final Foo f1 = new Foo();
	final Foo f2 = new Foo();
	Thread t1 = new Thread() {
		public void run() {
			f1.dosome();//与对象无关
		}
	};
	Thread t2 = new Thread() {
		public void run() {
			f2.dosome();
		}
	};
	t1.start();
	t2.start();
}
运行结果:
Thread-0:正在执行dosome方法...
Thread-0:执行dosome方法完毕!
Thread-1:正在执行dosome方法...
Thread-1:执行dosome方法完毕!

互斥锁

synchronized也叫互斥锁,即:使用synchronized修饰多块代码,只要他们的同步监视对象相同,那么这几段代码间就是互斥关系,即:多个线程不能同时执行这些代码。

class Boo{
	public synchronized void methodA() {
		try {
			Thread t = Thread.currentThread();
			System.out.println(t.getName()+":正在执行A方法");
			Thread.sleep(5000);
			System.out.println(t.getName()+":运行A方法完毕");
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	public void methodB() {
		synchronized(this) {
			try {
				Thread t = Thread.currentThread();
				System.out.println(t.getName()+":正在执行B方法");
				Thread.sleep(5000);
				System.out.println(t.getName()+":运行B方法完毕");
			}catch(Exception e) {
				e.printStackTrace();
			}
		}
		
	}
}
public static void main(String[] args) {
	final Boo boo = new Boo();
	Thread t1 = new Thread() {
		public void run() {
			boo.methodA();
		}
	};
	
	Thread t2 = new Thread() {
		public void run() {
			boo.methodB();
		}
	};
	t1.start();
	t2.start();
}
运行结果:
Thread-0:正在执行A方法
Thread-0:运行A方法完毕
Thread-1:正在执行B方法
Thread-1:运行B方法完毕

将集合或Map转换为线程安全的

public static void main(String[] args) {
	/*
	 * ArrayList与LinkedList都不是线程安全的
	 */
	List<String> list = new ArrayList<String>();
	list.add("one");
	list.add("two");
	list.add("three");
	list.add("four");
	System.out.println(list);
	//将给定集合转换为线程安全的集合
	list = Collections.synchronizedList(list);
	System.out.println(list);
	
	/*
	 * HashSet不是线程安全的
	 */
	Set<String> set = new HashSet<String>(list);
	System.out.println(set);
	//将给定的Set集合转换为线程安全的
	set = Collections.synchronizedSet(set);
	System.out.println(set);
	/*
	 * HashMap也不是线性安全的
	 */
	Map<String,Integer> map = new HashMap<String,Integer>();
	map.put("语文",99 );
	map.put("数学",98 );
	map.put("英语",97 );
	System.out.println(map);
	map = Collections.synchronizedMap(map);
	System.out.println(map);
	
	
	/*
	 * API手册上有说明
	 * 就算是线程安全的集合那么其中对于元素的操作,
	 * 如:add,remove等方法都不予迭代器遍历互斥,需要自行维护互斥关系。
	 */
}
运行结果:
[one, two, three, four]
[one, two, three, four]
[four, one, two, three]
[four, one, two, three]
{数学=98, 语文=99, 英语=97}
{数学=98, 语文=99, 英语=97}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值