03. 传统线程互斥技术
线程安全问题例子:银行转账
同一个账户一边进行出账操作(自己交学费),另一边进行入账操作(别人给自己付款),线程不同步带来的安全问题
示例:逐个字符的方式打印字符串
class Outputer
{
public void output(String name)
{
int len =name.length();
for (int i=0; i<len; i++)
SOP(name.charAt(i));逐个字符打印
SOP();换行
}
}
public void test()
{
Outputeroutputer = new Outputer();
newThread(
new Runnable()
{
public void run()
{
Thread.sleep(100);
outputer.output(“zhangxiaoxiang”);
}
}).start();
newThread(
new Runnable()
{
public void run()
{
Thread.sleep(100);
outputer.output(“lihuoming”);
}
}).start();
}
注意:
内部类不能访问局部变量,要访问需加final
静态方法中不能创建内部类的实例对象
打印结果发现的问题:线程不同步所致,两个线程都在使用同一个对象
要避免下边产生的问题,左边方法体中的代码要实现原子性
有一个线程正在使用这个方法的代码,别的线程就不能再使用。
就和厕所里的坑一样,已经有人在用了,别人就不能再去用了。
Java中某段代码要实现排他性,就将这段代码用synchronized关键字保护起来。
同步锁可以用任意对象,相当于门锁
synchronized(name)
{
for (int i=0; i<len; i++)
SOP(name.charAt(i));逐个字符打印
SOP();换行
}
这样的话,有一个线程进入保护区域后,没出来的话,别的线程就不能进入保护区域。
互斥方法:
a、同步代码块
synchronized(lock){}
b、同步方法
方法返回值前加synchronized
同步方法上边用的锁就是this对象
静态同步方法使用的锁是该方法所在的class文件对象
使用synchronized关键字实现互斥,要保证同步的地方使用的是同一个锁对象
public synchronized void output(String name)
{
int len =name.length();
这里就不要再加同步了,加上极易出现死锁
for (int i=0; i<len; i++)
SOP(name.charAt(i));逐个字符打印
SOP();换行
}
04. 传统线程同步通信技术
面试题,子线程10次与主线程100次来回循环执行50次
下面是我刚看完面试题就暂停视频自己试着写的代码,还可以,结果完成要求了
在单次循环结束后让这个刚结束循环的线程休眠,保证另一个线程可以抢到执行权。
public class ThreadInterViewTest
{
/**
* 刚看到面试题没看答案之前试写
* 子线程循环10次,回主线程循环100次,
* 再到子线程循环10次,再回主线程循环100次
* 如此循环50次
*/
publicstatic void main(String[] args)
{
intnum = 0;
while(num++<50)
{
newThread(new Runnable()
{
@Override
public void run()
{
circle("子线程运行", 10);
}
}).start();
try
{
//加这句是保证上边的子线程先运行,刚开始没加,主线程就先开了
Thread.sleep(2000);
}catch (InterruptedException e)
{
e.printStackTrace();
}
circle("主线程", 100);
}
}
publicstatic synchronized void circle(String name, int count)
{
for(int i=1; i<=count; i++)
{
System.out.println(name+"::"+i);
}
try
{
Thread.sleep(5000);
}catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
张老师讲的方法:
1、将子线程和主线程中要同步的方法进行封装,加上同步关键字实现同步
2、两个线程间隔运行,添加一个标记变量进行比较以实现相互通信,加色的部分
wait notify notifyAll wait会抛出异常
class Business
{
private boolean bShouleSub = true;
publicsynchronized void sub()
{
if (bShouleSub)
{
for (int i=1; i<11; i++)
SOP(sub+i);
bShouldSub= false;
this.notify();
}
else
this.wait();
}
publicsynchronized void main()
{
if (!bShouldSub)
{
for (int i=1; i<101; i++)
SOP(main+i);
bShouldSub= true;
this.notify();
}
else
this.wait();
}
}
经验:要用到共同数据(包括同步锁)或相同算法的多个方法要封装在一个类中
锁是上在代表要操作的资源类的内部方法中的,而不是上在线程代码中的。这样写出来的类就是天然同步的,只要使用的是同一个new出来的对象,那么这个对象就具有同步互斥特性
判断唤醒等待标记时使用while增加程序健壮性,防止伪唤醒
此处使用while以增加程序健壮性,因为存在虚假唤醒,有时候并没有被notify就醒了。如果该方法没有同步的话,此处就更要使用while进行判断了,避免进程不同步问题