同步函数:
只有当同步代码块和同步函数封装的内容是一样的时候,才可以直接将同步关键字作为修饰符修饰函数即可。
这样函数就具备了同步性。
这就是同步函数。同步的另一种表现形式。
这种表现形式较为简单。
同步函数使用锁是this 。是调用同步函数的对象。
同步函数和同步代码块有什么区别呢?
1,同步函数比同步代码块写法简单。
2,同步函数使用的锁是this。同步代码块使用的锁是任意指定的对象。
建议开发时,使用同步代码块。尤其是需要不同锁时。
静态同步函数使用的锁是什么?
静态随着类的加载而加载的,这时内存中存储的对象至少有一个
就是该类字节码文件对象。
这个对象的表示方式 类名.class 它就表示字节码文件对象。
饿汉式。
class Single
{
private static final Single s = new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}
懒汉式
在被多线程并发访问时,会出现多线程安全问题。
为了解决,加入了同步,虽然安全问题解决了,
但是性能降低了。
有没有办法将安全问题解决的同时,还可以提高性能。
解决改进方法:改成同步代码块。
可以通过双重判断的形式来完成这个过程。
class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance()
{
if(s==null)// -->0 -->1
{
synchronized(Single.class)
{
if(s==null)// -->0 -->1
s = new Single();
}
}
return s;
}
}
同步的另一个弊端:死锁。
最常见的死锁情况:同步嵌套。
同步中还有同步,两个同步用的不是一个锁。
记住尽量避免同步嵌套的情况。
在0和1之间不断变换
if(x==1){}
else{}
x=(x+1)%2;
线程间通信:多个线程在处理同一个资源。 但是处理的动作(线程的任务)却不相同。
等待唤醒机制。
涉及到的方法:
wait():等待,将正在执行的线程释放其执行资格和执行权,并存储到线程池中。
notify():唤醒,唤醒线程池中被wait的线程,一次唤醒一个,而且是任意的。
notifyAll():唤醒全部,可以将线程池中的所有wait线程都唤醒,
唤醒的意思就是让线程池中的线程具备执行资格。
这些方法都要使用在同步中才有效。
这些方法在使用时必须标明所属锁,这样才可以明确出这些放操作的到底是哪个锁上的线程。
为什么这些操作线程的方法定义在Object类中?
因为这些方法在使用时,必须要标明所属的锁,而锁又可以是任意对象。
能被任意对象调用的方法一定定义在Object类中。
代码演示
synchronized(r)
{
if(r.flag){
try{r.wait();}
catch(InterruptedException e){}
}
if(x==0)
{
r.name = "Mike";
r.sex = "man";
}
else
{
r.name = "小红";
r.sex = "女";
}
r.flag = true;
r.notify();
}
synchronized(r)
{
if(!r.flag)
{
try{r.wait();}
catch(InterruptedException e){}
}
System.out.println(r.name+"....."+r.sex);
r.flag = false;
r.notify();
}
多生产者多消费者问题。
发生的问题:
生产者生产的商品没有被消费就生产了新的商品。
问题在于两点:
1,本方唤醒了本方。
2,被唤醒的本方没有判断标记。只要将if判断该外while判断。
将if改为while循环判断标记后,出现了死锁。
因为本方唤醒了本方,而被唤醒的本方一判断标记,就继续等待。这样所有的线程都等待了。
必须唤醒对方才行,但是没有直接唤醒对方的动作,所以就使用了notifyAll,唤醒全部。
这个程序有一个bug。就是每次notifyAll。都会唤醒本方。
可不可以只唤醒对方呢?
JDK1.5版本提供了一些新的对象,优化了等待唤醒机制。
1,将synchronized 替换成了Lock接口。
将隐式锁,升级成了显示锁。
Lock
获取锁:lock();
释放锁:unlock();注意:释放的动作一定要执行,所以通常定义在finally中。
获取Condition对象:newCondition();
2,将Object中的wait,notify,notifyAll方法都替换成了Condition的await,signal,signalAll。
和以前不同是:一个同步代码块具备一个锁,该所以具备自己的独立wait和notify方法。
现在是将wait,notify等方法,封装进一个特有的对象Condition,而一个Lock锁上可以有多个Condition对象。
Lock lock = new ReentrantLock();
Condition conA = lock.newCondition();
Condition conB = lock.newCondition();
con.await();//生产,,消费
con.signal();生产
set()
{
if(flag)
conA.await();//生产者,
code......;
flag = true;
conB.signal();
}
out()
{
if(!flag)
conB.await();//消费者
code....;
flag = false;
conA.signal();
}
wait和sleep的区别:
wait:释放cpu执行权,释放同步中锁。
sleep:释放cpu执行权,不释放同步中锁。
synchronized(锁)
{
wait();
}
停止线程:
stop过时。
原理:run方法结束。run方法中通常定义循环,指定控制住循环线程即可结束。
1,定义结束标记。
2,当线程处于了冻结状态,没有执行标记,程序一样无法结束。
这时可以循环,正常退出冻结状态,或者强制结束冻结状态。
强制结束冻结状态:interrupt();目的是线程强制从冻结状态恢复到运行状态。
但是会发生InterruptedException异常。