------- android培训、java培训、期待与您交流! ----------
>>进程是一个正在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
线程是进程中的内容。是进程中真正执行的部分,是进程中得一个独立的控制单元,控制着进程的执行。所以一个进程这种至少有一个线程。
Java虚拟机启动的时候会有一个进程java.exe。该进程中至少有一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。
jvm的启动其实就是多线程程序。其中有一个程序负责从主函数开始执行,并控制程序运行的流程。同时为了提高效率,还启动了另一个控制单元(执行路径)专门负责堆内存中的垃圾回收。在程序正常执行过程中,如果出现了垃圾,这时另一个负责收垃圾的线程会在不定时的时间进行垃圾的处理。这两个程序是同时执行的。
负责垃圾回收代码执行的线程,称为垃圾回收线程,该线程要执行的代码在finalize中。
>>java的lang包中的类Thread可以定义线程。
创建线程的第一种(下面还有第二种)方式:继承Thread
1、定义类继承Thread
2、覆盖Thread类中的run方法。
3、创建Thread类的子类对象创建线程。
4、调用Thread类中的start方法开启线程,并执行子类中的run方法。
特点:当类去描述事物,事物中有属性和行为。如果行为中有部分代码需要被多线程所执行,如果行为中有部分代码需要被多线程所执行,就需要该类继承Thread类,产生该类的对象作为线程对象。可是这样做会导致每一个对象中都存储一份属性数据。无法在多个线程中共享
eg:class Demo extendsThread//定义类继承Thread
{
publicvoid run()//run()方法定义于Thread中,覆盖
{
System.out.println(“Demo run”);
}
}
class ThreadDemo
{
publicstatic void main(String[] args)//主线程是一个控制单元
{
Demod = new Demo();//创建好一个线程,一个控制单元
d.start();//start()方法定义于Thread中,功能是启动线程和调用run()方法
}
}
>>eg:多线程交替执行的情况
class Demo extends Thread
{
publicvoid run()
{
for(int x=0; x<60;x++)
{System.out.println(“Demo run”+x);}
}
}
class ThreadDemo
{
publicstatic void main(String[] args)
{
Demod = new Demo();//Demo线程
d.start();
for(intx=0;x<60;x++)//主线程中执行
{Systme.out.println(“main”+x);}
}
}
执行结果:会出现“mainx”与“Demo runx”交替打印的现象。cpu在运行时切换进程中的线程。当执行到Demo d = new Demo();时,除了主线程,还增加了一个d线程。
>>run();方法用于存储代码,一般需要覆盖,否则调用的是Thread中得run方法,里面是没有东西可以用的。另外,开启线程的目的是为了运行自己的代码,所以覆盖。通过start方法调用run方法。
如果直接调用run方法可以么?不可以,虽然运行正常,但是start的功能是开启线程并执行线程的run方法。run仅仅是对象调用方法。如果直接调用run方法,则线程虽然创建了,但是没有执行,执行run方法的还是主线程。
>>线程对象的获取和名称的定义
线程的编号是从0开始的
1、String getName()//返回该线程的名称。
但是主函数没有继承Thread不能直接使用getName(),而且主线程并非程序员定义的!
通过Thread中一个方法调用:
staticThread currentThread()
返回对当前正在执行的线程对象的引用。
eg:…………main(String[]args)
{
………………
System.out.println(Thread.currentThread().getName()+”--”+x);
………………
}
2、通过Thread构造函数可以给线程命名
eg:class Demo extendsThread
{
Demo(Stringname)
{
super(name);//传给父类的构造方法
}
publicvoid run()//覆盖父类的run方法
{
for(int x=0; x<60;x++)
{System.out.println(“Demo run”+x);}
}
}
3、如果要改名呢?
void setName(String name)//改变线程名称,使之与参数name相同。
eg:………………
Demod = new Demo(“旺财”);
d.setName(“小强”);
………………//线程名为“小强”而不是“旺财”或Thread-0;
>>线程被创建后有运行状态、冻结状态、消亡状态、临时阻塞状态(有资格无权)。
假设有三个线程A、B、C。当三个都start()方法后,叫做三个线程具备了执行资格。处于临时阻塞状态。
当某一个线程A正在被cpu处理,说明A处于运行状态,即具备了资格,也具备了CPU的执行权,而B、C处于临时阻塞状态。
又,当CPU切换到B线程时,B就具备执行权,这时A、C处于临时阻塞状态,只具备执行资格,不具备执行权。
冻结状态:当前线程释放了CPU的执行权,并释放执行资格。冻结状态的结束,表示线程重新具备了执行资格。但是不一定立刻具备执行权。比如:睡眠时间到,或者被wait的线程唤醒。
>>线程的冻结:放弃了执行资格
sleep(time);
wait();//唤醒notify();
线程的消亡stop();//run方法结束
临时阻塞状态:具备运行资格但没有执行权,唤醒时先回到冻结状态
>>Thread的构造函数可以传线程名称
所以Thread的子类可以这样:
ZitTread(String name){super(name);}
>>Thread.currentThread()等于this
>>eg:简单的卖票程序,多个窗口卖票:创建线程的第二种方式:Runnable接口
(
1、定义类实现Runnable接口
2、覆盖Runnable接口中的run方法。
将线程要运行的代码存放在run方法中。
3、通过Thread类建立线程对象。
4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数,因为,自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程去指定对象的run方法,就必须明确该run方法所属对象。
5、调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
)
特点:
1、描述事物的类中封装了属性和行为,如果有部分代码需要被多线程所执行,同时还在操作属性。那么可以通过实现Runnable接口的方式。因为该方式是定义一个Runnable接口的子类对象,可以被多个线程所操作,实现了数据的共享。
2、实现了Runnable接口的好处,避免了单继承的局限性。也就是说,一个类如果已经有了自己的父类是不可以继承Thread类的,但是该类中还有需要被多线程执行的代码。这时就可以通过在该类上功能扩展的形式实现一个Runnable接口。
过程代码部分:
//class Ticket extends Thread
class Ticket implements Runnable
{
//privatestatic int tick = 100;//方法1中定义静态可以防止重复卖票
privateint tick = 100;//tick在堆内存中
publicvoid run()
{
while(true)
{
if(tick>0)
{
System.out.println(currentThread().getName()+"sale:"+tick--);
}
}
}
}
class TicketDemo
{
publicstatic void main(String[] args)
{
//方法4
Tickett = new Ticket();//t是Runnable的实现,不是线程
Threadt1 = new Thread(t);//创建了一个线程,注意里面的t参数
Threadt2 = new Thread(t);//创建了一个线程,注意里面的t参数
Threadt3 = new Thread(t);//创建了一个线程,注意里面的t参数
Threadt4 = new Thread(t);//创建了一个线程,注意里面的t参数
t1.start();
t2.start();
t3.start();
t4.start();
/*方法3
Tickett = new Ticket();
Threadt1 = new Thread();//创建了一个线程
Threadt2 = new Thread();//创建了一个线程
Threadt3 = new Thread();//创建了一个线程
Threadt4 = new Thread();//创建了一个线程
t1.start();
t2.start();
t3.start();
t4.start();
运行的是Thread的start方法,所以什么都没有输出,怎么
才能让Ticket里面的run方法和线程发生关系呢?
解决方案:线程一开始就要明确所执行的代码,所以
Thread中有一个特殊的构造函数:Thread(runnabletarget)。
看方法4
*/
/*方法2
如果票的张数tick不定义静态而这样写:
Tickett1 = new Ticket();
t1.start();
t1.start();
t1.start();
t1.start();//同时跑4个
这样会报无效线程状态异常,start()代表已经从创建状态
到运行状态了,再次开启start()没有意义
解决方案:Runnable接口,其中就一个方法run();
*/
/*方法1
Tickett1 = new Ticket();
Tickett2 = new Ticket();
Tickett3 = new Ticket();
Tickett4 = new Ticket();
t1.start();
t2.start();
t3.start();
t4.start();*/
}
}
>>那么,实现方式和继承方式有什么区别么?
实现方式好处:避免了单继承的局限性。
举个例子,如果Student类继承了Person类,但是Student内又有需要多线程运行的代码,这时Student不能再继承Thread类了,只有通过实现来解决。在定义线程时,建议使用实现方式。
两种方式区别:
继承Thread:线程代码存放Thread子类run方法中。
实现Runnable,线程代码存在接口的子类的run方法。
>>eg: tick=1;
if(tick>0)
{
system………………tick--);
}
情况:系统中,0线程开始运行,刚刚判断完tick>0后,还没输出,冻结;这个时候1线程进来了……判断完tick>0后,还没输出,冻结,换0线程运行……这个时候tick已经为1了,当0线程输出完后,x—令tick为0,。这个时候1线程执行,因为之前已经判断过,所以直接输出,输出0。这样就造成了错误!
tick=1;
if(tick>0)
{
try
{
Thread.sleep(10);//10毫秒
}
catch(Exception e){}
system.out.println(Thread.currentThread().getName()+tick--);
}//可以观察到错误现象
问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
解决方案:同步代码块
synchronized(对象)
{
需要被同步的代码
}
eg:class Ticketimplements Runnable
{
private int tick = 1000;
Object obj = new Object();
public void run()
{
while(true)
{
synchronized(obj)
{
if(tick>0)
{
System.out.println(Thread.currentThread().getName()
+"....sale : "+ tick--);
}
……
>>同步的前提
1、必须要有两个或者两个以上的线程
2、必须是多个线程使用同一个锁
必须保证同步中只有一个线程在运行。
同步能解决多线程的安全问题,但是同时如果有多个线程需要判断锁,则较为消耗资源。
>>如何找安全问题:
1,明确哪些代码是多线程运行代码。
2,明确共享数据。
3,明确多线程运行代码中哪些语句是操作共享数据的。
>>函数封装代码和函数封装代码有什么不同呢?就差别在同步上!
所以,用synchronized修饰函数便可,变成同步函数!
eg:public synchronizedvoid Demo(){}
>>函数需要被对象调用,那么函数都有一个所属对象引用。就是this。
所以同步函数使用的锁是this。
eg:注意用同步的两个前提来理解,不满足前提会不安全,出现0号票的情况
class Ticket implements Runnable
{
privateint tick = 100;
Objectobj = new Object();
booleanflag = true;
publicvoid run()
{
if(flag)
{
while(true)
{
synchronized(this)
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exceptione){}
System.out.println(Thread.currentThread().getName()+
"....code : "+ tick--);
}
}
}
}
else//这个else是if(flag)的
while(true)
show();
}
publicsynchronized void show()//this
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exceptione){}
System.out.println(Thread.currentThread().getName()+"....show....: "+ tick--);
}
}
}
class ThisLockDemo
{
publicstatic void main(String[] args)
{
Tickett = new Ticket();
Threadt1 = new Thread(t);
Threadt2 = new Thread(t);
t1.start(); //走run方法
//但是主线程开启t1后,t1只有执行资格没有执行权
try{Thread.sleep(10);}catch(Exceptione){} //所以要等待下
t.flag= false; //否则系统直接执行完赋值false的代码,
//这时如果t1仍然只有执行资格的话,就只有show方法运行
t2.start(); //走show方法
}
}
>>如果同步函数被静态修饰,锁就不再是this了,因为静态方法中也不可以定义this。静态方法进内存的时候是没有本类对象的,但是一定有该类对应的字节码文件对象:类名.class,该对象类型是Class。
一个类加载进内存,会先将这个类对应的字节码文件封装成对象。该对象的表示方式“类名.class”代表一个字节码文件对象,该对象在内存中是唯一的。所以静态函数使用的锁为该类的字节码文件。
>>复习笔记2面向对象的懒汉式和饿汉式,注意懒汉式的同步锁是该类的字节码文件。
eg:饿汉式
………………
class Single
{
privateSingle(){}
privatestatic final Single s = new Single();
publicstatic Single getInstance()
{
returns;
}
}
………………
eg:懒汉式
………………
class Single
{
privateSingle(){}
privatestatic Single s = null;
publicstatic synchronized Single getInstance()
{
//单例设计模式要保证对象的唯一性
//这里容易产生多线程操作问题,故而加上同步
if(s== null)
{
s= new Single();
}
elsereturn s;
}
}
………………但是这样要判断多次锁,没有效率
为了解决效率问题,采用双重判断模式
………………
class Single
{
privateSingle(){}
privatestatic Single s = null;
public staticSingle getInstance()
{
if(s== null)
{
synchronized(Single.class)//注意,静态,锁不是this
{
if(s== null)//注意,这边的判断也是必要的
{
s= new Single();
}
}
}
returns;
}
}
>>虽然同步的出现解决了线程安全问题。但是也带来了一些弊端:
1、效率会降低
2、容易造成死锁
>>同步中嵌套同步,锁却不同,会造成死锁。
eg:class Testimplements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag= flag;
}
public void run()
{
if(flag== true)
{
synchronized(MyLock.lockA)
{
System.out.println(Thread.currentThread().getName()+"--if---A");
synchronized(MyLock.lockB)
{
System.out.println(Thread.currentThread().getName()+"--if---B");
}
}
}
else
{
synchronized(MyLock.lockB)
{
System.out.println(Thread.currentThread().getName()+"--else---B");
synchronized(MyLock.lockA)
{
System.out.println(Thread.currentThread().getName()+"--else---A");
}
}
}
}
}
class MyLock
{
publicstatic Object lockA = new Object();
publicstatic Object lockB = new Object();
}
class DeadLockTest
{
publicstatic void main(String[] args)
{
Testt1 = new Test(true);
Testt2 = new Test(false);
Threadth1 = new Thread(t1,"小强");
Threadth2 = new Thread(t2,"旺财");
th1.start();
th2.start();
}
}
输出结果:(输出结果会变化)
f:\java\workspace>java DeadLockTest
旺财--else---B
小强--if---A
f:\java\workspace>java DeadLockTest
小强--if---A
旺财--else---B
所以,尽量避免同步嵌套。
自己的解决方案,加上了while循环
eg:………………
public void run()
{
if(flag== true)
{
while(true)
{
synchronized(MyLock.lockA)
{
System.out.println(Thread.currentThread().getName()+"--if---A");
synchronized(MyLock.lockA)
{
System.out.println(Thread.currentThread().getName()+"--if---A");
}
}
}
}
else
{
while(true)
{
synchronized(MyLock.lockB)
{
System.out.println(Thread.currentThread().getName()+"--else---B");
synchronized(MyLock.lockB)
{ System.out.println(Thread.currentThread().getName()+"--else---B");
}
}
}
}
}
………………
>>eg:线程间通信,其实就是多个线程在操作同一个资源(Resource),但是操作的动作不同。
比如一辆车往仓库里送货,而另一辆车往外拉货。
class Res
{
Stringname;
Stringsex;
}
class Input implements Runnable
{
//Objectobj = new Object();//用的不是同一个锁,也会出现问题
private Res r ;//Res是内存中的唯一的,可以作为锁
Input(Resr)
{
this.r= r;
}
publicvoid run()
{
intx = 0;
while(true)
{
synchronized(r)//注意,两个方法处理同一个资源,两个都要同步
{
if(x==0)
{
r.name="mike";
r.sex="man";
}
else
{
r.name="丽丽";
r.sex= "女女女女女";
}
x= (x+1)%2;
}
}
}
}
class Output implements Runnable
{
private Res r ; //Res是内存中的唯一的,可以作为锁
//Objectobj = new Object();//用的不是同一个锁,也会出现问题
Output(Resr)
{
this.r= r;
}
publicvoid run()
{
while(true)
{
synchronized(r)//注意,两个方法处理同一个资源,两个都要同步,
//这里不能用obj
{
System.out.println(r.name+"...."+r.sex);
}
}
}
}
class InputOutputDemo
{
publicstatic void main(String[] args)
{
Resr = new Res();//Res是唯一的,可以作为锁
Inputin = new Input(r);
Outputout = new Output(r);
Threadt1 = new Thread(in);
Threadt2 = new Thread(out);
t1.start();
t2.start();
}
}
>>等待/唤醒机制
wait():让线程等待,将线程存储到一个线程池中。
notify():唤醒被等待的线程,通常都唤醒线程池中的第一个,让被唤醒的线程处于临时阻塞状态。
notifyAll():唤醒所有的等待线程,将线程池中所有线程都唤醒,让他们从冻结状态转到临时阻塞状态。
注意,等待唤醒机制,通常都用在同步中,因为需要锁的支持。而且必须要明确wait notify所作用的锁对象。
这三个方法用于操作线程,可是定义在了Object类中,why?
因为,这三个方法在使用时,都需要定义在同步中,要明确这些方法锁操作的线程所属于的锁。简单说,在A锁被wait的线程,只能被A锁的notify唤醒。
>>线程运行的时候,内存中建立线程池,唤醒时,从线程池唤醒,通常唤醒第一个等待的线程。wait();与notify();与notifyAll();是属于Object类的,他们都是用在同步当中的。
eg:输入一个信息,就打印一个信息
class Resource
{
Stringname;
Stringsex;
booleanb;
}
class Input implements Runnable
{
privateResource res;
privateint count = 0;
Input(Resourcer)
{
this.res= r;
}
publicvoid run()
{
intx = 0;
while(true)
{
synchronized(res)
{
if(res.b)
{
//wait()会抛出异常
try{res.wait();}catch(Exceptione){}//必须标示wait所属的锁r.wait();
}
if(x== 0)
{
res.name= "小青";
res.sex= "女"+count++;
}
else
{
res.name= "志明";
res.sex= "男"+count++;
}
res.b= true;
res.notify();
//必须标示notify所属的锁r.notify();不可以对不同锁中的线程进行唤醒
}
x= (x+1)%2;
}
}
}
class Output implements Runnable
{
//privateint count = 0;
privateResource res;
Output(Resourcer)
{
this.res= r;
}
publicvoid run()
{
while(true)
{
synchronized(res)
{
if(!res.b)
{
try{res.wait();}catch(Exceptione){}
}
System.out.println(res.name+":"+res.sex);
res.b= false;
res.notify();
}
}
}
}
class ResourceDengDaiHuanXing
{
publicstatic void main(String[] args)
{
Resourceresource = new Resource();
Inputin = new Input(resource);
Outputout = new Output(resource);
Threadinn = new Thread(in);
Threadoutt = new Thread(out);
inn.start();
outt.start();
}
}
//优化上述代码
class Resource
{
Stringname;
Stringsex;
booleanb;
//封装Input中的代码
publicsynchronized void set(String name,String sex)
{
if(b)
{
try{this.wait();}catch(InterruptedExceptione){}
}
//为了保证name和sex一次性被赋值,所以set方法定义为同步函数
this.name= name;
this.sex= sex;
b= true;
this.notify();
}
//封装Output中的代码
publicsynchronized void get()
{
if(!b)
{
try{this.wait();}catch(InterruptedExceptione){}
}
为了保证name和sex一次性被输出,所以set方法定义为同步函数
System.out.println(this.name+":"+this.sex);
this.b= false;
this.notify();
}
}
class Input implements Runnable
{
privateResource res;
privateint count = 0;
Input(Resourcer)
{
this.res= r;
}
publicvoid run()
{
intx = 0;
while(true)
{
if(x== 0)
{
res.set("小青","女");
}
else
{
res.set("志明","男");
}
x= (x+1)%2;
}
}
}
class Output implements Runnable
{
//privateint count = 0;
privateResource res;
Output(Resourcer)
{
this.res= r;
}
publicvoid run()
{
while(true)
{
res.get();
}
}
}
//进行优化
class ResourceDengDaiHuanXing
{
publicstatic void main(String[] args)
{
Resourceresource = new Resource();
Inputin = new Input(resource);
Outputout = new Output(resource);
Threadinn = new Thread(in);
Threadoutt = new Thread(out);
inn.start();
outt.start();
}
}
>>eg:多线程生产和多线程消费
情况,生产多件,消费一件
class ProducerConsumerDemo
{
publicstatic void main(String[] args)
{
Resourcer = new Resource();
Producerpro = new Producer(r);
Consumercon = new Consumer(r);
Threadt1 = new Thread(pro);
Threadt2 = new Thread(con);
/*如果这样来实现多生产者和多消费者
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Threadt3 = new Thread(con);
Threadt4 = new Thread(con);
通过while和notifyAll()解决
*/
t1.start();
t2.start();
/*会出现生产多个消费一个和生产一个多个消费的错误
t1.start();
t2.start();
t3.start();
t4.start();
通过while和notifyAll()解决
*/
}
}
class Resource
{
privateString name;
privateint count = 1;
privateboolean flag = false;
//t1、t2
publicsynchronized void set(String name)
{
//if(flag)
while(flag)
/*根据NO.6,处理:既然没有判断,改成while怎样?
结果全部等待,更改notify();解决*/
{
try{wait();}catch(Exceptione){}
/*---NO.1---
第一次执行后t1在这里放弃资格,假设t2下一个获取执行权,
但是也在这里放弃资格,剩下t3、t4
*/
/*---NO.6---
(关键)t2原本在这里放弃资格,醒来后,不做判断flag,
向下执行代码“this.name= name+"--"+count++;”
*/
}
this.name= name+"--"+count++;//姓名号数一起赋值,号数自加
/*---NO.7---
t2生产了一个,覆盖了t1的生产
*/
System.out.println(Thread.currentThread().getName()+
"--生产者--"+this.name);//---NO.4---t1生产了一个
flag= true;
//this.notify();//---NO.5---(关键)t1现在把t2唤醒
//---NO.8---唤醒t3,消费了t2生产的号码
this.notifyAll();//解决全部等待问题
}
//t3、t4
publicsynchronized void out()
{
//if(!flag)
while(!flag)
/*根据NO.6,处理:既然没有判断,改成while怎样?结果全部等待*/
{
try{wait();}catch(Exceptione){}
/*---NO.3---
t1被唤醒但是不一定有执行权,t3到这里放弃资格,
这时醒着的有t1和t4,t4到这里放弃资格
*/
}
System.out.println(Thread.currentThread().getName()+
"--消费者--"+this.name);
flag= false;
//this.notify();//---NO.2---假设t3执行到此,唤醒t1
this.notifyAll();//解决全部等待问题
}
}
class Producer implements Runnable
{
privateResource res;
Producer(Resourceres)
{
this.res= res;
}
publicvoid run()
{
while(true)
{
res.set("+商品+");
}
}
}
class Consumer implements Runnable
{
privateResource res;
Consumer(Resourceres)
{
this.res= res;
}
publicvoid run()
{
while(true)
{
res.out();
}
}
}
同理,生产一个消费多个的情况,是因为t1-t4线程在争夺cpu资源时,获取权限的顺序不同造成的。
>>JDK5.0(1.5)升级版提供了java.util.concurrent.locks包,里面有lock接口,其中有lock()与unlock()方法-显示的加减锁方式。
synchronized使用的是隐式的锁操作。
而Lock接口,使用的是显式的。
另外还有Condition接口,该对象的出现替代了Object中的wait,notify,notifyAll
其中有await()让线程等待,signal()唤醒一个线程和signalAll()唤醒全部线程的方法。
eg: import java.util.concurrent.locks.*;//由于是新特性,注意导入包
class ProducerConsumerDemo2
{
publicstatic void main(String[] args)
{
Resourcer = new Resource();
Producerpro = new Producer(r);
Consumercon = new Consumer(r);
Threadt1 = new Thread(pro);
Threadt2 = new Thread(pro);
Threadt3 = new Thread(con);
Threadt4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resource
{
privateString name;
privateint count = 1;
privateboolean flag = false;
//定义一个锁,ReentrantLock为Lock子类
privateLock lock = new ReentrantLock();
/*API中的定义
一个可重入的互斥锁Lock,它具有与使用 synchronized
方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,
但功能更强大。
*/
/*
通过指定的锁,创建了该锁上可以使用的监视器对象
且一个lock可以搭载多个Condition
*/
privateCondition condition_pro = lock.newCondition();
privateCondition condition_con = lock.newCondition();
/*API中得定义
Condition将 Object监视器方法(wait、notify和 notifyAll)
分解成截然不同的对象,以便通过将这些对象与任意 Lock实现组合使用,
为每个对象提供多个等待set(wait-set)。
*/
public void set(String name)throwsInterruptedException
{
lock.lock();//获取锁,相当于wait();
try
{
while(flag)
condition_pro.await();//await()会抛出异常
this.name= name+"--"+count++;
System.out.println(Thread.currentThread().getName()
+"...生产者.."+this.name);
flag= true;
condition_con.signal();//不用condition.signalALL()了
/*注意,唤醒不要弄错了,这里如果为condition_pro.signal();会死锁*/
}
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.signal();
/*注意,唤醒不要弄错了,这里如果为condition_con.signal();会死锁*/
}
finally
{
lock.unlock();
}
}
}
class Producer implements Runnable
{
privateResource res;
Producer(Resourceres)
{
this.res= res;
}
publicvoid run()
{
while(true)
{
try
{
res.set("+商品+");//注意set方法抛出异常
}
catch(InterruptedException e)
{
}
}
}
}
class Consumer implements Runnable
{
privateResource res;
Consumer(Resourceres)
{
this.res= res;
}
publicvoid run()
{
while(true)
{
try
{
res.out();//注意out方法抛出异常
}
catch(InterruptedException e)
{
}
}
}
}
其中特别要注意的一点,就像使用notify而不是notifyAll一样,如果在Resource类的set方法中写成condition_pro.signal();而在out方法中写成condition_con.signal();,就会造成死锁!全体线程等待!所以,应该是负责生产的线程生产了一件产品,然后唤醒(condition_con.signal();)输出线程消费,消费完了,再唤醒(condition_pro.signal();)负责生产的线程生产产品!两个线程执行完了以后要唤醒对方!
>>一些方法细节
sleep和wait有什么区别?
对时间的指定
1、sleep方法必须指定时间
2、wait方法有重载形式,可以指定时间,也可以不指定时间。
对于执行权和锁的操作
1、sleep():释放执行权,不释放锁,因为肯定能醒,肯定可以恢复临时阻塞状态。
2、wait():释放执行权,释放锁,因为wait不释放锁,如果没有时间指定,那么其他线程都进不了同步中,无法将其唤醒。
注意,同步中可以有多个存活的线程,但是只能有一个去执行同步的代码。因为只有一个线程会持有同步的锁,只有当该线程释放了锁,其他线程才会有机会获取到锁,而且只能有一个线程获取到锁,继续执行。
>>让线程停止,有stop()方法,但是stop()方法已经过时(固有的不安全性,强制停止任何线程)。
如何停止线程?只有一种原理,run()方法结束。另外多线程运行代码通常是循环结构,只要控制住循环,就可以让run方法结束。
但是如果使用标记(flag=true orfalse)会有弊端。
特殊情况:当线程处于冻结状态,就不会读取到标记,那么线程就不会结束。这个时候可以调用interrupt()方法,将处于冻结状态的线程,强制回到运行状态上来,它会返回一个InterruptedException异常。注意,中断(冻结)状态不是停止线程,stop才是停止。
>>setDaemon()将该线程标记为守护线程(后台线程)。该方法必须在启动线程前调用。
线程分前台和后台两种。运行方式都一样都会获取cpu的执行权执行。不同的在于,结束方式不同。前台线程只有run方法结束,才结束。后台线程,run方法结束,结束,还有,如果run方法没结束,而前台线程都结束了。后台线程一样自动结束。
所以一个进程是否结束参考的是,是否还有前台线程存活。如果前台线程都结束了,那么进程也就是结束了。
eg:…………
t1.setDaemon(true);
t1.start();
…………
“前台线程”全部结束,守护线程自动结束。
eg:interrupt方法与setDaemon方法例子
class StopThread implements Runnable
{
privateboolean flag = true;
publicsynchronized void run()
{
while(flag)
{
try
{
wait();//t1t2
}
catch(InterruptedException e)
{
System.out.println(Thread.currentThread().getName()+".....Exception");
setFlag();
}
System.out.println(Thread.currentThread().getName()+".....");
}
}
publicvoid setFlag()
{
flag= false;
}
}
class StopThreadDemo
{
publicstatic void main(String[] args)
{
StopThreadst = new StopThread();
Threadt1 = new Thread(st);
Threadt2 = new Thread(st);
//将两个线程全标记成后台线程。
t1.setDaemon(true);
t2.setDaemon(true);
t1.start();
t2.start();
intnum = 1;
while(true)
{
if(num++==50)
{
//st.setFlag();
//t1.interrupt();//将t1的线程冻结状态清除,强制让其恢复到运行状态来。
//但是这种强制动作会发生异常,需要对个中断异常进行处理.
//只有恢复到运行状态中来才有机会执行标记。
//t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+"....."+num);
}
}
}
>>eg:join方法
…………main
…………
t1.start();
try
{
t1.join();//表示t1需要cpu执行权,这时主线程冻结直到t1结束。
}
catch(InterruptedException e){}
t2.start();
…………
//如果这样
…………main
…………
t1.start();
t2.start();
t1.join();//t1t2交替执行,但是仍然是主线程等到t1执行完后开始执行,与t2无关
…………
会抛出:InterruptedException-如果任何线程中断了当前线程。当抛出该异常时,当前线程的中断状态被清除。
join方法可以用来临时加入线程执行。
>>线程中其它方法:
toString():返回线程名称、优先级、所属组等信息,其覆盖Object的方法。
eg:System.out.println(Thread.currentThread().toString());
一般情况下,谁开启的线程,线程就属于谁,比如一般属于main组,可以用ThreadGroup类更改。
优先级(包括主线程)默认是5,使用setPriority(intnewPriority);方法更改,一共10级。其中1、5、10,对应的常量Thread.MIN_PRIORITY、Thread.NORM_PRIORITY、Thread.MAX_PRIORITY,一般使用常量以提高阅读性。
eg:………………
t1.start();
t1.setPriority(Thread.MAX_PRIORITY);
t2.start();
…………
yield():暂停当前正在执行的线程对象,执行其它线程。
eg: class Demo implements Runnable
{
publicvoid run()
{
for(intx=0; x<60; x++)
{
System.out.println(Thread.currentThread().toString()+"....."+x);
Thread.yield();
//临时暂停。可以达到一种较为和谐的状态。对线程的运行的进行暂缓。
}
}
}
………………
>>两种建立线程的技巧方法:
/*one*/
Runnable r = new Runnable()
{
publicvoid run()
{
for(intx=0; x<100; x++)
{
System.out.println(Thread.currentThread().getName()+"....."+x);
}
}
};//不能调用start方法
new Thread(r).start();
/*two*/
public static void main(String[] args)
{
newThread()
{
publicvoid run()
{
for(intx=0; x<100; x++)
{
System.out.println(Thread.currentThread().getName()+"....."+x);
}
}
}.start();//注意调用start方法