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();
}
}