并发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 }

 

posted @ 2019-01-18 01:10  涛姐涛哥  阅读(660)  评论(0编辑  收藏  举报