Java多线程
Java多线程
Java实现多线程有四种方式:
1.继承Thread类;
2.实现Runable接口;
3.实现Callable接口,通过FutureTask包装器来创建Thread线程;
4.使用ExecutorService、Callable、Future实现有返回结果的多线程;
其中前两种线程执行完是没有返回结果的,后两种是有返回值的。
先贴出一个多线程售票的简单示例,根据代码去理解多线程:
/** * 一个售票类,实现Runnable接口重写run()方法来实现线程 */ @Data public class SellTicket implements Runnable{ //票总数 private int tickets; SellTicket(int tickets){ this.tickets = tickets; } @Override public void run() { while(true) { //锁,如果这里不加锁的话,当多个线程同时进入tickets > 0时,可能会出现最后余票为-1,-2的情况 synchronized (this){ if(tickets > 0) { try { Thread.sleep(100); } catch(Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "卖掉一张票,剩下: " + --tickets +"张"); }else{ break; } } } } }
/** * 测试类 */ public class Test { public static void main(String[] args) { SellTicket p1 = new SellTicket(20); Thread t1 = new Thread(p1,"线程1"); Thread t2 = new Thread(p1,"线程2"); Thread t3 = new Thread(p1,"线程3"); //setPriority(),参数为1-10,参数越大,优先级越高,参数超过10会报错 //t1.setPriority(1); //t2.setPriority(2); //t3.setPriority(10); t1.start(); t2.start(); t3.start(); } }
运行结果:
在测试类中,如果将注释的优先级放开,那执行结果都会是线程3在售票,当票数更多时,线程1和线程2也会开始售票。当有优先调度需求时,setPriority()就能派上用场。
总结:
1. 前两种创建多线程的方式差不多,只是因为Java只支持单继承但可以实现多个接口,所以继承Thread类来实现接口会有一定的局限性。
2.Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。
修改售票示例,通过实现Callable接口实现多线程,并知道线程2卖了多少张票:
/** * 一个售票类,实现Callable接口重写call()方法来实现线程 */ @Data public class SellTickets implements Callable<Integer> { //票总数 private int tickets; SellTickets(int tickets){ this.tickets = tickets; } @Override public Integer call() { int thread2 = 0; while(true) { //锁,如果这里不加锁的话,当多个线程同时进入tickets > 0时,可能会出现最后余票为-1,-2的情况 synchronized (this){ if(tickets > 0) { try { Thread.sleep(100); } catch(Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "卖掉一张票,剩下: " + --tickets +"张"); if("Thread-1".equals(Thread.currentThread().getName())){ thread2++; } }else{ break; } } } return thread2; } }
/** * 测试类 */ public class Test { public static void main(String[] args) { Callable<Integer> callable = new SellTickets(100); FutureTask<Integer> futureTask = new FutureTask<>(callable); Thread thread = new Thread(futureTask); thread.start(); FutureTask<Integer> futureTask2 = new FutureTask<>(callable); Thread thread2 = new Thread(futureTask2); thread2.start(); //获取线程2的返回值 Integer sum = 0; try{ sum = futureTask2.get(); }catch (Exception e){ e.printStackTrace(); } System.out.println("线程2售票:"+sum+"张"); } }
具体是创建Callable接口的实现类,并实现call()方法。并使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程。首先,我们发现,在实现Callable接口中,此时不再是run()方法了,而是call()方法,此call()方法作为线程执行体,同时还具有返回值!