线程高级应用-心得2-同步锁讲解及面试题案例分析

1.引入同步锁

2.同步锁案例分析

package com.itcast.family;

public class TraditionalThreadSynchronized {

	public static void main(String[] args) {
		new TraditionalThreadSynchronized().init();

	}

	// 该方法的作用是:外部类的静态方法不能实例化内部类对象;所以不能直接在外部类的main实例化,要创建一个中介的普通方法
	private void init() {
		final Outputer outputer = new Outputer();
		//线程1
		new Thread(new Runnable() {

			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					outputer.output("yangkai");
				}
			}
		}).start();
		//线程2
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					outputer.output3("123456");
				}
			}
		}).start();
	}

	static class Outputer {
		//第一种使用锁的方法:部分代码块上锁
		String xxx = "";
		public void output(String name) {
			/*synchronized()锁中可以传参是任何的一个空串,结果同步了但是应该会有其他问题,执行结果:
			 *  yangkai
       			123456
				123456
				yangkai
				123456
				也可以是方法传进来的参数,比如这里name可以执行,但是结果会有问题;执行结果:
				y1a2n3g4k5a6
				i
				yangkai
				123456
				所以一般都是用this,代表这个类本身,方法本身;
				
				用了锁线程里的对象就要是同一个对象,否则会出错;比如上门代码用的都是同一个Outputer对象outputer,
				要是将outputer换成new Outputer就会出错,因为是两个Outputer对象
			 */
			synchronized(Outputer.class){
			for (int i = 0; i < name.length(); i++) {
				//读取字符串内一个一个的字符
				System.out.print(name.charAt(i));
			}
			System.out.println();
			}
		}
		
		
		//第二种使用锁的方法:整个方法上锁
		//一般一个类中只使用一把锁,外面上了锁里面就不用上了,否则会出现死锁的状况
		/*上面两个线程一个用的output()方法中的锁,一个用的是output2()方法中的锁,
		 * 这样也可以互斥,达到同步的要求;因为他们用的是同一把门栓,即同一个对象;
		 * 都是是Outputer对象,一个this一个直接是其对象中的方法
		 */
		public synchronized void output2(String name) {
		for (int i = 0; i < name.length(); i++) {
			//读取字符串内一个一个的字符
			System.out.print(name.charAt(i));
		}
		System.out.println();
		}
		
		//3.第三种静态锁的方法,这时需要内部类也是静态的
		/*这时如果直接用第一种或第二种锁,不会与第三种形成互斥锁,因为静态方法与普通方法中的对象不是一个,
		 * 这时只需要把第一种锁的中的this参数改成Outputer.class即可
		 */
		public static synchronized void output3(String name) {
			for (int i = 0; i < name.length(); i++) {
				//读取字符串内一个一个的字符
				System.out.print(name.charAt(i));
			}
			System.out.println();
		}
	}
	
	
	/*
	 * 如果不使用线程锁synchronized会出现以下情况:
	 *  yangkai
		123456
		yangkai
		1y2a3n4g5k6
		ai 
	 */
}
3.同步锁面试题分析
package com.itcast.family;

/**
 * 面试题:
 *    子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,
 * 接着再回到主线程又循环100次,如此循环50次,代码如下:
 * 
 * 思路:
 *    编写一个业务类,是为了不把自己绕进去,体现代码的的高聚类特性和代码的健壮性,
 * 即共同数据(比如这里的同步锁)或共同算法的若干个方法都可以提到同一个类中编写
 */
public class TraditionalThreadCommunication {

	public static void main(String[] args) {
		final Business business = new Business();
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				for(int i=1;i<=50;i++){
					business.sub(i);
				}
			}
		}).start();
		
		for(int i=1;i<=50;i++){
			business.main(i);
	}

}
}

//编写一个有子方法(用来调用子线程)和主方法(调用主线程)的业务类
class Business{
	private boolean bShouldSub = true;
	public synchronized void sub(int i){
		while(!bShouldSub){
			try {
				//如果没轮到自己就等一会
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		for(int j=1;j<=10;j++){
			System.out.println("sub thread sequence of  " + j + " ,loop of  " + i);
		}
		bShouldSub = false;
		//唤醒下一个等待的线程
		this.notify();
	}
	public synchronized void main(int i){
		/*这里最好用while,但是跟if的效果一样,只是前者代码更健壮,
		 * while可以防止线程自己唤醒自己,即通常所说的伪唤醒;
		 * 相当于一个人做梦不是被别人叫醒而是自己做噩梦突然惊醒;
		 * 这时用while可以防止这种情况发生
		 */
		while(bShouldSub){
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		for(int j=1;j<=100;j++){
			System.out.println("main thread sequence of  " + j + " ,loop of  " + i);
		}
		bShouldSub = true;
		this.notify();
	}
}


 

 

posted @ 2013-02-07 14:03  yangkai_keven  阅读(171)  评论(0编辑  收藏  举报