并发编程:现如今电脑都支持同一时间运行多个程序了,那么并发编程也就成为了刚需.
并发编程的作用:提升效率.所谓人多力量大,一群人干活肯定比一个人干活要快不少.
各种语言都是如何实现并发编程的:
java实现并发编程:标准库中提供了Thread类,通过Thread就能够实现多线程.其实操作系统本身就提供了一组API,是C语言风格的,因为操作系统本身就是C/C++写的,所以提供的事C语言风格的也就不奇怪了.但java本身的特性就是为了方便,统一而生,如果换了个系统又要学习一套新的API就太麻烦了,所以java就索性直接把各个系统提供的用于并发编程的API都封装成了Thread类,这样的话就只用学习Thread类就可以应付各种操作系统了,也就降低了程序员的学习成本.
创建Thread类的几种基本方法:
第一种方法:
class MyThread extends Thread { @Override public void run() { System.out.println("hello thread"); } } public class Demo { public static void main(String[] args) { Thread t = new MyThread(); t.start(); } }
第二种方法:
class MyRunnable implements Runnable { @Override public void run() { System.out.println("hello thread"); } } public class Demo { public static void main(String[] args) { Thread t = new Thread(new MyRunnable()); t.start(); } }
第三种方法:
public class Demo { public static void main(String[] args) { Thread t = new Thread() { @Override public void run() { System.out.println("hello"); } }; t.start(); } }
第四种方法:
public class Demo { public static void main(String[] args) { Thread t = new Thread(new Runnable() { @Override public void run() { System.out.println("hello"); } }); t.start(); } }
第五种方法:
public class Demo { public static void main(String[] args) { Thread t = new Thread(() -> { System.out.println("hello thread"); }); t.start(); } }
接下来我们解析一下第一种方法的代码:
对于第二种方法里的Runnable:
sleep方法:sleep方法可以让线程进入阻塞状态,也就是暂时不会被cpu调度执行.
演示:假如我执行下面这串代码,创建了个线程,且这个线程在无限循环打印这个hello thread字符串,会非常快.
public class Demo { public static void main(String[] args) { Thread t = new Thread(() -> { while (true){ System.out.println("hello thread"); } }); t.start(); } }
如果我想让他慢一点,隔个1s钟打印一次,就可以用sleep方法了.sleep方法里的参数是时间,单位是ms.
public class Demo { public static void main(String[] args) { Thread t = new Thread(() -> { while (true){ System.out.println("hello thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); t.start(); } }
sleep方法是属于线程的,哪个线程调用它,哪个线程就进入阻塞状态.
一个进程至少有一个线程就是main方法线程.
两个线程并发执行,都调用sleep方法进入阻塞状态,且时间相同,再次唤醒先执行哪个线程是不确定的,这是由操作系统决定的.因此这两个线程相当于抢占式执行.这也会带来一些麻烦.因为有的时候我们是希望线程按照我们所预期的顺序来执行的。
解决方法:使用wait()和notify().
既然多线程比顺序执行执行效率高,那我们就来试验一下.分别创建两个方法,方法一使用顺序执行,方法二使用多线程执行.这两个方法的目的就是自增到10_0000_0000.看看谁的执行时间更短.
public class Demo { private static long num = 10_0000_0000L; public static void ser() { long begin = System.currentTimeMillis(); int a = 0; for (long i = 0; i < num; i++) { a++; } for (long i = 0; i < num; i++) { a++; } long end = System.currentTimeMillis(); long time = end - begin; System.out.println(time); } public static void mul() { long begin = System.currentTimeMillis(); Thread t1 = new Thread(() -> { for (long i = 0; i < num; i++) { i++; } }); t1.start(); Thread t2 = new Thread(() -> { for (long i = 0; i < num; i++) { i++; } }); t2.start(); try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } try { t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); long time = end - begin; System.out.println(time); } public static void main(String[] args) { ser(); // mul(); } }
当我调用ser()方法也就是只有一个线程,执行时间为500ms左右,但当我调用mul()方法也就是两个线程同时工作时,执行时间为200多ms.这样一比较就可以明显的得出一个结论,多线程效率是更高的.但要注意一点的是.你要计算的数据必须要够大.因为本身创建线程也是要时间的.如果你计算这个东西只用20ms,而创建两个线程就需要60ms,那肯定就不如直接使用串行执行了.本身多线程就是用来执行较大计算的.
举个例子,你今天要搬1000块砖,那肯定叫多几个人来搬,搬得快一点,那如果只要搬20块砖,那你就直接自己搬就完了呗,因为你要去叫人也是花费时间的,等你叫来人,20块砖早搬完了.
线程等待join()方法:join方法是用来控制执行顺序的,当执行到这个方法后,当前线程会进入阻塞状态,直到调用的线程执行完毕后才会继续执行下面的代码.因为在刚才计算时间的代码其实是在main这个主线程里的,也就是说主线程和其他的几个线程是并发执行的,如果不使用join方法,当创建完线程之后,计算时间这个代码也就执行完毕,那肯定是不对的.
join方法里的参数是时间,单位是ms,可以写也可以不写,当不写时就意味着死等.直到t线程结束才继续执行下面的代码
如果写了,比如写了1000ms也就是1s后如果t线程还没执行完毕主线程也不管了直接执行下面的代码.
</