目录
带着问题进行思考:
- 线程的启动
- 如何使线程暂停
- 如何使线程停止
- 线程的优先级
- 线程安全相关的问题
一.程序,线程,进程的概念
程序:一种静态的概念,是指令与数据的集合
进程:它是动态的概念,它是程序的一次执行过程,资源分配和调度的基本单位
。打开任务管理器,可以将运行中的.exe执行文件当做是进程。
线程:一个进程包括多个线程,进程中独立执行的子任务,一个线程只属于某个进程。
tips: 同类的多个线程共享堆和方法区的资源,但是每个线程都有自己的本地方法栈,程序计数器,虚拟机栈
注意: 多线程执行是异步的,切勿将代码中的顺序当做是执行的顺序,线程被调用时是随机的
tips: 同步与异步的区别?
同步:必须处理一件事后才能处理下一件事
异步:处理一件事的同时可以处理其他的事情,之间互不影响
二.线程创建的三种方式
①继承Thread类
public class A extends Thread{
@Override
public void run() {
for(int i = 0; i < 20; i++){
System.out.println("我在看书!!!!" + i);
}
}
public static void main(String[] args) {
A a = new A();
a.start();
for(int i = 0; i < 2000; i++){
System.out.println("我在看代码!!!!" + i);
}
}
}
上述代码执行过程中,也就是说线程准备启动执行的时候,并不是立即执行的而是通过CPU调度进行交替的执行。启动线程的方法是使用start()
方法,使线程处于就绪状态,而不是用run()
方法去启动线程的,run()方法只是调用该线程所关联的执行代码run而已。
小案例:利用多线程实现网上图片的下载
导入common-io包
public class A extends Thread{
private String url;//网络图片的地址
private String name;//保存名字
public A(String url, String name){
this.url = url;
this.name = name;
}
//线程的执行体
@Override
public void run() {
DownLoader downLoader = new DownLoader();
downLoader.down(url, name);
System.out.println("下载了文件"+ name);
}
public static void main(String[] args) {
A a1 = new A("http://img.alicdn.com/imgextra/i1/131300861/O1CN01svWcZd1IENcQxIAaN_!!0-saturn_solar.jpg_220x220.jpg_.webp","1.jpg");
A a2 = new A("http://img.alicdn.com/imgextra/i3/125593559/O1CN01uR4zA81cA4CmNXSmi_!!0-saturn_solar.jpg_220x220.jpg_.webp", "2.jpg");
A a3 = new A("http://img.alicdn.com/imgextra/i2/18674716/O1CN01Bo0iJp1khyS4Yzfj7_!!0-saturn_solar.jpg_220x220.jpg_.webp", "3.jpg");
a1.start();
a2.start();
a3.start();
}
//下载图片
class DownLoader{
public void down(String url, String name){
try{
FileUtils.copyURLToFile(new URL(url), new File(name));
}catch (IOException e){
e.printStackTrace();
System.out.println("IO异常下载出错");
}
}
}
}
②实现Runable接口
public class B implements Runnable {
@Override
public void run() {
for(int i= 0; i < 2000; i++){
System.out.println("1111运行中==>" + i);
}
}
public static void main(String[] args) {
B b = new B();
Thread thread = new Thread(b);
thread.start();
for(int i = 0; i < 3000; i++){
System.out.println("2222运行中==>" + i);
}
}
}
③通过Callable和Future创建线程
/**
* @author jektong
* @Date 2020/6/15-10:17
*/
public class C implements Callable<Integer> {
public static void main(String[] args) {
C c = new C();
FutureTask<Integer> ft = new FutureTask<>(c);
Thread thread = new Thread(ft);
thread.start();
for(int i = 0; i < 1000; i++) {
System.out.println("1111线程" + i);
}
}
@Override
public Integer call() throws Exception {
int i = 0;
for( i = 0; i < 1000; i++)
{
System.out.println("2222线程"+ i);
}
return i;
}
}
补充:有人说线程创建的方式是四种,还有一种就是通过线程池的方式来创建线程。
案例:模拟龟兔赛跑的案列
/**
* @author jektong
* @Date 2020/6/15-13:29
*/
public class Race implements Runnable{
//赢家
private static String winner;
@Override
public void run() {
for(int i = 0; i <= 100; i++){
//让兔子睡觉
if (Thread.currentThread().getName().equals("兔子") && i%10 == 0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
boolean flag = GameOver(i);
if(flag){
break;
}
System.out.println(Thread.currentThread().getName()
+ "-->跑了" + i + "步");
}
}
//谁赢了比赛
public Boolean GameOver(int steps){
if(winner != null){
return true;
}else {
if (steps >= 100){
winner = Thread.currentThread().getName();
System.out.println("winner is " + winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
Race race = new Race();
Thread thread1 = new Thread(race, "乌龟");
Thread thread2 = new Thread(race, "兔子");
thread1.start();
thread2.start();
}
}
三.线程停止
对于线程停止不可使用JDK提供的stop()
与destory()
方法,推荐让线程自己停下来,主要采用以下的方式去停止线程:
使用退出标志,使线程正常退出,也就是当run方法完成后线程终止
通过一个外部的标志位去终止线程,代码如下:
/**
* @author jektong
* @Date 2020/6/15-15:49
*/
public class TestStop implements Runnable {
//线程终止的标志位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag){
System.out.println("thread---run" + i);
}
}
//设置公开的方法终止线程
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for(int i = 0; i < 1000; i++){
System.out.println("main===>" + i);
if(i == 900){
testStop.stop();
System.out.println("此线程停止了");
}
}
}
}
四.线程休眠
- 使用
sleep()
方法休眠线程,括号可以指定时间(毫秒) - sleep存在InterruptedException异常
- sleep时间到达后,线程呈就绪状态
- sleep可以模拟网络延迟,倒计时等
- 每个对象是一个锁,sleep不会释放锁
倒计时案例
/**
* @author jektong
* @Date 2020/6/15-16:41
*/
public class ThreadSleep{
public static void down() throws InterruptedException{
int num = 10;
while (true){
Thread.sleep(1000);
System.out.println(num--);
if(num <= 0){
break;
}
}
}
public static void main(String[] args) {
try {
down();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
五.线程礼让
- 使用
yield()
方法,让当前的线程暂停,但是不会阻塞 - 将线程的状态变成就绪状态
- CPU重新调度此线程,注意礼让不一定成功,要看CPU的状态
/**
* @author jektong
* @Date 2020/6/15-17:15
*/
public class ThreadYeild implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始");
Thread.yield();//礼让线程,当然有可能礼让不成功
System.out.println(Thread.currentThread().getName() + "线程结束");
}
public static void main(String[] args) {
ThreadYeild threadYeild = new ThreadYeild();
new Thread(threadYeild,"A").start();
new Thread(threadYeild,"B").start();
}
}
六.线程强制执行
使用join()
的方法可以实现插队,此线程完成结束后,在执行其他的线程造成阻塞
一个插队小案例
/**
* @author jektong
* @Date 2020/6/15-18:20
*/
public class ThreadJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000 ; i++) {
System.out.println("VIP来了" + i);
}
}
public static void main(String[] args) throws InterruptedException {
ThreadJoin threadJoin = new ThreadJoin();
Thread thread = new Thread(threadJoin);
thread.start();
for (int i = 0; i < 100 ; i++) {
//当主线程运行到i==50的时候阻塞
if(i == 50){
thread.join();//阻塞主线程
}
System.out.println("普通用户来了" + i);
}
}
}
- 假设,有两个线程t1与t2,分别调用了start()方法,在t1后面调用了t1.join()方法
t1.start();
t1.join();
t2.start();
- t1执行完毕后,t2执行
- 如果是t1.join(1000),表示1s内执行t1如果未执行完毕,t2线程进入,与t1共享cpu资源
七.线程状态和线程的生命周期
- NEW(至今未启动的线程)新建状态
- RUNABLE(正在运行的状态) 运行状态
- BLOCKED阻塞状态
- WAITING等待状态
- TIME_WATING超时状态
- TERMINATED线程结束状态
/**
* @author jektong
* @Date 2020/6/15-20:33
*/
public class ThreadState implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5 ; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ThreadState threadState = new ThreadState();
Thread thread = new Thread(threadState);
//观察状态NEW
Thread.State state = thread.getState();
System.out.println(state);
//观察启动后RUN
thread.start();
state = thread.getState();
System.out.println(state);
//只要线程不终止,就一直跑下去
while (state != Thread.State.TERMINATED){
try {
Thread.sleep(1000);
state = thread.getState();
System.out.println(state);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
生命周期:
线程中断或者结束,一旦是死亡状态后就不能再次启动
八.线程的优先级
- 操作系统中将线程划分为各个优先级,优先级别越高,所分配的CPU资源也就越多
- 线程优先级1-10的数字表示
-
Thread.MIN_PRIORITY = 1;
Thread.NORM_PRIORITY = 5;
Thread.MAX_PRIORITY = 10; - 使用
getPriority()
获取优先级,setPriority()
设置优先级 - 优先级高的不一定就是先执行的,只是执行的概率比较大
小测试
/**
* @author jektong
* @Date 2020/6/15-21:42
*/
public class ThreadPriority implements Runnable{
public static void main(String[] args) {
ThreadPriority threadPriority = new ThreadPriority();
Thread thread1 = new Thread(threadPriority);
Thread thread2 = new Thread(threadPriority);
Thread thread3 = new Thread(threadPriority);
Thread thread4 = new Thread(threadPriority);
Thread thread5 = new Thread(threadPriority);
thread1.start();
thread2.setPriority(1);
thread2.start();
thread3.setPriority(4);
thread3.start();
thread4.setPriority(9);
thread4.start();
thread5.setPriority(6);
thread5.start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()
+ "--->" + Thread.currentThread().getPriority());
}
}
输出结果显示
九.守护线程
- 线程分为
用户线程
和守护线程
- 虚拟机必须保证用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 比如后台记录的日志和GC的垃圾回收器都是属于守护线程的只是我们无法察觉到
小测试
/**
* @author jektong
* @Date 2020/6/15-22:20
*/
public class ThreadDem implements Runnable {
@Override
public void run() {
int i = 0;
while (true){
try {
i++;
System.out.println("i===>" + i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ThreadDem threadDem = new ThreadDem();
Thread thread = new Thread(threadDem);
thread.setDaemon(true);//设置为守护线程
thread.start();
try {
Thread.sleep(5000);
System.out.println("运行完毕!thread对象不再打印了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}