同步的好处:
解决了多线程的安全问题。
同步的弊端:
1,相对消耗资源。
2,容易出现死锁。(当这个同步嵌套的时候容易出现,大家在开发时,尽量避免。)
为了面试:
//定义锁类。
class MyLock
{
static MyLock locka = new MyLock();
static MyLock lockb = new MyLock();
}
class Demo implements Runnable
{
private boolean flag ;
Demo(boolean flag)
{
this.flag = flag;
}
public void run()
{
if(flag)
{
while(true)
{
synchronized(MyLock.locka)
{
System.out.println("if......locka");
synchronized(MyLock.lockb)
{
System.out.println("if......lockb");
}
}
}
}
else
{
while(true)
{
synchronized(MyLock.lockb)
{
System.out.println("else......lockb");
synchronized(MyLock.locka)
{
System.out.println("else......locka");
}
}
}
}
}
}
class DeadLockDemo
{
public static void main(String[] args)
{
Demo d1 = new Demo(true);
Demo d2 = new Demo(false);
new Thread(d1).start();
new Thread(d2).start();
}
}
-----------------------------------
线程间通信。
多个线程在处理同一个数据资源,但是处理的动作却不一致。
这里就用到一个机制 等待/唤醒 机制。
等待和唤醒:
wait():让线程处于等待状态。这时线程会释放锁。并存入到了线程池中。
notify():通常唤醒线程池中的第一个。
notifyAll():将线程池中的所有等待线程都唤醒。
这三个方法都需要定义在同步当中。因为要标识出它们所作用的锁。
如果用在同步外,有可能发生 无效线程状态异常。
三个方法为什么定义在Object类中?因为锁可以任意对象,被任意对象都能调用的方法肯定定义在Object类。
//需求:两个线程对同一个资源进行操作,一个赋值姓名和性别,一个线程负责打印姓名和性别。
//1,定义资源。
class Res
{
private String name;
private String sex;
private boolean flag = false;
public synchronized void set(String name,String sex)
{
if(flag)
try{this.wait();}catch(InterruptedException e){}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
public synchronized void out()
{
if(!flag)
try{this.wait();}catch(InterruptedException e){}
System.out.println(name+"..."+sex);
flag = false;
this.notify();
}
}
class Input implements Runnable
{
private Res r;
Input(Res r)
{
this.r = r;
}
public void run()
{
int x = 0;
while(true)
{
if(x==0)
{
r.set("mike","nan");
}
else
r.set("丽丽","女女");
x = (x+1)%2;
}
}
}
class Output implements Runnable
{
private Res r;
Output(Res r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
class ResDemo
{
public static void main(String[] args)
{
Res r = new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
wait()和sleep()的特点:
wait:可以有时间限定,也可以没有时间限定。
sleep:必须有时间限定。
wait:释放cpu执行资源,也释放锁。
sleep:释放cpu执行资源,不释放锁。
----------------------------------------
线程间通信的演化。
需求:
当多个线程进行通信时,一部分线程在进行存储动作,一部分线程在进行取出动作。
无论多少个线程进行存储,只有可以可以执行,无论多少个线程进行取出,只能取出一次。
class Res
{
private String name;
private boolean flag;
private int count = 0;
public synchronized void set(String name)throws InterruptedException
{
while(flag)
wait();
this.name = name;
add();
System.out.println(Thread.currentThread().getName()+"----生产----"+name+"::"+count);
flag = true;
notifyAll();
}
public synchronized void out()throws InterruptedException
{
while(!flag)
wait();
System.out.println(Thread.currentThread().getName()+"....消费...."+name+"::"+count);
flag = false;
notifyAll();
}
public void add()
{
count++;
}
}
class Input implements Runnable
{
private Res r;
Input(Res r)
{
this.r = r;
}
public void run()
{
while(true)
{
try{r.set("商品");}catch(InterruptedException e){}
}
}
}
class Output implements Runnable
{
private Res r;
Output(Res r)
{
this.r = r;
}
public void run()
{
while(true)
{
try{r.out();}catch(InterruptedException e){}
}
}
}
class ResDemo
{
public static void main(String[] args)
{
Res r = new Res();
//创建两个生成者
new Thread(new Input(r)).start();
new Thread(new Input(r)).start();
//创建两个消费者
new Thread(new Output(r)).start();
new Thread(new Output(r)).start();
}
}
发现结果不爽!要么就生产不断的生产,却没有被消费,要么就是消费者不断的消费,却没有生产。
当然在某一时段,也会出现和谐情况。多运行就会不和谐。
发生的原因的是:当线程等待时。再次被唤醒,不会在去判断标记flag。
那么就会发生数据错乱的情况。
解决办法:一定要醒来的线程再一次判断标记。所以要将判断的标记的if改成while。
这时就将if(flag)改成while(flag).
该完以后,发现,郁闷了,线程和不和谐不重要,程序虽然没有结束,但是生产者消费者都不再运行,
因为都处于wait状态。
原因:是出现循环判断后,线程每一次都判断标记,都会出现等待的情况,
例如:3个线程等待,第四个线程,唤醒了其中一个,而这一个也是和该线程属于同一方的。
解决:希望可以唤醒另一放的线程。比如:消费需要去唤醒生产的。
而在Object提供的监视器方法中只提供了notify,和notifyAll进行唤醒。
所以这时可以使用notifyAll,将所有等待的线程都唤醒,这些线程中就包括对方线程。
就需要将代码中的notify改成notifyAll。
|
|