java 线程通信

package com.zkk;

public class Test1 {
	public static void main(String[] args) {
		new Test1().init();
	}

	private void init() {
		final Outputer outputer = new Outputer();// 静态方法中不能new 内部类的实例对象
		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					outputer.output("zhoukeke");
				}
			}
		}).start();
		
		
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					outputer.output("zhangsan");
				}
			}
		}).start();
	}

	class Outputer {
		public void output(String name) {
			int len = name.length();
			for (int i = 0; i < len; i++) {
				System.out.print(name.charAt(i));
			}
			System.out.println();
		}
	}
}


以上的代码是有问题的;问题如下


 会打印如图所示的字符串,用张老师的话来说,这是一个奇迹,多线程就会出现这种问题 cpu 跑到另外一个线程上啦

为了防止上述问题,使用同步技术

output 方法中的代码要实现原子性 当有一个线程来执行我的时候,别的线程不能来执行我 ,就像厕所里面的坑一样,有一个人在里面的时候,别人不能进来

在java 中是这么解决问题的,把你要保护的代码用关键字保护起来,排他性 synchronized 


package com.zkk;

public class Test1 {
	public static void main(String[] args) {
		new Test1().init();
	}

	private void init() {
		final Outputer outputer = new Outputer();// 静态方法中不能new 内部类的实例对象
		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					outputer.output("zhoukeke");
				}
			}
		}).start();

		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					outputer.output("zhangsan");
				}
			}
		}).start();
	}

	class Outputer {
		String xxx="";
		public void output(String name) {
			int len = name.length();
			synchronized (xxx) {//找任意一个对象就行 这里的对象用name 就不行 使用name 达不到同步的效果 必须是同一个对象
				                 // 使用xxx 对象 就没有奇迹啦
				for (int i = 0; i < len; i++) {
					System.out.print(name.charAt(i));
				}
				System.out.println();
			}
		}
	}
}

其实上述例子还是多此一举,用this 来代表锁

package com.zkk;

public class Test1 {
	public static void main(String[] args) {
		new Test1().init();
	}

	private void init() {
		final Outputer outputer = new Outputer();// 静态方法中不能new 内部类的实例对象
		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					outputer.output("zhoukeke");
				}
			}
		}).start();

		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					outputer.output("zhangsan");
				}
			}
		}).start();
	}

	class Outputer {
		public void output(String name) {
			int len = name.length();
			synchronized (this) {//找任意一个对象就行 这里的对象用name 就不行 使用name 达不到同步的效果 必须是同一个对象
				                 // 使用xxx 对象 就没有奇迹啦
				for (int i = 0; i < len; i++) {
					System.out.print(name.charAt(i));
				}
				System.out.println();
			}
		}
	}
}

方法中使用synchronized


package com.zkk;

public class Test1 {
	public static void main(String[] args) {
		new Test1().init();
	}

	private void init() {
		final Outputer outputer = new Outputer();// 静态方法中不能new 内部类的实例对象
		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					outputer.output("zhoukeke");
				}
			}
		}).start();

		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					outputer.output("zhangsan");
				}
			}
		}).start();
	}

	class Outputer {
		public void output(String name) {
			int len = name.length();
			synchronized (this) {// 找任意一个对象就行 这里的对象用name 就不行 使用name 达不到同步的效果
									// 必须是同一个对象
									// 使用xxx 对象 就没有奇迹啦
				for (int i = 0; i < len; i++) {
					System.out.print(name.charAt(i));
				}
				System.out.println();
			}
		}

		// 方法上使用synchronized 如果方法中再使用synchronized 极有可能出现死锁
		// 方法中的synchronized 使用的锁就是this 对象
		public synchronized void output2(String name) {
			int len = name.length();

			for (int i = 0; i < len; i++) {
				System.out.print(name.charAt(i));
			}
			System.out.println();

		}
	}
}

以下是错误范例output3 方法要相同步


package com.zkk;

public class Test1 {
	public static void main(String[] args) {
		new Test1().init();
	}

	private void init() {
		final Outputer outputer = new Outputer();// 静态方法中不能new 内部类的实例对象
		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					outputer.output("zhoukeke");
				}
			}
		}).start();

		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					outputer.output("zhangsan");
				}
			}
		}).start();
	}

	static class Outputer {// 加static 就相当于外部类
		public void output(String name) {
			int len = name.length();
			synchronized (this) {// 找任意一个对象就行 这里的对象用name 就不行 使用name 达不到同步的效果
									// 必须是同一个对象
									// 使用xxx 对象 就没有奇迹啦
				for (int i = 0; i < len; i++) {
					System.out.print(name.charAt(i));
				}
				System.out.println();
			}
		}

		// 方法上使用synchronized 如果方法中再使用synchronized 极有可能出现死锁
		// 方法中的synchronized 使用的锁就是this 对象
		public synchronized void output2(String name) {
			int len = name.length();

			for (int i = 0; i < len; i++) {
				System.out.print(name.charAt(i));
			}
			System.out.println();

		}

		public static synchronized void output3(String name) {
			int len = name.length();

			for (int i = 0; i < len; i++) {
				System.out.print(name.charAt(i));
			}
			System.out.println();

		}
	}
}

output3,和output2   output3 和output1 要想同步,要使用clazz 对象

package com.zkk;

public class Test1 {
	public static void main(String[] args) {
		new Test1().init();
	}

	private void init() {
		final Outputer outputer = new Outputer();// 静态方法中不能new 内部类的实例对象
		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					outputer.output("zhoukeke");
				}
			}
		}).start();

		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					outputer.output("zhangsan");
				}
			}
		}).start();
	}

	static class Outputer {// 加static 就相当于外部类
		public void output(String name) {
			int len = name.length();
			synchronized (Outputer.class) {// 找任意一个对象就行 这里的对象用name 就不行 使用name 达不到同步的效果
									// 必须是同一个对象
									// 使用xxx 对象 就没有奇迹啦
				for (int i = 0; i < len; i++) {
					System.out.print(name.charAt(i));
				}
				System.out.println();
			}
		}

		// 方法上使用synchronized 如果方法中再使用synchronized 极有可能出现死锁
		// 方法中的synchronized 使用的锁就是this 对象
		public synchronized void output2(String name) {
			int len = name.length();

			for (int i = 0; i < len; i++) {
				System.out.print(name.charAt(i));
			}
			System.out.println();

		}

		public static synchronized void output3(String name) {
			int len = name.length();

			for (int i = 0; i < len; i++) {
				System.out.print(name.charAt(i));
			}
			System.out.println();

		}
	}
}

接下来是线程同步的问题 面试题 线程相关的

子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着再回到主线程又循环100,如此循环50次,请写出程序


分析:

1,子线程和主线程一定要互斥 (我做的时候不打扰你,你做的时候不打扰我)


package com.zkk;

//通信
public class Test2 {
	public static void main(String[] args) {
		// 创建两个线程 一个循环100 次,一个循环10 次
		new Thread(new Runnable() {

			@Override
			public void run() {
				for (int i = 1; i <= 50; i++) {
					synchronized (Test2.class) {
						for (int j = 1; j <= 10; j++) {
							System.out.println("sub thread sequece of " + j
									+ ",loop of " + i);
						}
					}
				}
			}
		}).start();

		for (int i = 1; i <= 50; i++) {
			synchronized (Test2.class) {
				for (int j = 1; j <= 10; j++) {
					System.out.println("main thread sequece of " + j
							+ ",loop of " + i);
				}
			}
		}
	}
}

但是以上并不符合要求,没有分组 这个时候,应该怎么办?

 首先,明确面向对象的思想

 servelet 生成cookie ,要对密码进行加密,fileter 解密的时候, 要对cookie 进行解密,但是加密和解密的方法不适合写在他们的身上

应该自定义一个类,有加密和解密的方法 高内聚,低耦合 他们的代码要同步,就叫做有关联 代码如下:

package com.zkk;

//通信
public class Test2 {
	public static void main(String[] args) {
		final Business business = new Business();
		// 创建两个线程 一个循环100 次,一个循环10 次
		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 {
	public synchronized void sub(int i) {
		for (int j = 1; j <= 10; j++) {
			System.out.println("sub thread sequece of " + j + ",loop of " + i);
		}
	}

	public synchronized void main(int i) {
		for (int j = 1; j <= 100; j++) {
			System.out.println("main thread sequece of " + j + ",loop of " + i);
		}
	}
}

但是此时,打印的结果还是不如符合要求 没有你来我往,没有和谐发展 ,相互之间没有协调,没有通信,相互之间是互斥的做到了。接下来就是通信的问题啦

值得一提的是,如果不用面向对象的方法来做,就会很糟糕,很混乱,好的思想很重要,设计的巧妙,做起来就事半功倍了。

所以要牢牢记住一个原则,经验:要用到共同数据(包括同步锁)或共同的算法,的若干个方法应该归在同一个类身上,这种设计正好提醒了高类聚和程序的健壮性。

接下来就是通信了。


package com.zkk;

//通信
public class Test2 {
	public static void main(String[] args) {
		final Business business = new Business();
		// 创建两个线程 一个循环100 次,一个循环10 次
		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) {
		if (!bShouldSub) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		for (int j = 1; j <= 10; j++) {
			System.out.println("sub thread sequece of " + j + ",loop of " + i);
		}
		bShouldSub = false;
	}

	public synchronized void main(int i) {
		if (bShouldSub) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		for (int j = 1; j <= 100; j++) {
			System.out.println("main thread sequece of " + j + ",loop of " + i);
		}
		bShouldSub = true;
	}
}

以上代码还是有问题的,一般情况下是没有问题的,在等着就不好了,要唤醒 下面的代码:

package com.zkk;

//通信
public class Test2 {
	public static void main(String[] args) {
		final Business business = new Business();
		// 创建两个线程 一个循环100 次,一个循环10 次
		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) {
		if (!bShouldSub) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		for (int j = 1; j <= 10; j++) {
			System.out.println("sub thread sequece of " + j + ",loop of " + i);
		}
		bShouldSub = false;
		this.notify();
	}

	public synchronized void main(int i) {
		if (bShouldSub) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		for (int j = 1; j <= 100; j++) {
			System.out.println("main thread sequece of " + j + ",loop of " + i);
		}
		bShouldSub = true;
		this.notify();
	}
}

这样基本就做完了,记住一个原则,自己管理自己的状态,不要让别人插手

锁是上在代表要操作的资源内部方法中,而不是线程代码中!

而以上代码使用whiel 代替if 程序更加的健壮 最终代码如下

package com.zkk;

//通信
public class Test2 {
	public static void main(String[] args) {
		final Business business = new Business();
		// 创建两个线程 一个循环100 次,一个循环10 次
		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 sequece of " + j + ",loop of " + i);
		}
		bShouldSub = false;
		this.notify();
	}

	public synchronized void main(int i) {
		while (bShouldSub) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		for (int j = 1; j <= 100; j++) {
			System.out.println("main thread sequece of " + j + ",loop of " + i);
		}
		bShouldSub = true;
		this.notify();
	}
}

文档


synchronized 使用什么对象,wait 就使用什么对象,要不然就会报异常,并且,wait 在synchronized 里面的。

为什么使用while,在有些情况下,他会被假唤醒,(没有被通知的时候唤醒,  spurious wake up 伪唤醒) 噩梦惊醒 使用while 就能防止这种情况







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值