• 并发concurrent---1


    背景:并发知识是一个程序员段位升级的体现,同样也是进入BAT的必经之路,有必要把并发知识重新梳理一遍。

    并发concurrent:

    说到并发concurrent,肯定首先想到了线程,创建线程有两种方法:1、从Java.lang.Thread类派生一个新的线程类,重载它的run()方法;2、实现Runnalbe接口,重载Runnalbe接口中的run()方法;建议使用方法二创建线程,因为,如果是通过扩展 Thread类的方法来创建线程,那么这个自定义类就不能再去扩展其他的类,也就无法实现更加复杂的功能;而实现Runnable接口的方法来定义该类为线程类,这样就可以避免Java单继承所带来的局限性,也更符合面向对象编程的思想,最重要的就是使用实现Runnable接口的方式创建的线程可以处理同一资源,从而实现资源的共享。

    创建线程的两种方法:

     1 package www.concurent.test;
     2 public class TraditionalThread {
     3 
     4     public static void main(String[] args) {
     5         //Thread1:
     6         Thread thread = new Thread() {
     7             @Override
     8             public void run() {
     9                 while(true) {
    10                     try {
    11                         Thread.sleep(1000);
    12                     } catch (InterruptedException e) {
    13                         e.printStackTrace();
    14                     }
    15                     System.out.println("thread1: "+Thread.currentThread().getName());
    16                 }
    17             }
    18         }; 
    19         thread.start();
    20         
    21         //Thread2:
    22         //Runnable变量是线程要运行的代码的宿主,更适合面向对象思想的线程方法
    23         Thread thread2 = new Thread(new Runnable() {
    24             @Override
    25             public void run() {
    26                 while(true) {
    27                     try {
    28                         Thread.sleep(1000);
    29                     } catch (InterruptedException e) {
    30                         e.printStackTrace();
    31                     }
    32                     System.out.println("thread2: "+Thread.currentThread().getName());
    33                 }
    34             }
    35         });
    36         thread2.start();
    37     }
    38 }

    线程和Timer定时器很类似,下面介绍了两种和线程相似的定时器写法:1、定时一天之后调用方法查询天气情况接口,然后每隔60秒后继续调用该方法;2、定时每天00:39:32调用查询天气情况接口,通过Hutool工具和Timer定时器调用HTTP天气状况接口的返回结果如下截图:(result2得到了天津的天气状况)

    通过Hutool工具和Timer定时器调用HTTP天气状况接口

     1 import java.util.Calendar;
     2 import java.util.Date;
     3 import java.util.Timer;
     4 import java.util.TimerTask;
     5 import cn.hutool.http.HttpUtil;
     6 
     7 public class TraditionalTimerTest {
     8     //时间间隔
     9      private static final long PERIOD_DAY = 24 * 60 * 60 * 1000;
    10     //Timer 定时器
    11     public static void main(String[] args) {
    12         Calendar cl = Calendar.getInstance();
    13         cl.set(Calendar.HOUR_OF_DAY, 0);
    14         cl.set(Calendar.MINUTE, 39);
    15         cl.set(Calendar.SECOND, 32);
    16         Date date = cl.getTime();
    17         Date dateNow = new Date();
    18         //如果第一次执行定时任务的时间 小于 当前的时间
    19         //此时要在 第一次执行定时任务的时间 加一天,以便此任务在下个时间点执行。如果不加一天,任务会立即执行。
    20         if (date.before(dateNow)) {
    21             Calendar clAdd = Calendar.getInstance();
    22             clAdd.setTime(dateNow);
    23             clAdd.add(Calendar.DAY_OF_MONTH, 1);
    24              date = clAdd.getTime();
    25         }
    26         //Timer1:
    27         new Timer().schedule(new TimerTask() {
    28             @Override
    29             public void run() {
    30                 System.out.println("hello");
    31                 //Hutool调用http接口
    32                 String result1 = HttpUtil.get("http://t.weather.sojson.com/api/weather/city/101030100");
    33                 System.out.println("result1: "+result1);
    34             }
    35             //一天之后调用方法查询天气情况接口,然后每隔60秒后继续调用该方法
    36         },PERIOD_DAY , 1000*60);
    37         //Timer2:
    38         new Timer().schedule(new TimerTask() {
    39             @Override
    40             public void run() {
    41                 //Hutool调用http接口
    42                 String result2 = HttpUtil.get("http://t.weather.sojson.com/api/weather/city/101030100");
    43                 System.out.println("result2: " + result2);
    44             }
    45             //定时每天00:39:32调用查询天气情况接口
    46         }, date , PERIOD_DAY);
    47     }
    48 
    49 }

     如果是单个线程调用都还ok,要是有多个线程同时调用那就会出现并发产生;比如有一个方法Output()是经过charAt(i)获取字符串i的字符并且打印再控制台,然后线程A和线程B同时调用Output()方法,此时就会出现线程不安全问题(如银行取钱和转账同时进行),也就是并发;执行结果发现,线程A为执行完毕线程B就开始执行了,为了能后保证当有一个线程来执行某个方法时,其他的线程不能进来执行该方法,实现排他性,可以通过synchronized和ReentrantLock来实现线程同步;二者其实区别不大,synchronized由于是底层JVM实现的互斥,因此效率会高一些,而ReentrantLock的功能则比synchronized更多,比如定时获取某个锁,多个等待条件等,另外synchronized 会让线程阻塞,ReentrantLock会让线程等待,但是从行为效果上来看是一样的;下面有个例子:并发结果如截图显示,理想状态是打印“huawei”或者“isoftstone”,但是由于并发打印出来诸如此类“ishuaweoftstoni”结果。

    FYI:

     1 import java.util.concurrent.locks.Lock;
     2 import java.util.concurrent.locks.ReentrantLock;
     3 
     4 public class MyThreadSynchronized {
     5     public static void main(String[] args) {
     6         //要想调用内部类的对象,必须有外部类的实例对象
     7         new MyThreadSynchronized().init();   // 外部类的实例对象
     8     }
     9     public void init() {
    10         final Outputer outputer = new Outputer();
    11         //thread1:
    12         new Thread(new Runnable() {
    13             @Override
    14             public void run() {
    15                 while(!false) {
    16                     try {
    17                         Thread.sleep(100);
    18                     } catch (InterruptedException e) {
    19                         e.printStackTrace();
    20                     }
    21                     outputer.output3("huawei");
    22                 }
    23             }
    24         }).start();
    25         
    26         //thread2:
    27         new Thread(new Runnable() {
    28             @Override
    29             public void run() {
    30                 while(!false) {
    31                     try {
    32                         Thread.sleep(100);
    33                     } catch (InterruptedException e) {
    34                         e.printStackTrace();
    35                     }
    36                     outputer.output("isoftstone");
    37                 }
    38             }
    39         }).start();
    40     }
    41     //当有一个线程来执行某个方法时,其他的线程不能进来执行该方法,排他性、独一无二;
    42     //使用synchronized/lock同步,且线程用的同步锁是同一个同步对象,可用this互斥或方法.class
    43     //synchronized由于是底层JVM实现的互斥,因此效率会高一些
    44     //ReentrantLock的功能则比synchronized更多,比如定时获取某个锁,多个等待条件
    45     //synchronized 会让线程阻塞,ReentrantLock会让线程等待,但是从行为效果上来看是一样的;
    46 class Outputer{
    47     //内部类  静态方法中不能new内部类的实例对象
    48         //synchronized:
    49         public synchronized void output(String name) {
    50             for(int i = 0; i<name.length(); i++) {
    51                 System.out.print(name.charAt(i));
    52             }
    53             System.out.println();// switch line
    54         }
    55         
    56         //线程不安全
    57         public  void output3(String name) {
    58             for(int i = 0; i<name.length(); i++) {
    59                 System.out.print(name.charAt(i));
    60             }
    61             System.out.println();// switch line
    62         }
    63         
    64         //lock:
    65         public void ouputlock(String name) {
    66             Lock lock = new ReentrantLock();
    67             lock.lock();   // 上锁同步
    68             try {
    69                 for (int i = 0; i < name.length(); i++) {
    70                     System.out.print(name.charAt(i));
    71                 }
    72                 System.out.println();// switch line
    73             } finally {
    74                 lock.unlock();  // 解锁
    75             }
    76         }
    77         
    78 }
    79     
    80 }
  • 相关阅读:
    Vue2.5 旅游项目实例27 联调测试上线-项目打包上线
    Vue2.5 旅游项目实例26 联调测试上线-真机测试
    Vue2.5 旅游项目实例25 联调测试上线-项目前后端联调
    Vue2.5 旅游项目实例24 详情页-在项目中添加基础动画
    Vue2.5 旅游项目实例23 详情页 Ajax动态获取数据
    Vue2.5 旅游项目实例22 详情页 使用递归组件实现详情页列表
    HTML5标签embed详解
    MongoDB使用经验总结
    16个非常酷的jQuery插件
    kendo-ui的MVVM模式
  • 原文地址:https://www.cnblogs.com/taojietaoge/p/10285607.html
Copyright © 2020-2023  润新知