------- android培训、java培训、期待与您交流! ----------
第五部分:死锁
死锁的出现是因为同步中出现了同步,而且用的是不同的锁。
代码示例:
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag = flag;
}
public void run()
{
if(flag)
{
//同步中嵌套了同步,locka和lockb的互相嵌套导致了死锁问题
synchronized(MyLock.locka)
{
s.o.p("if locka");
synchronized(MyLock.lockb)
{
s.o.p("if lockb");
}
}
}
else
synchronized(MyLock.lockb)
{
s.o.p("else lockb");
synchronized(MyLock.locka)
{
s.o.p("else locka");
}
}
}
}
class MyLock
{
static Object locka = new Object();
static Object lockb = new Object();
}
class
{
public static void main(String[] args)
{
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
//try{Thread.sleep(10);}catch (Exception e){};//这句的作用是切换作用
//t.flag = false;
t2.start();
}
}
第六部分:线程通信和等待唤醒机制
1、线程通信:多个线程在对处理统一资源,但是处理任务不同(如存入和取出),这时就需要线程通信。
2、等待\唤醒机制的方法:
wait():让线程处于冻结状态,被wait的线程会被存储到线程池中。
notify():唤醒线程池中的一个线程(任何一个都有可能)。
notifyAll():唤醒线程池中的所有线程。
PS
a、这些方法都要使用在同步中,因为要对持有监视器(锁)线程进行操作。
b、必须要明确到底操作的是哪个锁上的线程。
c、wait和sleep区别?
1)wait可以指定时间也可以不指定。sleep必须指定时间。
2)在同步中时,对CPU的执行权和锁的处理不同。
wait:释放执行权,释放锁。
sleep:释放执行权,不释放锁。
d、为什么操作线程的方法wait、notify、notifyAll定义在了object类中?
因为这些方法在操作同步中的线程时,都必须要表示他们所操作线程只有的锁,只有同一个锁上的被等待线程,可以被锁上notify唤醒。
不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一把锁。锁可以是任意的对象,任意的对象调用的方式一定在object类中。
3、需求:一个线程存入名字和性别,另一个线程打印存入的名字和线程
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(Exception e){}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
public synchronized void out()
{
if(!flag)
try{this.wait();}catch(Exception e){}
s.o.p(name+" "+sex);
flag = true;
this.notify();
}
}
class Input implements Runnable;
{
private Res r;
Input(Res r)
{
this.r = r;
}
public void run()
{
int x = 0;
while(true)
{
synchronized(r)
{
if(r.flag)
try{r.wait();}catch(Exception e){}
if(x==0)
r.set("mike","man");
else
r.set("lili","girl");
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
{
public static void main(String[] args)
{
Res r = new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
第七部分:生产者和消费者问题
思路:
1、使用if判断标志会导致不该运行的线程运行了,所以应该用while来判断标志,线程获取CPU执行权及锁后,将重新判断是否具备运行条件。
2、notify方法只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。notifyAll解决了本方线程一定会唤醒对方线程的问题。
代码示例:
public class ProConDemo {
public static void main(String[] args) throws Exception{
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 =new Thread(pro);
Thread t2 =new Thread(pro);
Thread t3 =new Thread(con);
Thread t4 =new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
System.out.println("Hello World!");
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
res.out();
}
}
}
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
res.set("+商品+");
}
}
}
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
//在同步函数内使用wait方法,notifyAll方法。
public synchronized void set(String name)
{
while(flag)//修改为while(flag)
try{wait();}catch(Exception e){}
this.name = name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"..生产者"+this.name);
flag = true;
this.notifyAll();//修改为this.notifyAll()
}
//在同步函数内使用wait方法,notifyAll方法。
public synchronized void out()
{
while(!flag)//修改为while(flag)
try{wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"..............消费者"+this.name);
flag = false;
this.notifyAll();//修改为this.notifyAll()
}
}
第八部分:使用JDK1.5以后的新特性方法
1、背景:同步代码快对于锁来说是隐式操作。JDK1.5以后将同步和锁封装成了对象,并且将操作锁的隐式方式定义到了该对象中,将隐式动作变成了显示动作。
2、使用的方法和接口:
Lock接口:出现替代了同步代码块或者同步函数,将同步的隐式操作变成显示锁操作。同时更为灵活,可以一个锁上加上多组监视器。
lock():获取锁。
unlock():释放锁,为了防止异常出现,导致锁无法被关闭,所以锁的关闭动作要放在finally中。
Condition接口:出现替代了Object中的wait、notify、notifyAll方法。将这些监视器方法单独进行了封装,变成Condition监视器对象,可以任意锁进行组合。
Condition接口中的await方法对应于Object中的wait方法。
Condition接口中的signal方法对应于Object中的notify方法。
Condition接口中的signalAll方法对应于Object中的notifyAll方法。
3、还是以生产者和消费者为例子:
package cn.itcast.demo;
import java.util.concurrent.locks.*;
public class NewProConDemo {
public static void main(String[] args) {
Resource1 r = new Resource1();
Producer1 p = new Producer1(r);
Consumer1 c = new Consumer1(r);
Thread t1 = new Thread(p);
Thread t2 = new Thread(p);
Thread t3 = new Thread(c);
Thread t4 = new Thread(c);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resource1
{
private String name ;
private int count = 1;
private boolean flag = false;
//创建一个锁对象
private Lock lock = new ReentrantLock();
//通过已有的锁获取该锁上的监视器对象
private Condition condition_pro = lock.newCondition();
private Condition condition_con = lock.newCondition();
public void set(String name)throws InterruptedException
{
//新特性
/*
* 需要注意的地方,唤醒消费者用condition_con.signalAll();如果使用condition_pro.signalAll();是唤醒消费者。
* 在一个同步中要交替使用。
* */
lock.lock();
try
{
while(flag)
condition_pro.await();
this.name = name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者"+this.name);
flag = true;
condition_con.signalAll();
}
finally
{
//一定要关闭资源,所以在finally中使用解锁
lock.unlock();
}
}
public void out() throws InterruptedException{
lock.lock();
try{
while(!flag)
condition_con.await();
System.out.println(Thread.currentThread().getName()+"---消费者"+this.name);
flag = false;
condition_pro.signalAll();
}
finally{
//一定要关闭资源,所以在finally中使用解锁
lock.unlock();
}
}
}
class Producer1 implements Runnable{
private Resource1 res;
Producer1(Resource1 res)
{
this.res = res;
}
public void run()
{
while(true)
{
try{res.set("+商品+");}catch(Exception e){}
}
}
}
class Consumer1 implements Runnable{
private Resource1 res;
Consumer1(Resource1 res)
{
this.res = res;
}
public void run()
{
while(true)
{
try{res.out();}catch(Exception e){}
}
}
}
第九部分:停止线程(冻结)和强制恢复线程
stop方法已经过时。
怎么控制线程的任务结束呢?
任务中都会有循环结构,只要控制住循环就可以结束任务。
控制循环通常就用定义标记来完成。
在while循环中通常要判断标志flag。
public void run()
{
while(true)
{
s.o.p(Thread.currentThread().getName()+"...run");
}
}
这是如果定义一个方法,满足条件后改变判断标志。
public void changeFlag()
{
flag = false;
}
但是如果线程处于了冻结状态,无法读取标记,如何结束呢?可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备CPU的执行资格。
强制动作会发生InterruptedException,一定要记得处理。
第十部分:线程的其他方法
1、守护线程:
使用setDaemon方法将线程设置为守护线程,该方法必须在启动线程前调用。
代码示例:
t1.setDaemon(true);
t2.setDaemon(true);
t1.start();
t2.start();
2、join方法
当A线程执行到了B线程的join方法时,A就会等待。等B线程都执行完,A才会执行。join可以用来临时加入线程执行
代码示例:
t1.start();
t1.join();//抢夺cpu执行权
t2.start();
//如果将t1.join();放在这儿,则是t1和t2抢夺cpu抢夺执行权