多线程
1.多线程定义
- 每一个应用程序开启后都会开启一个进程,一个进程中可以开启多个线程完成不同的功能,比如qq开启一个进程,qq可以同时聊天、听音乐、看qq空间就是开启了多个线程来完成不同的任务。
- CPU在某一个时间片只能处理一个任务(线程),如果没有实现多线程,你的qq就应该先执行聊天任务,聊天任务结束才可以听音乐、听音乐任务结束后才可以打开qq空间,我们可以开启多线程,让cpu交替去执行这3个任务,由于cpu计算能力很强,让你感觉这3个任务在同时进行,这样的好处是提高了用户的体验度
- 总结:有1,2,3个任务,cpu要一件完成之后再去做之后的事情,但是通过多线程实现可以让cpu交替去执行1,2,3任务,让用户在视觉上觉得这三件任务,是在同时执行,提高了用户的体验度。
2.多线程的应用
- 多线程在java中有着举足轻重的作用,大量应用于服务开发和网络编程。小到自己的程序,大到底层系统的原理,都存在多线程的应用。比如吃鸡游戏,同一个界面多个对战,一个电商网站如淘宝上百万人同时访问,也是多线程实现的,比如12306购票网站,双11天猫抢购都应用了多线程。
- 负载均衡,物理服务器、云服务器:华为、百度、腾讯、阿里
- 这些网站和应用都是目前最流性的三高代表:1.高可用 2.高性能 3.高并发
- 通过多线程我们可以实现高并发
3.并行和并发的区别
- 并行:指两个或多个事件在同一时刻发生(同时发生)
- 并发:指两个或多个事件在同一时间段内发生
- 多线程就是在同一个时间段内多个线程并发执行
4.多线程的好处:
- 多线程提高了用户的体验度,使程序的响应速度更快。
- 占用大量处理时间的任务可以使用多线程来提高cpu的利用率.即占用大量处理的任务可以通过线程的调度方法让其他任务执行
- 多线程可以分别设置优先级来优化性能
5.线程的生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cMJWn2cE-1602504482161)(E:\图片\images\xian.png)]
- 线程的生命周期包含5个阶段:包括新建、就绪、运行、阻塞、销毁
- 新建:在编译期间创建线程对象属于新建状态,如:Thread t1 = new Thread(myRunnable,“线程A”);
- 就绪状态:当调用t1.start()方法后就属于就绪状态,这时候线程处于等待cpu执行此线程任务,谁先抢到cpu资源谁就开始执行,是随机性的。
- 运行状态:当就绪状态的线程获得cpu资源执行任务就是运行状态。run方法中用于定义线程的操作任务
- 阻塞状态:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如sleep()、wait()等方法让该线程处于阻塞状态,这个时候其他的线程CPU的资源可能性变得更大,CPU并不会等待阻塞的线程(任务),会去执行其他的线程(任务),也可以通过其他机制让阻塞状态的线程唤醒,比如调用notify()或者notifyAll()方法让阻塞的线程恢复就绪状态,直到再次等待CPU分配资源进入运行状态。
- 销毁状态:如果线程正常执行完毕后或者线程被提前强制的终止或出现异常导致结束,就属于销毁状态,线程的资源释放
- 具体参考下面线程调度
6.实现线程
- 实现多线程有4种方式:
- 继承Thread父类重写run()方法
- 实现Runable接口重写run()方法
- 实现callable接口
- 线程池实现多线程
- 主线程
- 当java 程序启动时,一个线程立刻执行,该线程叫做主线程(main Thread).因为它是程序开始就执行的。
- 主线程的作用
- 它是产生其他子线程的线程。
- 通常它必须最后完成执行,因为它需要执行各种关闭操作。
-
开启主线程
/** * 开启主线程 * * */ public class ThreadTest { public static void main(String[] args) { //返回当前的线程对象,main方法就是主线程 Thread t = Thread.currentThread(); System.out.println(t.getName()); t.setName("myThread");//设置线程名称 System.out.println(t.getName()); } }
-
主线程与子线程交替
-
子线程
/** * extends Thread 重写run()方法来创建子线程 * @author Administrator * */ public class MyThread extends Thread{ @Override public void run() { for (int i = 0; i <= 10; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } }
-
主线程
/** * extends Thread 重写run()方法来创建子线程 * @author Administrator * */ public class MyThread extends Thread{ @Override public void run() { for (int i = 0; i <= 10; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } }
-
结果
Thread-0:0 Thread-0:1 Thread-0:2 Thread-0:3 Thread-0:4 Thread-0:5 Thread-0:6 main()主线程执行了:1600398861751 main()主线程执行了:1600398861751 main()主线程执行了:1600398861751 main()主线程执行了:1600398861751 main()主线程执行了:1600398861751 Thread-0:7 main()主线程执行了:1600398861751 main()主线程执行了:1600398861752 main()主线程执行了:1600398861752 main()主线程执行了:1600398861752 main()主线程执行了:1600398861752 Thread-0:8 main()主线程执行了:1600398861752 Thread-0:9 Thread-0:10
-
-
通过继承Thread父类继承多线程
/** * extends Thread 重写run()方法来创建子线程 * @author Administrator * */ public class MyThread extends Thread{ @Override public void run() { //线程的任务放在run()方法中执行 for (int i = 0; i <= 10; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } }
测试
public class Test { public static void main(String[] args) { MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); t1.setName("线程A"); t2.setName("线程B----------"); t1.start(); t2.start(); } }
因为java是单继承,如果需要实现多线程的类已经继承了其他的父类那么就不能够继承Thread父类,所以建议使用实现Runable接口来实现多线程。
-
通过实现Runable接口来实现多线程
/** * 通过实现Runnable接口来实现多线程 * @author Administrator * */ public class MyRunable implements Runnable{ @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i <= 1000; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } }
测试
public class Test { public static void main(String[] args) { MyRunable myRunnable = new MyRunable(); Thread t1 = new Thread(myRunnable,"线程A"); Thread t2 = new Thread(myRunnable,"--线程B---"); t1.start(); t2.start(); } }
-
利用多线程下载图片资源
import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import org.apache.commons.io.FileUtils; public class WebDownloader { /** * 下载图片 * @param url * @param name */ public void downloader(String url, String name) { try { FileUtils.copyURLToFile(new URL(url), new File(name)); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println("不合法的url"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println("下载失败!"); } } }
测试
/** * 多任务下载图片 * @author Administrator * */ public class TestDownloader extends Thread{ private String url;//下载地址 private String name;//下载名称 public TestDownloader(String url, String name) { super(); this.url = url; this.name = name; } @Override public void run() { //把任务放在run方法实现多线程(多任务)交替执行 WebDownloader wd = new WebDownloader(); wd.downloader(url,name); System.out.println("下载图片"+name); } public static void main(String[] args) { TestDownloader t1 = new TestDownloader("https://image.baidu.com/", "t1.png"); TestDownloader t2 = new TestDownloader("https://image.baidu.com/", "t2.png"); TestDownloader t3 = new TestDownloader("https://image.baidu.com/", "t3.png"); t1.start(); t2.start(); t3.start(); } }
-
通过匿名内部类实现
public class Test { public static void main(String[] args) { //通过匿名内部类实现多线程 new Thread() { public void run() { for (int i = 0; i <=100; i++) { System.out.println("aaa"); } } }.start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i <=100; i++) { System.out.println("bbb==============="); } } }).start(); } }
-
实现Callable接口实现多线程
-
Callable接口必须返回一个类型的结果
import java.util.concurrent.Callable; public class MyThread implements Callable<Boolean>{ @Override public Boolean call() throws Exception { for (int i = 0; i <= 10; i++) { try { System.out.println(Thread.currentThread().getName()+"正在执行"+i); } catch (Exception e) { e.printStackTrace(); return false; } } return true; } }
-
测试
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import day02.MyThread; public class Test { public static void main(String[] args) throws InterruptedException, ExecutionException { // Thread t1 = new Thread(new MyThread(),"线程A"); // Thread t2 = new Thread(new MyThread(),"线程B"); MyThread t1 =new MyThread(); MyThread t2 =new MyThread(); //通过线程池创建两个线程 ExecutorService ser = Executors.newFixedThreadPool(2); //提交执行 Future<Boolean> result1 = (Future<Boolean>)ser.submit(t1); Future<Boolean> result2 = (Future<Boolean>)ser.submit(t2); //实现implements Callable<?>和之前的不一样,这个接口的方法执行线程并且返回一个结果值 boolean r1 = result1.get(); boolean r2 = result2.get(); System.out.println(r1); System.out.println(r2); //4.关闭服务 ser.shutdown(); } }
-
7.线程调度
- 线程调度指按照特定的机制为多个线程分配CPU的使用权
方法 | 说明 |
---|---|
setPriority(int newpriority) | 设置线程的优先级 |
static void sleep(long millis) | 在指定的毫秒数内让当前正在执行的线程休眠 |
void join() | 加入,让正在执行的线程停止,让本线程强制性加入 |
static void yield() | 礼让,暂停当前正在执行的线程,让其他线程执行 |
void interupt() | 中断线程 |
boolean isActive() | 测试线程是否处于活动状态 |
-
设置优先级
public class MyThread implements Runnable{ @Override public void run() { for (int i = 0; i <=100; i++) { System.out.println(Thread.currentThread().getName()+"正在运行"+i); } } public static void main(String[] args) { Thread t1 = new Thread(new MyThread(),"线程A"); Thread t2 = new Thread(new MyThread(),"线程B====="); //t1.setPriority(10); //t2.setPriority(1); //设置优先级后,理论上t1比t2先执行 t1.setPriority(Thread.MAX_PRIORITY);//设置t1优先级最大 t2.setPriority(Thread.MIN_PRIORITY);//设置t2优先级最小 t1.start(); t2.start(); //获取优先级 int pri = t1.getPriority(); } }
-
线程休眠 sleep()
public class ThreadSleepDemo { public static void main(String[] args) { ThreadSleepDemo t = new ThreadSleepDemo(); t.sleepTime(5); } private void sleepTime(int time) { for (int j = 1; j < time; j++) { System.out.println("主线程执行了"+j+"秒"); try { //让线程休眠1秒 Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
线程休眠后就属于阻塞状态,其他线程就会抢占cpu资源。
-
join() 强制加入
子线程
public class MyThread implements Runnable{ @Override public void run() { for (int i = 1; i <= 10; i++) { System.out.println(Thread.currentThread().getName()+"运行了:"+i); } } }
测试
public class Test { public static void main(String[] args) { Thread t = new Thread(new MyThread()); t.start(); for (int i = 0; i <= 10 ;i++) { //当主线程执行第5个任务的时候,让子线程t强行加入进行执行 if(i==5) { try { //让子线程强制抢占cpu资源,执行完了之后再给主线程main执行 t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+"运行"+i); } } }
-
线程礼让yield()
子线程
public class MyThread implements Runnable{ @Override public void run() { for (int i = 1; i <= 100; i++) { System.out.println(Thread.currentThread().getName()+"正在运行了:"+i); if(i==3) { //线程礼让,让其他线程先执行 Thread.yield(); } } } }
测试
public class Test { public static void main(String[] args) { Thread t1 = new Thread(new MyThread(),"子线程A"); Thread t2 = new Thread(new MyThread(),"子线程B=========="); t1.start(); t2.start(); } }