java10_多线程
多线程
java.Thread
—线程简介
-多任务:
- 边吃饭边看手机
- 疯狂司机:开车+打电话+吊点滴
- 上厕所玩手机
现实中太多这样同时做多件事情的例子了,看起来是多个任务都在做,其实本质上我们的大脑在同一时间依旧只做了一件事情。
-进程Process:
-程序:程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
-进程:进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位。
-通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程(main主线程),不然没有存在的意义。线程是CPU调度和执行的单位。
-线程Thread:
-独立执行的路径。
-在程序运行时,即使自己没有创建线程,后台也会有多个线程,如主线程(用户线程),gc线程(守护线程:垃圾回收(Garbage Collection))。
-很多的多线程是模拟出来的,真正的多线程是指有多个CPU,即多核,如服务器。如果是模拟出来的多线程,即在一个CPU的情况下,在同一个时间点,CPU只能执行一个代码,因为切换的很快,所以就有同时执行的错觉(如同进行多个任务时的人脑)。
-多线程:
-原来有一条道路,慢慢因为车太多,道路堵塞效率极低。
为了提高使用效率,能够充分利用道路,于是加了多个车道。
-看视频的时候,可以同时看画面,听声音,看弹幕等等,这说明在在同一个进程里有很多 多个线程。
-在一个进程中,开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧 密相关的,先后顺序是不能人为干预的。
-对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制。
-线程会带来额外的开销,如CPU调度时间,并发控制开销。
-每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。
—线程实现
线程创建:
-三种创建方式:Thread,Runnable,Callable。
-Thread class 继承
(重点)
- 自定义线程类继承Thread类。
- 重写run()方法,编写线程执行体。
- 创建线程对象,调用start()方法,启动线程。
package 史前蓝熊.Thread;
//创建线程的方式之一,继承Thread类
//步骤:继承Thread类,重写run()方法,调用start()方法。
public class Thread01 extends Thread{
//重写run()方法
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码"+i);
}
}
public static void main(String[] args) {
//创建一个线程对象
Thread01 testthread1 = new Thread01();
//调用start()方法,开启线程
testthread1.start();//线程不一定立即执行,CPU安排调度。
//main线程,主线程
for (int i = 0; i < 20; i++) {
System.out.println("我在学习主线程"+i);
}
}
}
结果:
我在学习主线程0
我在学习主线程1
我在学习主线程2
我在学习主线程3
我在学习主线程4
我在学习主线程5
我在学习主线程6
我在学习主线程7
我在学习主线程8
我在学习主线程9
我在学习主线程10
我在学习主线程11
我在学习主线程12
我在看代码0
我在看代码1
我在看代码2
我在看代码3
我在看代码4
我在看代码5
我在看代码6
我在学习主线程13
我在学习主线程14
我在学习主线程15
我在看代码7
我在学习主线程16
我在学习主线程17
我在看代码8
我在看代码9
我在看代码10
我在看代码11
我在看代码12
我在看代码13
我在看代码14
我在看代码15
我在看代码16
我在学习主线程18
我在看代码17
我在看代码18
我在看代码19
我在学习主线程19//交错着进行
- 调用run()方法与调用start()方法的区别:
-调用run()方法相当于,直接在进程里面加了一段程序。
-调用start()方法相当于,开辟出了一个子线程,与main主线程抢夺CPU资源。
-Runnable接口
(超重点)
- 定义MyRunna类实现Runnable接口。
- 实现run()方法,编写线程执行体。
- 创建线程对象,调用start()方法启动线程。
package 史前蓝熊.Thread;
//创建线程方式二
//实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法。
public class Runnable01 implements Runnable{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码"+i);
}
}
public static void main(String[] args) {
//创建runnable接口的实现类对象
Runnable01 runnable01 = new Runnable01();
//创建线程对象,通过线程对象来开启线程。(代理)
/*
Thread thread = new Thread(runnable01);
thread.start();
简化为下面的一行代码。
*/
new Thread(runnable01).start();
//main线程,主线程
for (int i = 0; i < 20; i++) {
System.out.println("我在学习主线程"+i);
}
}
}
结果:
我在学习主线程0
我在学习主线程1
我在学习主线程2
我在学习主线程3
我在学习主线程4
我在学习主线程5
我在学习主线程6
我在学习主线程7
我在学习主线程8
我在学习主线程9
我在学习主线程10
我在学习主线程11
我在学习主线程12
我在学习主线程13
我在学习主线程14
我在学习主线程15
我在学习主线程16
我在学习主线程17
我在学习主线程18
我在学习主线程19
我在看代码0
我在看代码1
我在看代码2
我在看代码3
我在看代码4
我在看代码5
我在看代码6
我在看代码7
我在看代码8
我在看代码9
我在看代码10
我在看代码11
我在看代码12
我在看代码13
我在看代码14
我在看代码15
我在看代码16
我在看代码17
我在看代码18
我在看代码19
—继承Thread类与实现Runnable接口的对比:
-继承Thread类
- 子类继承Thread类具备多线程能力
- 启动线程:子类对象.start()
- 不建议使用:避免OOP单继承局限性
-实现Runnable接口
- 实现接口Runnable具备多线程的能力
- 启动线程:传入目标对象+Thread对象.start()
- 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用。
资源抢夺:
对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制。
package 史前蓝熊.Thread;
//多个线程同时操作同一个对象
//买火车票的例子
//发现问题:多个线程操作同一个资源的时候,线程不安全,线程紊乱。
public class Runnable02 implements Runnable{
//票数
private int ticketNums = 10;
@Override
public void run() {
while(true){
if(ticketNums<=0){
break;
}
//Thread.currentThread().getName()获得当前线程的名字
System.out.println(Thread.currentThread().getName()+"拿到了第"+(ticketNums--)+"张票");
}
}
public static void main(String[] args) {
Runnable02 runnable02 = new Runnable02();
new Thread(runnable02 ,"①").start();
new Thread(runnable02 ,"②").start();
new Thread(runnable02 ,"③" ).start();
}
}
结果:
②拿到了第10张票
②拿到了第8张票
②拿到了第7张票
②拿到了第6张票
③拿到了第9张票
①拿到了第10张票
③拿到了第4张票
②拿到了第5张票
③拿到了第2张票
①拿到了第3张票
②拿到了第1张票
龟兔赛跑:
package 史前蓝熊.Thread;
//模拟龟兔赛跑
public class Thread_HareAndTortoise implements Runnable{
private static String winner;
@Override
public void run() {
for (int i = 0; i <=100; i++) {
//判断比赛是否结束。
System.out.println(Thread.currentThread().getName()+"——》跑了"+i+"步");
boolean flag = gameOver(i);
if(flag){
break;
}
//模拟兔子休息
if(Thread.currentThread().getName().equals("兔子")&& i%10==0){
try{
Thread.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private boolean gameOver(int steps){
if(winner !=null ){
//已经有胜利者
return true;
}{
if (steps >=100){
winner = Thread.currentThread().getName();
System.out.println("Winner is"+winner);
}
}
return false;
}
public static void main(String[] args) {
Thread_HareAndTortoise thread_hareAndTortoise = new Thread_HareAndTortoise();
new Thread(thread_hareAndTortoise,"兔子").start();
new Thread(thread_hareAndTortoise,"乌龟").start();
}
}
结果:
乌龟——》跑了0步
兔子——》跑了0步
乌龟——》跑了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步
兔子——》跑了1步
乌龟——》跑了96步
兔子——》跑了2步
兔子——》跑了3步
乌龟——》跑了97步
兔子——》跑了4步
乌龟——》跑了98步
乌龟——》跑了99步
乌龟——》跑了100步
兔子——》跑了5步
Winner is乌龟//停滞不前,终会平庸
-Callable接口
(了解,工作需要)