要了解线程我们就必须先知道进程,进程是正在运行中的程序,是系统资源分配的基本单位。(单核CPU在同一时刻,只进行一个进程,宏观方面是并行,微观方面是串行)。
线程是进程中的一条执行路径CPU的基本调度单位,一个进程由一个或者多个线程组成,彼此完成不同的工作,同时执行称作多线程。
线程的创建
线程的创建主要有三种方法:
- 继承Thread类,重写Run方法
示例代码:
MyThread:
public class MyThread extends Thread{ //继承Thread类,重写Run方法 // 重写Run方法 @Override public void run() { for(int i=0;i<=10;i++){ System.out.println("这是第"+i+"个线程,名字是"+Thread.currentThread().getName()); } } }
TestMyThread:
public class TestThread { public static void main(String[] args) { // 创建一个新的线程m1 MyThread m1=new MyThread(); // 调用Start方法启动线程 m1.start(); } }
- 实现Runnable接口
代码示例:
MyRunnable类:
public class MyRunnable implements Runnable{ @Override public void run() { for(int i = 0;i<10;i++){ System.out.println("MyRunnnable"+i); } } }
TestRunnable类:
1 public class TestMyRunnable extends Thread{ 2 3 public static void main(String[] args) { 4 5 // 第一种方法,使用MyRunnable创建对象 6 7 // MyRunnable mr=new MyRunnable(); 8 9 // Thread t2=new Thread(mr); 10 11 // t2.start(); 12 13 // 第二种方法,使用匿名内部类优化Runnable 14 15 Runnable r1=new Runnable() { 16 17 @Override 18 19 public void run() { 20 21 for (int i = 0; i < 5; i++) { 22 23 System.out.println(Thread.currentThread().getName() + "..." + i); 24 25 } 26 27 } 28 29 }; 30 31 Thread thread=new Thread(r1,"我的线程"); 32 33 thread.start(); 34 35 36 37 } 38 39 } 40 41 42 43 实现Callable接口:(具体内容后面会出博客) 44 45 package org.dance.day2.future; 46 47 48 49 import org.dance.tools.SleepTools; 50 51 52 53 import java.util.Random; 54 55 import java.util.concurrent.Callable; 56 57 import java.util.concurrent.ExecutionException; 58 59 import java.util.concurrent.FutureTask; 60 61 /** 62 63 * Callable的两种执行方式 64 65 * @author ZYGisComputer 66 67 */ 68 69 public class UseCallable { 70 71 72 73 /** 74 75 * 实现Callable接口的线程 76 77 */ 78 79 private static class UseCall implements Callable<Integer>{ 80 81 82 83 private int sum; 84 85 86 87 @Override 88 89 public Integer call() throws Exception { 90 91 System.out.println("callable子线程开始执行任务计算"); 92 93 Thread.sleep(2000); 94 95 for (int i = 0; i < 5000; i++) { 96 97 sum += i; 98 99 } 100 101 System.out.println("子线程任务计算完成,返回值:"+sum); 102 103 return sum; 104 105 } 106 107 } 108 109 110 111 public static void main(String[] args) throws ExecutionException, InterruptedException { 112 113 114 115 UseCall useCall = new UseCall(); 116 117 118 119 // 使用FutureTask包装 120 121 FutureTask<Integer> futureTask = new FutureTask<>(useCall); 122 123 124 125 // 包装为Thread 126 127 Thread thread = new Thread(futureTask); 128 129 130 131 thread.start(); 132 133 134 135 // 开始主线程的任务 136 137 Random random = new Random(); 138 139 140 141 SleepTools.second(1); 142 143 144 145 if(random.nextBoolean()){ 146 147 System.out.println("获取Callable result:"+futureTask.get()); 148 149 }else{ 150 151 System.out.println("中断计算"); 152 153 // 中断计算,取消线程的执行 154 155 futureTask.cancel(true); 156 157 } 158 159 } 160 161 }
线程的组成
- 单核时代:一个CPU的工作时间分成很小的时间片,再把时间片分给不同的线程。【CPU时间片:操作系统(OS):会给每个线程分配时间】
- 多核时代:不同CPU分别执行不同线程
- 运行数据:1)堆:存储线程所需使用的对象,多个线程可共享堆中的对象
2)栈:局部变量,每个线程都有独立的栈
线程的状态
1) 线程的守护
语法:.setDeamon(true)设置为守护线程
特性:前台线程执行完后后台守护线程会自动结束,举例来讲Java中的垃圾回收器功能就是一个守护线程,当jvm虚拟机启动后,垃圾回收器会自动开始启动(System.gc)当关闭时也会自动跟随关闭。
代码示例:
Deamon类:
public class Deamon extends Thread{ @Override public void run() { for(int i=0;i<=10;i++){ System.out.println(Thread.currentThread().getName()+"==="+i); try { Thread.sleep(520); } catch (InterruptedException e) { e.printStackTrace(); } } } }
TestDeamon:
public class TestDeamon { public static void main(String[] args) { //创建线程,默认前台线程 Deamon d1=new Deamon(); //设置线程为安全线程 d1.setDaemon(true); d1.start(); for(int i=0;i<=10;i++){ System.out.println(Thread.currentThread().getName()); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } }
2) 线程休眠
Public static void sleep(long millis)
语法:Thread.Sleep(时间)[public static void sleep(long millis)]
注意,此处存在抛出异常,父类未抛出,子类的范围比父类小,应当同样不可抛出
代码示例:
ThreadSleep类:
public class ThreadSleep extends Thread{ @Override public void run() { for(int i=0;i<10;i++){ System.out.println(Thread.currentThread().getName()+"....."+i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }
Test:
public class TestSleep { public static void main(String[] args) { ThreadSleep s1=new ThreadSleep(); s1.start(); ThreadSleep s2=new ThreadSleep(); s2.start(); } }
3)线程放弃、避让
当前线程主动放弃时间片回到就绪状态,竞争下一个时间片
Public static void yield()
语法:Thread.yield()
代码案例说明:
线程是抢占式的执行方式,我们创建两个线程,其大概率上不会出现接替执行的情况,在这里,我们让每一个线程执行完之后都在最后执行Yield()方法,若运行结果同样大部分都是接替执行则可以证明yield方法。
代码示例:
YieldThread:
public class YieldThread extends Thread{ @Override public void run() { for(int i=0;i<10;i++){ System.out.println(Thread.currentThread().getName()+"..."+i); // 主动放弃CPU Thread.yield(); } } }
TestYield类:
public class TestYield { public static void main(String[] args) { YieldThread y=new YieldThread(); YieldThread y2=new YieldThread(); y.start(); y2.start(); } }
4) 线程加入
加入当前线程,并阻塞当前线程直到加入线程执行完毕之后。
涉及的语法:
public final void join()允许其他线程加入到当前线程之中
- 注意:Join方法需要使用try..catch语句否则抛出异常
- 休眠和放弃是静态方法,使用类来调用。Join不是静态方法需要使用对象点方法名来调用。
线程加入的含义:只有等加入的线程执行完毕后才可以继续执行原有的线程。
代码案例说明:
代码示例:
JoinThread:
public class JoinThread extends Thread{ @Override public void run() { for(int i=0;i<=50;i++){ System.out.println(Thread.currentThread().getName()+"....."+i); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } } }
TestJoin:
public class TestJoin { public static void main(String[] args) { JohnThread j1=new JohnThread(); j1.start(); try { j1.join(); } catch (InterruptedException e) { e.printStackTrace(); } //for主线程 for(int i=0;i<20;i++){ System.out.println(Thread.currentThread().getName()+"******"+i); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } } }
5) 线程优先级
语法:
线程对象.setpriority()
线程优先级分1-10级,对于未设置线程优先级的线程来说,默认的线程优先级是5.优先级级别越高则获取CPU的机会越多,掌握时间片的机会就越大。
代码示例说明:
线程会按照start方法启动的顺序启动线程,在这里我们先启动的是第一线程,而后依次是第二线程、第三线程。但是第三线程的优先级最大,第二线程的优先级次之,第一线程的优先级最次,如此若是每次执行完代码后结束的大概率是第一线程则可以证明优先级设置有效。
代码示例:
PriorityThread:
public class PriorityThread extends Thread{ @Override public void run() { for(int i=0;i<10;i++){ System.out.println(Thread.currentThread().getName()+"......"+i); } } }
TestPriority类:
public class TestPriority { public static void main(String[] args) { PriorityThread p1=new PriorityThread(); PriorityThread p2=new PriorityThread(); PriorityThread p3=new PriorityThread(); // 给线程命名 p1.setName("第一线程"); p2.setName("第二线程"); p3.setName("第三线程"); p1.setPriority(2); p2.setPriority(6); p3.setPriority(10); p1.start(); p2.start(); p3.start(); } }