Java第九章学习心得

写在前面:

这一章主要讲了线程的有关知识点。线程这一章和其他几章有着本质上的区别,究其原因,是在于线程允许了同时可以做多个事情,可以并发的执行一些任务。那么接下来我们就来一起探究一下本章里的重要知识。

目录:

1.FutureTask类有什么作用?它实现了哪些接口?Callable接口和Runnable接口有什么不同?

2.volatile关键字有什么作用?请举例说明。

3.编写Java程序模拟烧水泡茶最优工序。

4. 编写一个基于多线程的生产者/消费者Java应用,各产生10个生产者和消费者线程,共享一个缓冲区队列(长度自设),生产者线程将产品放入到缓冲区,消费者线程从缓冲区取出产品。

5.阅读公众号“码农翻身”的文章---《我是一个线程》。

1.FutureTask类有什么作用?它实现了哪些接口?Callable接口和Runnable接口有什么不同?

答:详解如下。

FutureTask表示可取消的异步计算。FutureTask类提供了一个Future的基本实现 ,具有启动和取消计算的方法,查询计算是否完整,并检索计算结果。结果只能在计算完成后才能检索; 如果计算尚未完成,则get方法将阻止。 一旦计算完成,则无法重新启动或取消计算(除非使用runAndReset()调用计算 )。

FutureTask实现了RunnableFuture接口,RunnableFuture接口继承了Runnable接口和Future接口,所以FutureTask兼备Runnable和Future两种特性

下面说一下Callable接口。

Interface Callable<V>   V call() throws Exception   可返回计算结果值,且可以抛出异常。在这里我们看到函数的返回值类型为V,而我们传进去的参数恰好也是V,那么就实现了一个理想的结果:我传进去什么类型的参数,Callable就返回什么类型的参数。不传参,就不返回任何参数。

我们举个例子看看:

class Adder implements Callable<Integer>
{   
    int[] datas=null;
    Adder(int[] _datas){
       datas = _datas;
    }
public Integer call(){
     int sum=0;
     for (int i=0;i<datas.length;i++){
         sum+=datas[i];
         System.out.println(sum);
     }
     return Integer.valueOf(sum); }}

那么我们使用Adder类对象的call方法就能得到这个整形数组中所有数字的和。

再说一下Runnable接口:

接口抽象方法:public void run();同样需要覆写run方法。

public class MyThread {
    public static void main ( String args[] ) {                 
       Thread a = new Thread(new Runnable(){//匿名内部类
          public void run(){
             System.out.println("This is another thread.");
          }
       });
       a.start();
       System.out.println("This is main thread."); 
    }                                             
}//上面几个例子中,新建的MyThread类对象只被使用了一次,那么按照之前所讲,可以使用匿名类对象来写。
public class MyThread {
    public static void main ( String args[] ) {                
        Thread a = new Thread(()->{
             System.out.println("This is another thread.");
        }); //Lambda简写, Java SE8特性     
        a.start();
        System.out.println("This is main thread.");  
   }                                             
}

 此种方式更加灵活,且多个线程可共享某个对象的资源。(target值一样,都指向了那个被使用的对象)

两者之间最大的区别在于:runnable没有返回值,而实现了callble接口的任务线程能返回执行结果;callable接口实现类中的run方法允许异常向上抛出,但runnable接口中实现的run方法中的异常必须在内部进行处理,而不能向上抛出。

2.volatile关键字有什么作用?请举例说明。

答:详见如下。

我们知道,为提高效率,当需要使用某一个变量的时候,通常不会倾向于从硬盘、内存中去读取,而是从缓存中去读取。这样每个代码块可以备份一个这个变量的副本,以供下次使用的时候迅速拿来。但是正是这种高效、便利的方法带来了线程方面的隐患。

例如,有两个线程需要操作同一个变量i,如果不加限制,那么他们就会各自将初始的i缓存下来,然后各自进行操作。假设这两个线程应该交替执行i--操作,但由于这个缓存问题,没办法保证两者严格按照你一次我一次的来执行。例如,第一次线程一执行,那么线程一备份的i减1;下一次线程二执行,线程二的i减1.这样减一操作执行了两次,但是没有任何一个i等于8.

而volatile就是解决这种问题的。好了,现在开始,你们都别给我用缓存了,宁可麻烦一点。现在i确实可以保证是等于8了,但是还有一个问题,就是这个i并不能保证按照我们的设想而等于8.只要某个线程连续执行两次,就能得到这个结果,但是我们希望达成两个目的:第一,两个线程交替执行;第二,这个i被两个线程各自减去1后得到8.第二个任务被完成了一半,最本质的事情还没有解决:无法保证线程的交替执行。

不过这个步骤仍然是不可或缺的。准确说来,这是完成任务的必要条件,而非充分条件,自然更不是充要条件。

3.编写Java程序模拟烧水泡茶最优工序。

答:详解如下。

import java.util.*;
import java.util.concurrent.*;
/**
 * FutureTask_1 需要执行的任务:洗水壶、烧开水、泡茶
 */
class FutureTask_1 implements Callable<String> {

    // FutureTask_1 中持有 FutureTask_2 的引用
    FutureTask<String> futureTask_2;

    // 通过构造器初始化 成员变量
    public FutureTask_1(FutureTask<String> futureTask_2) {
        this.futureTask_2 = futureTask_2;
    }

    // 重写的 Callable 接口中的 call 方法。
    @Override
    public String call() throws Exception {

        System.out.println("T1:洗水壶");
        TimeUnit.SECONDS.sleep(1);

        System.out.println("T1:烧开水");
        TimeUnit.SECONDS.sleep(15);

        // 获取 T2 线程的茶叶
        String teas = futureTask_2.get();
        System.out.println("拿到茶叶:" + teas);

        System.out.println("T1:开始泡茶...");

        return "上茶:" + teas;
    }
}
/**
 * FutureTask_2 需要执行的任务:洗茶壶、洗茶杯、拿茶叶
 */
class FutureTask_2 implements Callable<String> {

    @Override
    public String call() throws Exception {

        System.out.println("T2:洗茶壶");
        TimeUnit.SECONDS.sleep(1);

        System.out.println("T2:洗茶杯");
        TimeUnit.SECONDS.sleep(2);

        System.out.println("T2:拿茶叶");
        TimeUnit.SECONDS.sleep(1);

        return "茉莉花茶";
    }
}
public class TEST{
    public static void main(String []args){

        // 创建 FutureTask_2 的任务
        FutureTask<String> futureTask_2 = new FutureTask<>(new FutureTask_2());
        // 创建 FutureTask_1 的任务,并将 FutureTask_2 任务的引用传入
        FutureTask<String> futureTask_1 = new FutureTask<>(new FutureTask_1(futureTask_2));

        // 创建线程 T1,来执行任务 FutureTask_1
        Thread t1 = new Thread(futureTask_1);
        t1.start();

        // 创建线程 T2,来执行任务 FutureTask_2
        Thread t2 = new Thread(futureTask_2);
        t2.start();

        try {
            // 获取任务 FutureTask_1 的最后一步的结果
            System.out.println(futureTask_1.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

4. 编写一个基于多线程的生产者/消费者Java应用,各产生10个生产者和消费者线程,共享一个缓冲区队列(长度自设),生产者线程将产品放入到缓冲区,消费者线程从缓冲区取出产品。

class Resource implements Runnable {
  volatile public int i;        
  volatile public Integer it;
  public  Resource(int _i){
    i = _i;
    it = new Integer(i);
  }
  public  void run(){
   while(true){
    synchronized(it){
     if (i>0){
        try{
           Thread.sleep(200);
        }
        catch(Exception e){}
        i--;
        System.out.println(Thread.
        currentThread().getName()+"  "+i);
    }
    else{
        System.out.println(Thread.
        currentThread().getName());
        break; }}
}}}
class Maker implements Runnable {
  volatile public int i;        
  volatile public Integer it;
  public  Maker(int _i){
    i = _i;
    it = new Integer(i);
  }
  public  void run(){
   while(true){
    synchronized(it){
     if (i>0){
        try{
           Thread.sleep(200);
        }
        catch(Exception e){}
        i++;
        System.out.println(Thread.
        currentThread().getName()+"  "+i);
    }
    else{
        System.out.println(Thread.
        currentThread().getName());
        break; }}
}}}
public class TestSecurity{
public static void main(String[] args){
  Resource m = new Resource(9);
  Thread t1 = new Thread(m);
  Thread t2 = new Thread(m);
  Maker m1=new Maker(m);
  Maker m2=new Maker(m);
  t1.start();
  t2.start();
  m1.start();
  m2.start();
}
}

5.阅读公众号“码农翻身”的文章---《我是一个线程》。

真的推荐大家去看看。讲的深入浅出、通俗易懂,很多决策和想法都更贴近实际生活中的分配调度问题,让每个人都能切切实实感受到这个东西确实是给我们带来了很多的方便。这样我们就能更好的模拟生活中的更多问题,让代码变得”聪明“,从而大大减少我们的负担。

嗯。。就写到这里吧

听说你还没有给惜惜点关注?!

哼!我数三个数,再不点赞、收藏加关注,我就要吃了你哦!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值