并发concurrent---1
背景:并发知识是一个程序员段位升级的体现,同样也是进入bat的必经之路,有必要把并发知识重新梳理一遍。
说到并发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 }