java第五课

   ■  关于线程

编写一个线程的实例如下: 

class  MultiThread {
    
public static void main(String[] args){
        MyThread mt
=new MyThread();
        mt.start();
        
while(true){
        System.out.println(
"main:"+Thread.currentThread().getName());
        }

    }

}

class  MyThread  extends  Thread {
    
public void run(){
        
while(true){
        System.out.println(getName());
        }

    }

}

 

编译运行:

 可见打印出了线程的名称,其中Thread-0是系统自动分配的。

我们在main函数中先创建一个MyThread类的对象mt,启动线程,然后打印输出获得主函数线程的语句,然而运行程序先输出主函数线程名称,后输出MyThread类的对象启动的线程名称。

这是因为,当运行程序时,先执行主函数方法所在线程,系统为其分配一个时间片,而当主函数执行到mt.start();时并没有用完时间片,所以先输出了main函数的线程名称,然后mt.start();执行,执行mt的方法run所在线程。

Thread类的setDaemon方法设置线程是否为后台线程,只有一个布尔型参数,true表示时后台线程。

setDaemon方法必须在线程启动之前调用:

class  MultiThread {
    
public static void main(String[] args){
        MyThread mt
=new MyThread();
        mt.setDaemon(
true);   //设置线程mt是为后台线程
        mt.start();
        
int index=0;
        
while(true){
            
if(index++==5){
                
break;
            }

        System.out.println(
"main:"+Thread.currentThread().getName());
        }

    }

}

class  MyThread  extends  Thread {
    
public void run(){
        
while(true){
        System.out.println(getName());
        }

    }

}

编译运行:

 可见,当主函数方法所在线程终止时,后台线程也会终止执行。

当我们在run()中打印完线程名称时,再执行语句yield();则让出执行时间片,运行结果:

 可以设置和获取线程的优先级:

setPriority(int newPriority) ;

getPriority() ;

Thread类定义了三个优先级常量:

MAX_PRIORITY       最大优先级10

MIN_PRIORITY        最小优先级1

NORM_PRIORITY   默认优先级5

改变一个线程的优先级:

mt.setPriority(Thread.MAX_PRIORITY);

查看运行情况:

(此时如果yield();仍然被执行)

 如果删除yield();语句,则:

 只有Thread-0在执行。

也可以去实现Runnable类的接口:

class  MultiThread {
    
public static void main(String[] args){
        MyThread mt
=new MyThread();
        
new Thread(mt).start();
        
while(true){
        System.out.println(
"main:"+Thread.currentThread().getName());
        }

    }

}

class  MyThread  implements  Runnable {   //实现Runnable接口
    public void run(){
        
while(true){
        System.out.println(Thread.currentThread().getName());
        }

    }

}

编译运行:

通常,如果我们并不需要修改线程类当中除了run方法以外其它方法的行为,最好去实现Runnable接口。

实现Runnable接口有两个好处:

一个是:如果我们定义的类(MyThread)已经继承了一个类,因为java中支持单继承,所以我们定义的类(MyThread)无法再去继承其它的类。所以可以采用实现接口的方式。

另一个是:实现Runnable接口,如果多线程同时访问同一种资源,这时会很方便。比如:

class  MultiThread {
    
public static void main(String[] args){
        MyThread mt
=new MyThread();
        
new Thread(mt).start();  //创建多个线程
        new Thread(mt).start();
        
new Thread(mt).start();
        
new Thread(mt).start();
        
new Thread(mt).start();
        
new Thread(mt).start();
        
while(true){
        System.out.println(
"main:"+Thread.currentThread().getName());
        }

    }

}

class  MyThread  implements  Runnable {
    
int index=0;  //共享变量
    public void run(){
        
while(true){
        System.out.println(Thread.currentThread().getName()
+":"+index++);
        }

    }

}

编译运行:

可见,多个线程可以同时都访问同一个共享计数变量index,实现计数。

另外,还可以通过内部类实现上述多个线程同时访问同一个共享计数变量的功能:

class  MultiThread {
    
public static void main(String[] args){
        MyThread mt
=new MyThread();
        mt.getThread().start();  
//用内部类的实例启动线程
        mt.getThread().start();
        mt.getThread().start();
        mt.getThread().start();
        mt.getThread().start();
        
while(true){
        System.out.println(
"main:"+Thread.currentThread().getName());
        }

    }

}

class  MyThread {
    
int index=0;  //共享计数变量index
    class InnerThread extends Thread{  //定义内部类InnerThread
        public void run(){
            
while(true){
                System.out.println(Thread.currentThread().getName()
+":"+index++);
            }

        }

    }

    Thread getThread()
{  //实现一个获得内部类InnerThread的实例的方法
        return new InnerThread();
    }

}

编译运行:

下面通过一个售票系统实例来应用线程的知识:

class  TicketSystem {
    
public static void main(String[] args){
        SellThread st
=new SellThread();
        
new Thread(st).start();
        
new Thread(st).start();
        
new Thread(st).start();
        
new Thread(st).start();
        
new Thread(st).start();
    }

}

class  SellThread  implements  Runnable {
    
int tickets=20;
    
public void run(){
        
while(true){
            
if(tickets>0){
                System.out.println(Thread.currentThread().getName()
+" sell ticket "+tickets);
                tickets
--;
            }

        }

    }

}

编译运行:

上面的程序存在隐患:

如果在售最后一张票时,各个线程进入到if(tickets>0)语句时,时间片都到期了,则下一次执行时间片时分别打印出1,0,-1,-2,-3这样的票,显然是不正确的。

我们运行程序之所以没有出现这种情况是因为时间片很短,但是在实际中经过长期运行就可能发生上面的隐患。

然而,真正找到错误原因又是很困难的。

下面我们使用Thread类的静态方法sleep,让隐患重现:

class  TicketSystem {
    
public static void main(String[] args){
        SellThread st
=new SellThread();
        
new Thread(st).start();
        
new Thread(st).start();
        
new Thread(st).start();
        
new Thread(st).start();
        
new Thread(st).start();
    }

}

class  SellThread  implements  Runnable {
    
int tickets=20;
    
public void run(){
        
while(true){
            
if(tickets>0)
                
try{
                    Thread.sleep(
10);  //sleep方法会抛出异常
                }
catch(Exception e){
                    e.printStackTrace();
                }

                System.out.println(Thread.currentThread().getName()
+" sell ticket "+tickets);
                tickets
--;
            }

        }

    }

}

编译运行:

要解决这个问题,要涉及到线程的同步问题。

 

■  线程同步

同步的两种方式:同步块和同步方法。

上面程序中,if语句块是一个临界区,要实现线程互斥地访问共享变量tickets。

用同步块实现如下:

class  TicketSystem {
    
public static void main(String[] args){
        SellThread st
=new SellThread();
        
new Thread(st).start();
        
new Thread(st).start();
        
new Thread(st).start();
        
new Thread(st).start();
        
new Thread(st).start();
    }

}

class  SellThread  implements  Runnable {
    
int tickets=20;
    Object obj
=new Object();  //定义一个对象
    public void run(){
        
while(true){
            
synchronized(obj){
                
if(tickets>0){
                    
try{
                        Thread.sleep(
10);
                        }
catch(Exception e){
                            e.printStackTrace();
                        }

                        System.out.println(Thread.currentThread().getName()
+" sell ticket "+tickets);
                        tickets
--;
                }

            }

        }

    }

}

编译运行:

用同步块实现,现在结果正常了。

同步块实现机制:

synchronized(){}实现了线程的同步,在{}之间的代码段成为理临界区。

java中每一个对象都有一个监视器,或者叫做锁。

上面程序通过obj作为一个监视器对象,同步块实现了对obj监视器加锁和解锁功能。

同步方法的实现如下:

class  TicketSystem {
    
public static void main(String[] args){
        SellThread st
=new SellThread();
        
new Thread(st).start();
        
new Thread(st).start();
        
new Thread(st).start();
        
new Thread(st).start();
        
new Thread(st).start();
    }

}

class  SellThread  implements  Runnable {
    
int tickets=20;
    Object obj
=new Object();
    
public void run(){
        
while(true){
            sell();  
//调用同步方法
        }

    }

    
public synchronized void sell(){  //定义同步方法,将临界区代码在这个方法中实现
        if(tickets>0){
            
try{
                Thread.sleep(
10);
                }
catch(Exception e){
                    e.printStackTrace();
                }

                System.out.println(Thread.currentThread().getName()
+" sell ticket "+tickets);
                tickets
--;
        }

    }

}

编译运行:

用同步方法,这就实现了线程同步的功能。

同步方法实现机制:

类中有一个this对象,this对象作为一个监视器,同步方法实现对this监视器的加锁和解锁操作,实现线程同步。

同步方法利用的是this所代表的对象的锁。

每个class也有一个锁,是这个class所对应的Class对象的锁。

测试在同步方法中是this对象的加锁解锁:

class  TicketSystem {
    
public static void main(String[] args){
        SellThread st
=new SellThread();
        
new Thread(st).start();
        
try{
            Thread.sleep(
1);  //启动一个线程之后,先要让主函数所在线程睡眠一下
        }
catch(Exception e){
            e.printStackTrace();
        }

        st.bool
=true;  //用于控制同步块和同步方法交错执行的boolean变量
        new Thread(st).start();
    }

}

class  SellThread  implements  Runnable {
    
int tickets=20;
    
boolean bool=false;  //定义类SellThread的boolean型成员变量
    public void run(){
        
if(bool){  //如果bool=false;则执行同步块
            while(true){
                
synchronized(this){
                    
if(tickets>0){
                        
try{
                            Thread.sleep(
10);
                        }
catch(Exception e){
                            e.printStackTrace();
                        }

                        System.out.println(
"Synchronized-Segment:"+Thread.currentThread().getName()+" sell ticket "+tickets);
                        tickets
--;
                    }

                }

            }

        }

        
else{  //否则bool=true;则执行调用的同步方法
            while(true){
                sell();
            }

        }

    }

    
public synchronized void sell(){  //同步方法的实现
        if(tickets>0){
            
try{
                Thread.sleep(
10);
                }
catch(Exception e){
                    e.printStackTrace();
                }

                System.out.println(
"Synchronized-Method:"+Thread.currentThread().getName()+" sell ticket "+tickets);
                tickets
--;
        }

    }

}

编译运行:

我们可以看到同步块和同步方法交错执行,实现同步。验证了this对象作为一个监视器实现加锁解锁。

■  死锁实例

死锁的例子如下:

class  TicketSystem {
    
public static void main(String[] args){
        SellThread st
=new SellThread();
        
new Thread(st).start();
        
try{
            Thread.sleep(
1);
        }
catch(Exception e){
            e.printStackTrace();
        }

        st.bool
=true;
        
new Thread(st).start();
    }

}

class  SellThread  implements  Runnable {
    
int tickets=20;
    Object obj
=new Object();
    
boolean bool=false;
    
public void run(){
        
if(bool){
            
while(true){
                
synchronized(obj){  //第一个线程先访问obj对象,加锁
                        try{
                            Thread.sleep(
10);
                        }
catch(Exception e){
                            e.printStackTrace();
                        }

                        
synchronized(this)//当被唤醒时请求this对象
                            if(tickets>0){
                                System.out.println(
"Synchronized-Segment:"+Thread.currentThread().getName()+" sell ticket "+tickets);
                                tickets
--;
                            }

                        }

                }

            }

        }

        
else{
            
while(true){
                sell();
            }

        }

    }

    
public synchronized void sell(){
        
synchronized(obj){
            
if(tickets>0){
                
try{
                    Thread.sleep(
10);
                }
catch(Exception e){
                    e.printStackTrace();
                }

                System.out.println(
"Synchronized-Method:"+Thread.currentThread().getName()+" sell ticket "+tickets);
                tickets
--;
            }

        }

    }

}

编译运行:

执行过程:

第一个线程先访问obj对象,加锁,然后进入睡眠状态;

然后第二个线程执行sell方法,对this对象进行加锁;

第二个进程进入sell方法后请求obj对象,所以进入等待状态,唤醒第一个线程;

第一个线程通过执行synchronized(this)请求this对象。

这就陷入了这样的循环等待:

第一个线程占有obj对象,而请求对this对象进行加锁;而第二个线程占有this对象,而请求对obj对象进行加锁。

双方互相等待,程序的执行不能够向前推进。

■  生产者-消费者问题

class  Test {
    
public static void main(String[] args){
        Buffer buf
=new Buffer();
        Producer pro
=new Producer(buf);
        Consumer con
=new Consumer(buf);
        pro.start();
        con.start();
    }

}

class  Buffer {  //定义缓冲区类,存放数据
    int value;
    
boolean bFull=false;  //定义boolean型变量,用于互斥操作缓冲区
    public synchronized void put(int value){
        
if(!bFull){  //如果缓冲区为空
            this.value=value;
            bFull
=true;
            notify();  
//通知消费者已经放入了生产的数据
        }

        
try{
            wait();  
//如果缓冲区满则等待
        }
catch(Exception e){
            e.printStackTrace();
        }

    }

    
public synchronized int get(){
        
if(!bFull){  //如果缓冲区空
            try{
                wait();  
//等待生产者放入生产的数据
            }
catch(Exception e){
                e.printStackTrace();
            }

        }

        bFull
=false;  //设置缓冲区为空的标志
        notify();  //通知生产者缓冲区中的数据已经取出,缓冲区为空了 
        return value;  //如果缓冲区不空,则取出数据
    }

}

class  Producer  extends  Thread {  //定义生产者类
    Buffer buf;
    Producer(Buffer buf)
{
        
this.buf=buf;
    }

    
public void run(){
        
for(int i=0;i<10;i++){
            buf.put(i);
            System.out.println(
"Produce "+i);
        }

    }

}

class  Consumer  extends  Thread {  //定义消费者类
    Buffer buf;
    Consumer(Buffer buf)
{
        
this.buf=buf;
    }

    
public void run(){
        
while(true){
            System.out.println(
"Consume "+buf.get());
        }

    }

}

编译运行:

如果在方法put和get不用synchronized修饰为同步方法,则运行出错:

因为它们必须对同一个this对象进行操作。

使用wait方法时会抛出异常,必须进行异常捕捉处理。

 

■  线程的状态

线程的终止可以通过设置一个flag变量,并结合interrupt()方法。

我们用设置一个boolean型变量bStop:

class  TestThread {
    
public static void main(String[] args){
        MyThread mt
=new MyThread();
        mt.start();
        
int index=0;
        
while(true){
            
if(index++==10){
                mt.stopThread();
                
break;
            }

            System.out.println(Thread.currentThread().getName());
        }

        System.out.println(
"main() exit!");
    }

}

class  MyThread  extends  Thread {
    
private boolean bStop=false;
    
public synchronized void run(){
        
while(!bStop){
            
try{
                wait();
            }
catch(Exception e){
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName());
        }

    }

    
public void stopThread(){
        bStop
=true;
    }

}

编译运行:

我们发现程序并没有退出,因为在MyThread类的run方法中,我们启动mt.start()的线程处于等待状态。

我们可以设置interrupt()方法终止线程的运行:

class  TestThread {
    
public static void main(String[] args){
        MyThread mt
=new MyThread();
        mt.start();
        
int index=0;
        
while(true){
            
if(index++==10){
                mt.stopThread();
                mt.interrupt();  
//中断mt所在线程
                break;
            }

            System.out.println(Thread.currentThread().getName());
        }

        System.out.println(
"main() exit!");
    }

}

class  MyThread  extends  Thread {
    
private boolean bStop=false;
    
public synchronized void run(){
        
while(!bStop){
            
try{
                wait();
            }
catch(InterruptedException e){  //当中断线程时会抛出一个InterruptedException异常,必须对其进行捕捉处理
                if(bStop){  //如果bStop为true时,即mt所在线程已经被中断
                    return;  //返回
                }

            }
catch(Exception e){
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName());
        }

    }

    
public void stopThread(){
        bStop
=true;
    }

}

编译运行:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值