一、并行、串行、并发
在了解java中多线程的三种实现方式之前,我们首先需要明白并行、串行、并发三个概念。
1.并行:多个CPU同时处理多个任务;
2.串行:单个CPU处理多个任务,当一个任务执行完成之后下一个任务才能够执行;
3.并发:单个CPU处理多个任务,每个任务都会被分一定的时间片,一个任务执行一段时间无论完成与否都要切换另一个任务执行。
在java中多线程其实就是并发的一种模式。
二、java实现多线程的三种方式
我们以买咖啡为例,现在咖啡店只有一个窗口,将每一个人买咖啡的过程视为一个任务。
1.实现Runnable接口
package multithreading; public class LRunnableOne { public static void buyCoffee(String name) { System.out.println(name + "开始买咖啡"); System.out.println(name + "正在买咖啡"); System.out.println(name + "买完了"); } public static void main(String[] args) { Thread t1 = new Thread(new lr("张三")); Thread t2 = new Thread(new lr("李四")); Thread t3 = new Thread(new lr("王五")); System.out.println("run方法并不会启动新的线程,只是执行线程中run内的方法,仍然是在主线程上依次上进行"); t1.run(); t2.run(); t3.run(); System.out.println("start方法会启动新的线程,并发执行"); t1.start(); t2.start(); t3.start(); } } class lr implements Runnable{ String name; public lr(String name) { this.name = name; } @Override public void run() { LRunnableOne.buyCoffee(name); } }
这里我们先是直接调用了run方法,又调用了start方法,这样做是为了说明start方法与run方法的不同。start方法是启动就绪的线程,然后调用线程内的run方法;run并不会启动线程,只是执行其中的方法体。start方法只能调用一次,但是run方法可以调用多次。
2.继承Thread类
package multithreading; public class LThreadOne { public static void buyCoffee(String name) { System.out.println(name + "开始买咖啡"); System.out.println(name + "正在买咖啡"); System.out.println(name + "买完了"); } public static void main(String[] args) { th th1 = new th("张三"); th th2 = new th("李四"); th th3 = new th("王五"); th1.start(); th2.start(); th3.start(); } } class th extends Thread { String name; public th(String name) { this.name = name; } @Override public void run() { LThreadOne.buyCoffee(name); } }
3.实现Callable接口
package multithreading; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class LCallable { public static void buyCoffee(String name) { System.out.println(name + "开始买咖啡"); System.out.println(name + "正在买咖啡"); System.out.println(name + "买完了"); } public static void main(String[] args) { lca l1 = new lca("张三"); lca l2 = new lca("李四"); lca l3 = new lca("王五"); FutureTask futureTask1 = new FutureTask<>(l1); FutureTask futureTask2 = new FutureTask<>(l2); FutureTask futureTask3 = new FutureTask<>(l3); Thread thread1 = new Thread(futureTask1); Thread thread2 = new Thread(futureTask2); Thread thread3 = new Thread(futureTask3); thread1.start(); thread2.start(); thread3.start(); } } class lca implements Callable { String name; public lca(String name) { this.name = name; } @Override public String call() throws Exception { LCallable.buyCoffee(name); return null; } }
以上就是java中三种实现多线程的方式。但是观察以上结果我们也不难发现一个问题,一个窗口售卖咖啡,应该是一个人买完了另一个人才能开始买。还好这里仅仅是售卖咖啡,如果是银行的存取款,就可能会出现一笔存款被取多次的问题,这就是线程的安全问题。
线程安全问题:当多个线程操作同一个数据时,可能会出现数据的异常。
具体解决线程安全问题就需要用到同步和互斥的概念了。
互斥:一个共享资源,A访问时,其它的都被阻塞(不能访问),当A访问完成时另一个才能访问;
同步:A的结果是B的前提,也就是A、B不能同时运行。
互斥是一种特殊的同步,而同步是更为复杂的互斥。
(本文仅作个人学习记录用,如有纰漏敬请指正)