多线程 Java程序运行原理和JVM的启动是多线程的吗 Java程序运行原理:Java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。JVM的启动
是多线程的吗:JVM启动至少启动了垃圾回收线程 和主线程,所以是多线程的。
实现多线程 创建新执行线程有两种方法。 继承Thread类
: 将类声明为Thread的子类。该子类应重写Thread类的run方法。接着调用这个Thread类的start方法(和python一模一样 )
实现Runnable接口
: 创建线程的另一种方法是声明实现Runnable接口的类。该类然后实现run方法。接着将这个类作为参数传给Thread类的构造器,生成Thread对象,最后调用这个Thread对象的start方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class Testing { public static void main (String[] args) { MyThread mt = new MyThread(); mt.start(); for (int i = 0 ; i < 1000 ; i++) { System.out.println("bb" ); } } } class MyThread extends Thread { public void run () { for (int i = 0 ; i < 1000 ; i++) { System.out.println("aaaaaaaaaaaa" ); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Demo3_Thread { public static void main (String[] args) { MyRunnable mr = new MyRunnable(); Thread t = new Thread(mr); t.start(); for (int i = 0 ; i < 1000 ; i++) { System.out.println("bb" ); } } } class MyRunnable implements Runnable { @Override public void run () { for (int i = 0 ; i < 1000 ; i++) { System.out.println("aaaaaaaaaaaa" ); } } }
两种方式的区别
源码的区别:
继承Thread : 由于子类重写了Thread类的run(), 当调用start()时, 直接找子类的run()方法
实现Runnable : 构造函数中传入了Runnable的引用, 成员变量记住了它, start()调用run()方法时内部判断成员变量Runnable的引用是否为空, 不为空编译时看的是Runnable的run(),运行时执行的是子类的run()方法
继承Thread
好处是 : 可以直接使用Thread类中的方法,代码简单
弊端是 : 因为Java的单继承 , 如果已经有了父类,就不能用这种方法
实现Runnable接口
好处是 : 即使自己定义的线程类有了父类也没关系 , 因为有了父类也可以实现接口,而且接口是可以多实现的
弊端是 : 不能直接使用Thread中的方法 , 需要先获取到线程对象后 , 才能得到Thread的方法 , 代码复杂
匿名实现两种方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Testing { public static void main (String[] args) { new Thread() { public void run () { for (int i = 0 ; i < 1000 ; i++) { System.out.println("aaaaaaaaaaaa" ); } } }.start(); for (int i = 0 ; i < 1000 ; i++) { System.out.println("bb" ); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Testing2 { public static void main (String[] args) throws IOException { new Thread(new Runnable(){ @Override public void run () { for (int i = 0 ; i < 1000 ; i++) { System.out.println("aaaaaaaaaaaa" ); } } }).start(); for (int i = 0 ; i < 1000 ; i++) { System.out.println("bb" ); } } }
实现Runnable的原理
看Thread类的构造函数,传递了Runnable接口的引用
通过init()方法找到传递的target给成员变量的target赋值
查看run方法,发现run方法中有判断,如果target不为null就会调用Runnable接口子类对象的run方法
1 2 MyRunnable mr = new MyRunnable(); Thread t = new Thread(mr);
简单来说 , 就是将mr一层层的函数中的传递 , 最后执行会mr.run()方法
多线程的方法
获取名字 : 通过getName()方法 获取线程对象的名字
设置名字 : 通过构造函数 可以传入String类型的名字(或者使用setName()方法)
1 2 3 4 5 6 7 8 9 public class Testing { public static void main (String[] args) { new Thread() { public void run () { System.out.println(this .getName()); } }.start(); } }
1 2 3 4 5 6 7 8 9 public class Testing { public static void main (String[] args) { new Thread("thread-hyl" ) { public void run () { System.out.println(this .getName()); } }.start(); } }
1 2 3 4 5 6 7 8 9 10 public class Testing { public static void main (String[] args) { new Thread() { public void run () { this .setName("hyl" ); System.out.println(this .getName()); } }.start(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Testing { public static void main (String[] args) { Thread thread1 = new Thread() { public void run () { this .setName("hyl" ); System.out.println(this .getName()); } }; thread1.setName("hyl" ); thread1.start(); } }
获取当前线程的对象 Thread.currentThread() : 返回对当前正在执行的线程对象 的引用。
1 2 3 4 5 6 7 8 9 10 11 public class Testing3 { public static void main (String[] args) { new Thread(new Runnable(){ @Override public void run () { System.out.println(Thread.currentThread().getName()); } } ).start(); } }
休眠线程 Thread.sleep(1);
1 2 3 4 5 6 7 8 public class Testing4 { public static void main (String[] args) throws InterruptedException { for (int i = 20 ; i >= 0 ; i--) { Thread.sleep(1000 ); System.out.println("倒计时第" +i + "秒" ); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Demo3_Sleep { public static void main (String[] args) throws InterruptedException { new Thread() { public void run () { for (int i = 0 ; i < 10 ; i++) { try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName() + "...aaaaaaaaaa" ); } } }.start(); } }
守护线程 setDaemon(), 设置一个线程为守护线程, 该线程不会单独执行, 当其他非守护线程都执行结束后, 自动退出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class Testing4 { public static void main (String[] args) { Thread t1 = new Thread() { public void run () { for (int i = 0 ; i < 2 ; i++) { System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaa" ); } } }; Thread t2 = new Thread() { public void run () { for (int i = 0 ; i < 50 ; i++) { System.out.println(getName() + "...bb" ); } } }; t2.setDaemon(true ); t1.start(); t2.start(); } }
加入线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public class Testing5 { public static void main (String[] args) { final Thread t1 = new Thread() { public void run () { for (int i = 0 ; i < 10 ; i++) { System.out.println(getName() + "...aaaaaaaaaaaaa" ); } } }; Thread t2 = new Thread() { public void run () { for (int i = 0 ; i < 10 ; i++) { if (i == 2 ) { try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(getName() + "...bb" ); } } }; t1.start(); t2.start(); } }
礼让线程 yield让出cpu礼让线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Demo6_Yield { public static void main (String[] args) { new MyThread().start(); new MyThread().start(); } } class MyThread extends Thread { public void run () { for (int i = 1 ; i <= 1000 ; i++) { if (i % 10 == 0 ) { Thread.yield(); } System.out.println(getName() + "..." + i); } } }
设置线程的优先级 setPriority()设置线程的优先级
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Demo7_Priority { public static void main (String[] args) { Thread t1 = new Thread(){ public void run () { for (int i = 0 ; i < 100 ; i++) { System.out.println(getName() + "...aaaaaaaaa" ); } } }; Thread t2 = new Thread(){ public void run () { for (int i = 0 ; i < 100 ; i++) { System.out.println(getName() + "...bb" ); } } }; t1.setPriority(Thread.MIN_PRIORITY); t2.setPriority(Thread.MAX_PRIORITY); t1.start(); t2.start(); } }
同步代码块 什么情况下需要同步
当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步.
如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码.
同步代码块
使用synchronized
关键字加上一个锁对象来定义一段代码, 这就叫同步代码块
多个同步代码块如果使用相同的锁对象, 那么他们就是同步的
异步代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public class Testing { public static void main (String[] args) { final Printer p = new Printer(); new Thread() { public void run () { while (true ) { p.print1(); } } }.start(); new Thread() { public void run () { while (true ) { p.print2(); } } }.start(); } } class Printer { public void print1 () { System.out.print("黑" ); System.out.print("马" ); System.out.print("程" ); System.out.print("序" ); System.out.print("员" ); System.out.print("\r\n" ); } public void print2 () { System.out.print("传" ); System.out.print("智" ); System.out.print("播" ); System.out.print("客" ); System.out.print("\r\n" ); } }
这里的异步代码块为:
1 2 3 4 5 System.out.print("传" ); System.out.print("智" ); System.out.print("播" ); System.out.print("客" ); System.out.print("\r\n" );
将其改为同步只要添加一个关键字即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 synchronized (d) { System.out.print("黑" ); System.out.print("马" ); System.out.print("程" ); System.out.print("序" ); System.out.print("员" ); System.out.print("\r\n" ); } synchronized (d) { System.out.print("传" ); System.out.print("智" ); System.out.print("播" ); System.out.print("客" ); System.out.print("\r\n" ); }
两段代码需要使用同一把锁
锁对象可以是任意的
所以不能使用匿名对象作为锁 , 因为匿名的对象不是同一个对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class Demo1_Synchronized { public static void main (String[] args) { final Printer p = new Printer(); new Thread() { public void run () { while (true ) { p.print1(); } } }.start(); new Thread() { public void run () { while (true ) { p.print2(); } } }.start(); } } class Printer { Demo d = new Demo(); public void print1 () { synchronized (d) { System.out.print("黑" ); System.out.print("马" ); System.out.print("程" ); System.out.print("序" ); System.out.print("员" ); System.out.print("\r\n" ); } } public void print2 () { synchronized (d) { System.out.print("传" ); System.out.print("智" ); System.out.print("播" ); System.out.print("客" ); System.out.print("\r\n" ); } } } class Demo {}
同步方法 使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的
非静态
的同步方法的锁对象是什么?
答 : 非静态的同步方法的锁对象是this
静态
的同步方法的锁对象是什么?
答 : 是该类的字节码对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class Demo2_Synchronized { public static void main (String[] args) { final Printer2 p = new Printer2(); new Thread() { public void run () { while (true ) { p.print1(); } } }.start(); new Thread() { public void run () { while (true ) { p.print2(); } } }.start(); } } class Printer2 { Demo d = new Demo(); public static synchronized void print1 () { System.out.print("黑" ); System.out.print("马" ); System.out.print("程" ); System.out.print("序" ); System.out.print("员" ); System.out.print("\r\n" ); } public static void print2 () { synchronized (Printer2.class) { System.out.print("传" ); System.out.print("智" ); System.out.print("播" ); System.out.print("客" ); System.out.print("\r\n" ); } } }
线程安全问题
多线程并发操作同一数据时, 就有可能出现线程安全问题
使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Testing3 { public static void main (String[] args) { new Ticket().start(); new Ticket().start(); new Ticket().start(); new Ticket().start(); } } class Ticket extends Thread { private static int ticket = 100 ; public void run () { while (true ) { if (ticket <= 0 ) { break ; } System.out.println(getName() + "...这是第" + ticket-- + "号票" ); } } }
注意:
private static int ticket = 100;
一定要加static
. 这样就是四个窗口共同卖100张
如果是private int ticket = 100;
就是每个线程都是各自卖100张(一共卖了400张)
其实上面代码是有问题的 , 给上面代码添加一个睡眠来暴露出这个问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class Testing3 { public static void main (String[] args) { new Ticket().start(); new Ticket().start(); new Ticket().start(); new Ticket().start(); } } class Ticket extends Thread { private static int ticket = 100 ; public void run () { while (true ) { if (ticket <= 0 ) { break ; } try { Thread.sleep(10 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName() + "...这是第" + ticket-- + "号票" ); } } }
原因很简单 ,
当ticket==3
的时候 , 线程1,2,3同时进入睡眠
醒来之后不管ticket等于多少 , 还是向下执行
解决方法:使用Thread类
使用Thread类
使用Ticket类的字节码文件作为锁对象
所以就算是四个Ticket对象的线程都会同步
不能使用this , 因为四个Ticket对象都是不同的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class Demo3_Ticket { public static void main (String[] args) { new Ticket().start(); new Ticket().start(); new Ticket().start(); new Ticket().start(); } } class Ticket extends Thread { private static int ticket = 100 ; public void run () { while (true ) { synchronized (Ticket.class) { if (ticket <= 0 ) { break ; } try { Thread.sleep(10 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName() + "...这是第" + ticket-- + "号票" ); } } } }
锁对象也可以改为private static Object obj = new Object();
或者其他
但是注意 : 如果用引用数据类型成员变量当作锁对象,必须是静态的
原因还是那个 : 这里是创建了四个Ticket对象 , 如果private Object obj = new Object();
. 那么每个Ticket对象都有自己的obj . 导致了不是同一个锁
使用实现Runnable接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class Demo4_Ticket { public static void main (String[] args) { MyTicket mt = new MyTicket(); new Thread(mt).start(); new Thread(mt).start(); new Thread(mt).start(); new Thread(mt).start(); } } class MyTicket implements Runnable { private int tickets = 100 ; @Override public void run () { while (true ) { synchronized (this ) { if (tickets <= 0 ) { break ; } try { Thread.sleep(10 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "...这是第" + tickets-- + "号票" ); } } } }
死锁 多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁
避免死锁 : 尽量不要嵌套使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class Demo5_DeadLock { private static String s1 = "筷子左" ; private static String s2 = "筷子右" ; public static void main (String[] args) { new Thread() { public void run () { while (true ) { synchronized (s1) { System.out.println(getName() + "...获取" + s1 + "等待" + s2); synchronized (s2) { System.out.println(getName() + "...拿到" + s2 + "开吃" ); } } } } }.start(); new Thread() { public void run () { while (true ) { synchronized (s2) { System.out.println(getName() + "...获取" + s2 + "等待" + s1); synchronized (s1) { System.out.println(getName() + "...拿到" + s1 + "开吃" ); } } } } }.start(); } }
线程安全的类
Vector是线程安全的 , ArrayList是线程不安全的
StringBuffer是线程安全的 , StringBuilder是线程不安全的
Hashtable是线程安全的 , HashMap是线程不安全的
两个线程间的通信 怎么通信
如果希望线程等待, 就调用wait()
如果希望唤醒等待的线程, 就调用notify();
这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 public class Demo1_Notify { public static void main (String[] args) { final Printer p = new Printer(); new Thread() { public void run () { while (true ) { try { p.print1(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run () { while (true ) { try { p.print2(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } } class Printer { private int flag = 1 ; public void print1 () throws InterruptedException { synchronized (this ) { if (flag != 1 ) { this .wait(); } System.out.print("黑" ); System.out.print("马" ); System.out.print("程" ); System.out.print("序" ); System.out.print("员" ); System.out.print("\r\n" ); flag = 2 ; this .notify(); } } public void print2 () throws InterruptedException { synchronized (this ) { if (flag != 2 ) { this .wait(); } System.out.print("传" ); System.out.print("智" ); System.out.print("播" ); System.out.print("客" ); System.out.print("\r\n" ); flag = 1 ; this .notify(); } } }
`this.notify(); : 随机 唤醒单个等待的线程
三个及以上间的线程通信 通信的问题
notify()方法是随机唤醒一个线程
notifyAll()方法是唤醒所有线程
JDK5之前无法唤醒指定的一个线程
如果多个线程之间通信, 需要使用notifyAll()通知所有线程, 用while来反复判断条件
在同步代码块中,用哪个对象锁,就用哪个对象调用wait方法
为什么wait方法和notify方法定义在Object这类中?
因为锁对象可以是任意对象,Object是所有的类的基类,所以wait方法和notify方法需要定义在Object这个类中
sleep方法和wait方法的区别?
sleep方法必须传入参数,参数就是时间,时间到了自动醒来 , wait方法可以传入参数也可以不传入参数,传入参数就是在参数的时间结束后等待,不传入参数就是直接等待
sleep方法在同步函数或同步代码块中,不释放锁,睡着了也抱着锁睡 , wait方法在同步函数或者同步代码块中,释放锁
互斥锁
同步
使用ReentrantLock类的lock()
和unlock()
方法进行同步
通信
使用ReentrantLock类的newCondition()方法可以获取Condition对象
需要等待的时候使用Condition的await()方法, 唤醒的时候用signal()方法
不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;public class Demo3_ReentrantLock { public static void main (String[] args) { final Printer3 p = new Printer3(); new Thread() { public void run () { while (true ) { try { p.print1(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run () { while (true ) { try { p.print2(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run () { while (true ) { try { p.print3(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } } class Printer3 { private ReentrantLock r = new ReentrantLock(); private Condition c1 = r.newCondition(); private Condition c2 = r.newCondition(); private Condition c3 = r.newCondition(); private int flag = 1 ; public void print1 () throws InterruptedException { r.lock(); if (flag != 1 ) { c1.await(); } System.out.print("黑" ); System.out.print("马" ); System.out.print("程" ); System.out.print("序" ); System.out.print("员" ); System.out.print("\r\n" ); flag = 2 ; c2.signal(); r.unlock(); } public void print2 () throws InterruptedException { r.lock(); if (flag != 2 ) { c2.await(); } System.out.print("传" ); System.out.print("智" ); System.out.print("播" ); System.out.print("客" ); System.out.print("\r\n" ); flag = 3 ; c3.signal(); r.unlock(); } public void print3 () throws InterruptedException { r.lock(); if (flag != 3 ) { c3.await(); } System.out.print("i" ); System.out.print("t" ); System.out.print("h" ); System.out.print("e" ); System.out.print("i" ); System.out.print("m" ); System.out.print("a" ); System.out.print("\r\n" ); flag = 1 ; c1.signal(); r.unlock(); } }
线程组 线程组概述
Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理 ,Java允许程序直接对线程组进行控制。
默认情况下,所有的线程都属于主线程组
。
public final ThreadGroup getThreadGroup()
: 通过线程对象获取他所属于的组
public final String getName()
: 通过线程组对象获取他组的名字
我们也可以给线程设置分组
ThreadGroup(String name) 创建线程组对象并给其赋值名字
创建线程对象
Thread(ThreadGroup?group, Runnable?target, String?name)
设置整组的优先级或者守护线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Testing3 { public static void main (String[] args) { MyRunnable mr = new MyRunnable(); Thread t1 = new Thread(mr, "张三" ); Thread t2 = new Thread(mr, "李四" ); ThreadGroup tg1 = t1.getThreadGroup(); ThreadGroup tg2 = t2.getThreadGroup(); System.out.println(tg1.getName()); System.out.println(tg2.getName()); } } class MyRunnable implements Runnable { @Override public void run () { for (int i = 0 ; i < 1000 ; i++) { System.out.println(Thread.currentThread().getName() + "...." + i); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Demo4_ThreadGroup { public static void main (String[] args) { ThreadGroup tg = new ThreadGroup("我是一个新的线程组" ); MyRunnable mr = new MyRunnable(); Thread t1 = new Thread(tg, mr, "张三" ); Thread t2 = new Thread(tg, mr, "李四" ); System.out.println(t1.getThreadGroup().getName()); System.out.println(t2.getThreadGroup().getName()); tg.setDaemon(true ); } } class MyRunnable implements Runnable { @Override public void run () { for (int i = 0 ; i < 1000 ; i++) { System.out.println(Thread.currentThread().getName() + "...." + i); } }
线程的五种状态
新建 : 创建线程对象
就绪 : 线程对象已经启动了,但是还没有获取到CPU的执行权
运行 : 获取到了CPU的执行权
阻塞 : 没有CPU的执行权回到就绪
死亡 : 代码运行完毕,线程消亡
线程池 线程池概述
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
内置线程池的使用概述
JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
public static ExecutorService newFixedThreadPool(int nThreads)
: 线程池里有多个线程
public static ExecutorService newSingleThreadExecutor()
: 线程池里就一个线程
这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法
使用步骤:
创建线程池对象
创建Runnable实例
提交Runnable实例
关闭线程池
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class Testing4 { public static void main (String[] args) { ExecutorService pool = Executors.newFixedThreadPool(2 ); pool.submit(new MyRunnable()); pool.submit(new MyRunnable()); pool.shutdown(); } } class MyRunnable implements Runnable { @Override public void run () { for (int i = 0 ; i < 1000 ; i++) { System.out.println(Thread.currentThread().getName() + "...." + i); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;public class Testing4 { public static void main (String[] args) throws InterruptedException, ExecutionException { ExecutorService pool = Executors.newFixedThreadPool(2 ); Future<Integer> f1 = pool.submit(new MyCallable(100 )); Future<Integer> f2 = pool.submit(new MyCallable(50 )); System.out.println(f1.get()); System.out.println(f2.get()); pool.shutdown(); } } class MyCallable implements Callable <Integer > { private int num; public MyCallable (int num) { this .num = num; } @Override public Integer call () throws Exception { int sum = 0 ; for (int i = 1 ; i <= num; i++) { sum += i; } return sum; } }
Callable的评价
单例设计模式 单例设计模式:保证类在内存中只有一个对象。
如何保证类在内存中只有一个对象呢?
私有构造方法。private
在本类中创建一个本类的对象。Singleton s;
提供公共的访问方式。 public static Singleton getInstance(){return s}
示例 :
饿汉式
: 一开始就创建了实例对象 , 之后只要通过getInstance()
去引用这个对象即可.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Testing4 { public static void main (String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2); } } class Singleton { private Singleton () {} public static final Singleton s = new Singleton(); public static Singleton getInstance () { return s; } }
懒汉式
: 先不创建对象 , 在需要的时候才创建对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class Testing4 { public static void main (String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2); } } class Singleton { private Singleton () {} private static Singleton s ; public static Singleton getInstance () { if (s == null ) { s = new Singleton(); } return s; } }
懒汉式
和饿汉式
的区别:
饿汉式是空间换时间,懒汉式是时间换空间
在多线程访问时,饿汉式不会创建多个对象,而懒汉式有可能会创建多个对象
所以尽量使用饿汉式
Runtime类 Runtime类是一个单例类
1 2 3 4 5 6 7 8 import java.io.IOException;public class Demo2_Runtime { public static void main (String[] args) throws IOException { Runtime r = Runtime.getRuntime(); r.exec("shutdown -s -t 300" ); } }
Timer类 线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import java.util.Date;import java.util.Timer;import java.util.TimerTask;public class Demo3_Timer { public static void main (String[] args) throws InterruptedException { Timer t = new Timer(); t.schedule(new MyTimerTask(), new Date(188 , 6 , 1 , 14 , 22 , 50 ),3000 ); } } class MyTimerTask extends TimerTask { @Override public void run () { System.out.println("起床背英语单词" ); } }
简单工厂模式 概述 : 又叫静态工厂方法模式
,它定义一个具体的工厂类负责创建一些类的实例
优点 : 客户端不需要在负责对象的创建,从而明确了各个类的职责
缺点 : 这个静态工厂类负责所有对象的创建,如果有新的对象增加,或者某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期的维护
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class Testing5 { public static void main (String[] args) { Dog d = (Dog) AnimalFactory.createAnimal("dog" ); d.eat(); Cat c = (Cat) AnimalFactory.createAnimal("cat" ); c.eat(); } } class AnimalFactory { public static Animal createAnimal (String name) { if ("dog" .equals(name)) { return new Dog(); }else if ("cat" .equals(name)) { return new Cat(); }else { return null ; } } } abstract class Animal { public abstract void eat () ; } class Cat extends Animal { @Override public void eat () { System.out.println("猫吃鱼" ); } } class Dog extends Animal { @Override public void eat () { System.out.println("狗吃肉" ); } }
工厂方法模式
概述 : 工厂方法模式中抽象工厂类负责定义创建对象的接口,具体对象的创建工作由继承抽象工厂的具体类实现。
优点:客户端不需要在负责对象的创建,从而明确了各个类的职责,如果有新的对象增加,只需要增加一个具体的类和具体的工厂类即可,不影响已有的代码,后期维护容易,增强了系统的扩展性
缺点:需要额外的编写代码,增加了工作量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class Testing { public static void main (String[] args) { DogFactory df = new DogFactory(); Dog d = (Dog) df.createAnimal(); d.eat(); } } interface Factory { public Animal createAnimal () ; } class DogFactory implements Factory { @Override public Animal createAnimal () { return new Dog(); } } class CatFactory implements Factory { @Override public Animal createAnimal () { return new Cat(); } } abstract class Animal { public abstract void eat () ; } class Cat extends Animal { @Override public void eat () { System.out.println("猫吃鱼" ); } } class Dog extends Animal { @Override public void eat () { System.out.println("狗吃肉" ); } }