说明
进程和线程的关系就是:一个进程可以包含一个或多个线程,但至少会有一个线程。
多进程的优点:多进程稳定性比多线程高,因为在多进程的情况下,一个进程崩溃不会影响其他进程,而在多线程的情况下,任何一个线程崩溃会直接导致整个进程崩溃。
场景场景:一个进程中至少一个线程,一个进程中多个线程,多个进程中多个线程
Java语言内置了多线程支持。当Java程序启动的时候,实际上是启动了一个JVM进程,然后,JVM启动主线程来执行main()
方法。在main()
方法中,我们又可以启动其他线程 。
继承Thread
从Thread
派生一个自定义类,然后覆写run()
方法:
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 Main { public static void main(String[] args) { //创建线程 MyThread t01 = new MyThread(); MyThread t02 = new MyThread(); MyThread t03 = new MyThread("线程03"); t01.start(); t02.start(); t03.start(); //设置线程名(补救的设置线程名的方式) t01.setName("线程01"); t02.setName("线程02"); // 设置主线程名称 // Thread.currentThread().setName("主线程"); for(int i=0;i<50;i++) { // Thread.currentThread() 获取当前正在执行线程的对象 System.out.println("主线程" + Thread.currentThread().getName() + ":" + i); } } } class MyThread extends Thread { public MyThread() { } public MyThread(String name) { super(name); } //run方法是每个线程运行过程中都必须执行的方法 @Override public void run() { for (int i = 0; i < 50; i++) { System.out.println("子线程" + this.getName() + ":" + i); } } }
打印结果发现:主线程50个,子线程有150个(3*50),可以看出来,主线程在和子线程抢占CPU的过程中,交替打印结果
此处最重要的为start()方法。单纯调用run()方法不会启动线程,不会分配新的分支栈。
start()方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。线程就启动成功了。
启动成功的线程会自动调用run方法(由JVM线程调度机制来运作的),并且run方法在分支栈的栈底部(压栈)。
run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。
单纯使用run()方法是不能多线程并发的。
实现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 30 31 public class demo { public static void main(String[] args) { MyRunnable runnable = new MyRunnable(); //创建Thread对象,并把MyRunnable对象作为Tread类构造方法的参数传递进去 //创建Thread对象,并把MyRunnable对象作为Tread类构造方法的参数传递进去 Thread tread = new Thread(runnable); tread.start(); //通过匿名内部类的方式创建线程 new Thread(new Runnable() { @Override public void run() { for (int i = 1; i <= 50; i++) { System.out.println( "主线程:" + i); } } }).start(); } } class MyRunnable implements Runnable { @Override public void run() { for (int i = 1; i <= 50; i++) { System.out.println( "子线程:" + i); } } }
实现Callable 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 import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;public class TestThread { public static void main (String[] args) throws ExecutionException, InterruptedException { FutureTask<String> futureTask = new FutureTask <>(()->{ for (int i = 0 ; i < 50 ; i++) { System.out.println("子线程1执行中:" +i); } return "线程1执行完毕" ; }); FutureTask<String> futureTask2 = new FutureTask <>(()->{ for (int i = 0 ; i < 50 ; i++) { System.out.println("子线程2执行中:" +i); } return "线程2执行完毕" ; }); new Thread (futureTask).start(); new Thread (futureTask2).start(); System.out.println(futureTask.get()); System.out.println(futureTask2.get()); } }
线程池
方法名
功能
newFixedThreadPool(int nThreads)
创建固定大小的线程池
newSingleThreadExecutor()
创建只有一个线程的线程池
newCachedThreadPool()
创建一个不限线程数上限的线程池,任何提交的任务都将立即执行
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 import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.FutureTask;public class TestThread { public static void main (String[] args) throws ExecutionException, InterruptedException { testFixedThreadPool(); } private static void testFixedThreadPool () { ExecutorService fixThread = Executors.newFixedThreadPool(3 ); for (int i = 0 ; i < 10 ; i++) { int finalI = i; fixThread.execute(new Runnable () { @Override public void run () { try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } Thread.currentThread().setName("线程" ); System.out.println(Thread.currentThread().getName() + " " + finalI); } }); } } }
总结
看了以上四种分析,我们可以清晰的发现了java中其实创建线程的方式就只有一种就是利用Thread+Runnable来实现多线程。其他多有方式都是对这个实现方式的变种。
当然操作线程还有更多的知识,比如设置线程的一些状态、设置线程同步等,更加详细知识参考这里
1 2 3 4 5 6 setPriority # 设置线程优先级 sleep # 让线程进入阻塞状态 join # 当一个线程调用join方法时,该线程会强制抢占cpu资源,直到该线程执行完毕其他线程才会继续执行 yield # 释放cpu资源,让当前线程进入就绪状态,礼让操作,重新和其他线程竞争cpu资源,可能不一定成功 interrupt # 中断线程 setDaemon # 将线程设置为守护线程,用来守护用户线程的,虚拟机只会保证用户线程的执行完毕,守护线程会随着虚拟机的关闭而关闭。