Java第9章作业:线程

第一题:volatile关键字有什么作用?

由于CPU为了提高对变量的读写效率会将变量存入自己的缓存中,在多个线程访问相同变量时,如果多个线程位于不同的CPU(多核CPU)里面,那么每个线程访问变量(以k=0为例)时会从不同的CPU缓存里面读取,若一个线程将k的值改为了2,但还未将k的值写入了内存中,此时另一个线程所读取到的k是错误的值,这种问题称为可见性问题,即一个线程对变量的修改无法被其他线程“看到”。

volatile关键字的作用就是禁用CPU缓存,即不将变量k存入CPU缓存中,而是直接在内存中进行读取,这样多个线程访问变量k时将会访问到相同的值。

volatile的英文意思是不稳定的,反复无常的,用其修饰变量表明这个变量是不稳定的,每次读写都需要在内存中进行。

第二题:编写Java程序模拟烧水泡茶最优工序。

烧水泡茶最优工序图示如下:
在这里插入图片描述
先分析一下,从图中可以看出,需要两个线程,线程t1执行洗水壶、烧水和泡茶,线程t2执行洗茶壶、洗茶杯、拿茶叶,这些事务的关系是:线程t1执行完洗水壶之后去烧水,同时给线程t2发消息告诉它可以洗茶壶了,当线程t2拿完茶叶之后则通知线程t1表示泡茶工具已经准备好了,等t1烧完水后就可以泡茶了。

代码如下:

package thread;

class Worker{
    volatile private boolean isKettleWashed = false;

    synchronized public void washKettle(){ // 洗水壶
        System.out.println("I am washing kettle, please wait 2s......");
        try{
            Thread.sleep(2*1000); // 2s
        } catch(InterruptedException ie){}

        System.out.println("Kettle is clean!");
        isKettleWashed = true;
        notifyAll();
    }

    public void heatWater(){ // 烧水
        System.out.println("I am heating up water, please wait 10s......");
        try{
            Thread.sleep(10*1000); // 30s
        } catch(InterruptedException ie){}
        
        System.out.println("Water is boiling!");
    }

    synchronized public void washTeapot(){ // 洗茶壶
        while(! isKettleWashed){
            System.out.println(isKettleWashed);
            try{ wait(); }
            catch(InterruptedException ie) {}
        }

        System.out.println("I am washing teapot, please wait 2s......");
        try{
            Thread.sleep(2*1000); // 2s
        } catch(InterruptedException ie){}

        System.out.println("Teapot is clean!");
    }

    public void washTeacup(){ // 洗茶杯
        System.out.println("I am washing teacup, please wait 4s......");
        try{
            Thread.sleep(4*1000); // 4s
        } catch(InterruptedException ie){}

        System.out.println("Teacup is clean!");
    }

    public void takeTea(){ // 拿茶叶
        System.out.println("I am taking tea, please wait 2s......");
        try{
            Thread.sleep(2*1000); // 2s
        } catch(InterruptedException ie){}

        System.out.println("Tea is prepared!");
    }

    public void makeTea(){ // 泡茶
        System.out.println("I am making tea......");
    }
}

class Thread1 implements Runnable{
    Worker w;
    public Thread1(Worker _w){ this.w = _w; }

    public void run(){
        w.washKettle();
        w.heatWater();
        w.makeTea();
    }
}

class Thread2 implements Runnable{
    Worker w;
    public Thread2(Worker _w){ this.w = _w; }

    public void run(){
        w.washTeapot();
        w.washTeacup();
        w.takeTea();
    }
}

public class MakeTea {
    public static void main(String[] args){
        Worker w = new Worker();
        Thread thr1 = new Thread(new Thread1(w), "thread1");
        Thread thr2 = new Thread(new Thread2(w), "thread2");

        long startTime =  System.currentTimeMillis();
        
        thr1.start();
        thr2.start();

        try{
            thr1.join();
            thr2.join();
        }
        catch(InterruptedException it){}

        long endTime =  System.currentTimeMillis();
        long usedTime = (endTime-startTime)/1000;
        System.out.print("Total time is "+usedTime+"s");    
        
    }
}

输出如下:

I am washing kettle, please wait 2s......
Kettle is clean!
I am heating up water, please wait 10s......
I am washing teapot, please wait 2s......
Teapot is clean!
I am washing teacup, please wait 4s......
Teacup is clean!
I am taking tea, please wait 2s......
Tea is prepared!
Water is boiling!
I am making tea......
Total time is 12s

上述代码中需要注意的两个方法如下:

synchronized public void washKettle()
synchronized public void washTeapot()

之所以需要在这三个方法前面加上synchronized,是因为必须在洗完水壶(washKettle方法)之后才可以洗茶壶(washTeapot方法)。

补充知识点:创建线程的两种方式

方式1:通过继承Thread类来创建线程

/** MyThread即是新的线程 */
class MyThread extends Thread{
	/** 必须覆盖Thread类的run方法 */
    public void run(){
        System.out.println("Subclass of Thread");
    }
}

public class CreateThread {
    public static void main(String[] args) {
        // 1. 继承Thread类
        Thread t1 = new MyThread(); // 创建线程对象
        t1.start();
    }
}

方式2:实现Runnable接口,并通过Thread(Runnable target)来创建线程对象

这种方式可以让多个线程访问相同的资源(变量),该资源在实现Runnable接口的类中定义,如下例子中线程t2和t3都可以访问MyRunThreadname属性。


class MyRunThread implements Runnable{
    String name = "heihei";
    /** 必须实现Runnable接口的run方法 */
    public void run(){
        System.out.println("Runnable thread, name is "+name);
    }
}


public class CreateThread {
    public static void main(String[] args) {
        // 2. 实现Runnable接口
        MyRunThread myrun = new MyRunThread();
        Thread t2 = new Thread(myrun);
        t2.start();
		
		Thread t3 = new Thread(myrun);
        t3.start();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值