当多个线程同时操作同一个对象会出现线程安全问题
1.使用synchronized代码块及其原理
2.使用synchronized方法
3.分析静态方法所使用的同步监视器对象是什么?
4.wait与notify实现线程间的通信
--》用面试宝典中的子线程循环10次和主线程循环5次,两种交替运行
50次的例子进行讲解。(要用到共同数据包括同步锁的若干个方法应该
归在同一个类身上,这种设计正好体现了高类聚和程序的健壮性。)
下面用代码的形式讲解上面的知识点:
package thread;
public class SynchronizedTest {
private void init() {
final Outputer outputer =new Outputer();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("xiaoming");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("yeyongli");
}
}
}).start();
}
public static void main(String[] args) {
new SynchronizedTest().init();
}
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();
}
}
}
上面代码很简单开了两个线程,分别打印xiaoming和yeyongli
运行结果:
上面出现了一个奇迹yeyonglixiaoming,它没把yeyongli执行完,cpu就切换到xiaoming的线程上去了,虽然这种是小概率事件,但是我们也要处理。
我们只要实现output的原子性,当A执行打印代码的时候,其他的线程就在外面等着,等我执行完才能进来,这就是排它性,
只要修改打印的类如下:
class Outputer{
String xxx ="";
public void output(String name){
int len=name.length() ;
synchronized (xxx) {
for (int i=0; i<len;i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
把要运行的代码用synchronized包起来,就能实现同步,不管是A线程还是B线程来调用这个方法都是用一个XXX就好比是同一把锁,程序运行不敢保证没有问题,但是我敢保证你找不出错误来。xxx变量其实可以写成this,this只被调用的对象,他们调用的都是同一个对象
package thread;
public class SynchronizedTest {
private void init() {
final Outputer outputer =new Outputer();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("xiaoming");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output2("yeyongli");
}
}
}).start();
}
public static void main(String[] args) {
new SynchronizedTest().init();
}
class Outputer{
String xxx ="";
public void output(String name){
int len=name.length() ;
synchronized (this) {
for (int i=0; i<len;i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
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();
}
}
}
锁还能加在方法上实现的效果和代码里是相同的,现在做了改进,第一个线程调用的output方法,
第二个线程是调用output2方法,这两个线程能实现互斥吗?
答案是能实现的,因为他们用的都是同一个监视器对象,所以他们有互斥效果。
package thread;
public class SynchronizedTest {
private void init() {
final Outputer outputer =new Outputer();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("xiaoming");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output3("yeyongli");
}
}
}).start();
}
public static void main(String[] args) {
new SynchronizedTest().init();
}
static class Outputer{
String xxx ="";
public void output(String name){
int len=name.length() ;
synchronized (this) {
for (int i=0; i<len;i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
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();
}
}
}
运行结果:
运行结果不同步,如何让output和output3同步呢?
我们只要把output3的this改成Output.class就行
因为output3是静态方法,它运行的时候肯定对应一个锁对象,
静态方法运行的时候不会创建一份类的对象,对应的对象就是字节码对象,
字节码对象也是不用创建的,所以两个线程就可以同步了。
以下是最后一个面试题的代码:
import java.util.concurrent.atomic.AtomicInteger;
public class TraditionalThreadCommunication {
/**
* @param args
*/
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) {
// TODO Auto-generated catch block
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(bShouldSub){
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for(int j=1;j<=100;j++){
System.out.println("main thread sequence of " + j + ",loop of " + i);
}
bShouldSub = true;
this.notify();
}
}