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 就能防止这种情况