java中常用的线程同步的函数:
sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会执行;
yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
sleep()可使优先级低的线程得到执行的机会,当然也可以让同优先级和高优先级的线程有执行的机会;yield()只能使同优先级的线程有执行的机会。
调用join()的线程将使其执行完毕后,才执行其他线程。可以控制线程的执行保持特定的顺序。
waite() 和notify()因为会对对象的“锁标志”进行操作,所以它们必须在synchronized函数或synchronized block中进行调用。如果在non-synchronized函数或non-synchronized block中进行调用,虽然能编译通过,但在运行时会发生 IllegalMonitorStateException的异常。
用synchronized来修饰的函数或者程序块将只能允许一个线程访问。
三个经典例子:
1.生产者和消费者问题
class Resource{
int id;
public Resource(int id){
this.id=id;
}
public String toString(){
return "Resource:"+id;
}
}
//临界数据操作类
class DataOpr{
int index=0;
//创建临界资源池
Resource[] rsc=new Resource[10];
//添加资源
public synchronized void add(Resource r){
try{
while(index==rsc.length)){
this.wait();
}
this.notifyAll();
rsc[index]=r;
index++;
}catch(Exception e){
e.printStackTrace();
}
}
//取出资源
public synchronized Resource get(){
try{
while(index==0){
this.wait();
}
this.notifyAll();
}catch(Exception e){
e.printStackTrace();
}
index--;
return rsc[index];
}
}
//生产者线程
class Producer implements Runnable{
DataOpr dp=null;
public Producer(DataOpr dp){
this.dp=dp;
}
public void run(){
for(int i=0;i<20;i++){
Resource rsc=new Resource(i);
dp.add(rsc);
System.out.println("生产了"+rsc);
}
}
}
//消费者线程
class Consumer implements Runnable{
DataOpr dp=null;
public Consumer(DataOpr dp){
this.dp=dp;
}
public void run(){
try{
for(int i=0;i<20;i++){
Thread.sleep(200);
Resource rsc=dp.get();
System.out.println("消费了"+rsc);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
class TestProCon{
public static void main(String[] args){
DataOpr dp=new DataOpr();
Producer p=new Producer(dp);
Consumer c=new Consumer(dp);
Thread t1=new Thread(p);
Thread t2=new Thread(c);
t1.start();
t2.start();
}
}
2.死锁程序示例
//测试类
public class DeadLock{
public static void main(String[] args){
Resource1 r1=new Resource1();
Resource2 r2=new Resource2();
Visitor1 v1=new Visitor1(r1,r2);
Visitor2 v2=new Visitor2(r1,r2);
Thread t1=new Thread(v1);
Thread t2=new Thread(v2);
t1.start();
t2.start();
}
}
//临界资源1
class Resource1{
int i=0;
public void visit(String visitor){
System.out.println("Resource1"+visitor);
}
}
//临界资源2
class Resource2{
public void visit(String visitor){
System.out.println("Resource22"+visitor);
}
}
//线程1
class Visitor1 implements Runnable{
Resource1 r1;
Resource2 r2;
public Visitor1(Resource1 r1,Resource2 r2){
this.r1=r1;
this.r2=r2;
}
public void run(){
synchronized(r1){
r1.visit("visitor1");
try{
Thread.sleep(2000);
}catch(Exception e){
}
synchronized(r2){
r2.visit("visitor2");
}
}
}
}
//线程2
class Visitor2 implements Runnable{
Resource1 r1;
Resource2 r2;
public Visitor2(Resource1 r1,Resource2 r2){
this.r1=r1;
this.r2=r2;
}
public void run(){
synchronized(r2){
r1.visit("visitor2");
try{
Thread.sleep(2000);
}catch(Exception e){
}
synchronized(r1){
r2.visit("visitor1");
}
}
}
}
3. 读者写着问题(读者优先)
//测试类
public class TReaderWriter{
public static void main(String[] args){
TLiberary lib=new TLiberary();
TReader tr=new TReader (lib);
TWriter tw=new TWriter(lib);
Thread tt1=new Thread(tr);
Thread tt2=new Thread(tr);
Thread tt3=new Thread(tr);
Thread tt4=new Thread(tr);
Thread tt5=new Thread(tr);
Thread tt6=new Thread(tw);
Thread tt7=new Thread(tw);
Thread tt8=new Thread(tw);
tt1.start();
tt2.start();
tt3.start();
tt4.start();
tt5.start();
tt6.start();
tt7.start();
tt8.start();
}
}
//图书馆(临界资源类)
class TLiberary{
int rCount;
int wCount;
public synchronized void read(){
while(wCount>=1){
try{
this.wait();
}catch(Exception e){
}
}
this.notify();
rCount++;
System.out.println("reading...");
try{
Thread.sleep(1000);
}catch(Exception e){
}
rCount--;
}
public synchronized void write(){
while(rCount>=1 && wCount==1){
try{
this.wait();
}catch(Exception e){
}
}
this.notify();
wCount++;
System.out.println("writing...");
try{
Thread.sleep(1000);
}catch(Exception e){
}
wCount--;
}
}
//读者线程
class TReader implements Runnable{
TLiberary lib;
public TReader(TLiberary lib){
this.lib=lib;
}
public void run(){
lib.read();
}
}
//写者线程
class TWriter implements Runnable{
TLiberary lib;
public TWriter(TLiberary lib){
this.lib=lib;
}
public void run(){
lib.write();
}
}
相关面试题:
答: 有两种实现方法,分别是继承 Thread类与实现Runnable接口
用synchronized关键字修饰同步方法
反 对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果 很难检查出真正的问题所在。suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此 时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就 会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用 wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。
2 、 sleep() 和 wait() 有什么区别 ?
答: sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。
wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。
3 、 请说出你所知道的线程同步的方法。
答: wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
notityAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
参考:http://wxg6203.iteye.com/blog/763488
目的:多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。
线程的4个状态:
New(新建)线程对象仅仅是创建,并没有调用start()方法。
runnable(就绪)只要调度程序将时间片分配给线程,它便可以立刻运行
Blocked(阻塞)线程可以运行,但是由于某个条件阻止了它的运行。当线程为阻塞状态时,调度程序将不会将时间片分给它。
Dead(死亡)
注:线程进入阻塞的某些原因:1)sleep()方法,使线程进入休眠状态。2)wait()方法,使线程挂起。3)线程等待某个输入/输出的完成。4)线程试图调用某个对象上的同步控制方法,但是该对象锁不可用。5)suspend(),但是Java 2 中被废弃了。通常有3种可以恢复:sleep自动恢复、对于suspend调用resume恢复、对于wait可以用通知(notify或notiyA11)方法使其恢复。
实现方式:继承Thread类和实现Runnable接口重写run方法
线程的级别:
static int MAX_PRIORITY
线程可以具有的最高优先级。
static int MIN_PRIORITY
线程可以具有的最低优先级。
static int NORM_PRIORITY
分配给线程的默认优先级。
主要方法
void interrupt() //中断线程。
static boolean interrupted() //测试当前线程是否已经中断。
boolean isAlive()//测试线程是否处于活动状态。
boolean isDaemon() //测试该线程是否为守护线程。
boolean isInterrupted() //测试线程是否已经中断。
void join() //等待该线程终止。
run()//如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回
static void sleep(long millis)//在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。
void start()// 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
void stop()// 过时
suspend()//已过时。
resume()// 已过时。
destroy()//已过时。
yield()//暂停当前正在执行的线程对象,并执行其他线程。
执行图
注意点:
sleep() 方法:sleep() 允许 指定以毫秒为单位的一段时间作为参数,它使得线程在指定的时间内进入阻塞状态,不能得到CPU 时间,指定的时间一过,线程重新进入可执行状态。可能给其他线程执行的机会(自私,睡着了,不释放锁,时间到了才放锁)
yield() 方法:yield() 使得线程放弃当前分得的 CPU 时间,但是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程。不过yield()只能使同等级别的线程获取执行的机会(公平竞争,释放大家再次选举)。而sleep(1000)使同级别或不同级别的都有可能。
wait() 和 notify() 和notifyAll()方法是Object中定义的方法:
必须在synchronized代码块中使用,在synchronized代码被执行期间,线程可以调用对象的wait()方法,释放对象的锁标志,进入等待的状态,并且可以调用notify()或者notifyAll()方法通知正在等待的其他线程。notify()通知的是等待队列中的第一个线程,notifyAll()通知的是等待队列中的所有数量。
几个方法配套使用,wait() 使得线程进入阻塞状态,它有两种形式,一种允许 指定以毫秒为单位的一段时间作为参数,另一种没有参数,前者当对应的 notify() 被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对应的 notify() 被调用。
wait()和sleep()方法的区别:
wait()在object类里定义;sleep()在Thread类里定义。
wait()方法只能放在同步方法或同步块中,表示资源同步时,线程需要等待。
sleep()方法可放在任何位置,表示当前线程睡眠。
wait()方法会释放对象锁;sleep()不会释放对象锁。
wait()方法要等待唤醒之后,线程才会继续执行。
sleep()则是休眠一段时间,线程自动恢复执行.
sleep()必须捕获异常,而wait(),notify()和notifyAll()不需要捕获异常
notify()和notifyAll()的区别
notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。
notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。
join()是直到执行完(或强制执行一段时间)当前的线程后才往下执行主线程或其他的线程
Java有两种Thread:“守护线程Daemon”与“用户线程User”。
用户线程:Java虚拟机在它所有非守护线程已经离开后自动离开。
守护线程:守护线程则是用来服务用户线程的,如果没有其他用户线程在运行,那么就没有可服务对象,也就没有理由继续下去。
setDaemon(boolean on)方法可以方便的设置线程的Daemon模式,true为Daemon模式,false为User模式。setDaemon(boolean on)方法必须在线程启动之前调用,当线程正在运行时调用会产生异常。isDaemon方法将测试该线程是否为守护线程。值得一提的是,当你在一个守护线程中产生了其他线程,那么这些新产生的线程不用设置Daemon属性,都将是守护线程,用户线程同样。
例:我们所熟悉的Java垃圾回收线程就是一个典型的守护线程,当我们的程序中不再有任何运行中的Thread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是Java虚拟机上仅剩的线程时,Java虚拟机会自动离开。
在守护线程中创建非守护线程(前提是非守护线程执行语句要放在守护线程前),则不会因为其他所有非守护线程的结束而程序运行结束。总之一句话,如果当前还有一个非守护线程在执行,则程序不会结束(不管他是不是在守护线程中创建)。因为在守护线程中创建的默认都是守护线程,当然如果把守护线程创建的默认守护线程改成非守护线程,运行结果就不一样了。而在非守护线程中创建守护线程就很好理解了。
同步:
同步有两种方法。一种同步方法,一种同步代码!分别是synchronized,wait与notify
注意:
同步和异步有何异同,在什么情况下分别使用他们?。
如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。
当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。
synchronized 和 java.util.concurrent.locks.Lock 的异同
主要相同点: Lock能完成synchronized所实现的所有功能
主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。