您的位置:首页 > 其它

3.线程的同步互斥与通信

2017-03-22 10:33 197 查看
一、使用synchronized代码块及其原理

二、使用synchronized方法

三、分析静态方法所使用的同步监视器对象是什么?

/**
*	多线程应用:两个线程对同一个对象(数据)进行操作,就会出现线程安全的问题
*	线程问题:银行转账
*
*	总结:互斥的关键字是 synchronized,必须用同一把锁挡住线程,否则线程不同步。
*/
public class TraditionalThreadSynchronized1 {
public static void main(String[] args) {
new TraditionalThreadSynchronized1().init();
}
private void init(){
/**
* 内部类不能访问局部变量,为了能够访问局部变量要加final
* 在静态方法中不能 new 内部类的实例对象,必须先创建外部类的实例对象,才能创建内部类的实例对象
*/
final Output outputer = new Output();
//线程A
new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("zhangxiaoxiang");
}
}
}).start();
//线程B
new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* new Output().output("lihuoming");
* 不能传入上面的这种形式,因为锁必须传入同一个对象
*/
outputer.output2("lihuoming");
}
}
}).start();
//线程C
new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* new Output().output("lihuoming");
* 不能传入上面的这种形式,因为锁必须传入同一个对象
*/
outputer.output3("lihuoming");
}
}
}).start();
}
//内部类,加了 static 就相当于外部类
static class Output{
/**
* 因为A线程用的outputer对象,B线程用的outputer对象,
* 所以可以在Output类里面定义一个变量传入到synchronized中
* synchronized(xxx){}
*/
//String xxx = "";
//把传入的字符串的字符一个一个的打印在屏幕上
public void output(String name){
int len =  name.length();
/**
* synchronized(name)
* 不能传入name,因为达不到同步的效果(A线程传入的是zhagnxiaoxiang,B传入的是lihuoming),必须要传入同一个对象
*/
synchronized(Output.class){
//这段代码要实现原子性,才能保证线程同步,即排他性原理
for(int i=0;i<len;i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
/**
* ①保护方法的全部代码就在方法上加关键字 synchronized,一个方法上最好用一个
* synchronized,要不然极易出现死锁。
* ②方法上的synchronized就是用的 this对象,所以线程A调用output,线程B
* 调用output2,它们用的是同一个监视器对象(同一个门栓),所以有互斥效果。
*/
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和output同步?
* 	   因为output3是一个静态方法,静态方法要同步,必须要用到锁对象,静态方法运行的时候
* 不用创建类的实例对象,但是静态方法的字节码对象已经在内存里面了,所以在output中传入
* Output.class,就可以实现同步。
*
* 重点:静态方法只与字节码对象关联
*/
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();
}
}
}
四、wait与notify实现线程间的通信
/**
* 问题:
* 	子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,
*	接着再回到主线程又循环100,如此循环50次,请写出程序。
*/
public class TraditionalThreadCommunication {
public static void main(String[] args) {
final Business business = new Business();
new Thread(new Runnable() {
public void run() {
for (int i = 1; i <= 50; i++) {
business.sub(i);
}
}
}).start();

for (int i = 1; i <= 50; i++) {
business.main(i);
}
}
}
/**
* 经验:①要用到的共同数据(包括同步锁)或共同算法的若干个方法应该归在同一个类身上,
*       这种设计正好体现了高类聚和程序的健壮性。
*      ②锁是上在代表要操作的资源的类的方法中,而不是线程代码中。
*      Business业务类(对象),其有两个方法,即主方法和子方法。锁是上在具体资源 (具体干事的)对象的身上,
*      线程只是拿着Business对象去运行,所以互斥的问题不是写在线程上的,而是写在线程锁要访问的资源的内部上的,
*      这样的好处可以是可以插拔式的运行。
*/
class Business {
private boolean bShouldSub = true;
//子线程
public synchronized void sub(int i) {
/**
* 如果是主线程,就等待
* while防止伪唤醒,且比if要好
*/
while (!bShouldSub) {
try {
//this作为同步监视器对象
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 (bShouldSub) {
try {
//this作为同步监视器对象
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();//唤醒子线程,因为子线程原先是等待的
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: