1.创建线程的方式:以下创建线程的方式,本质上都是相同的,都要借助Thread类,在内核中创建出新的PCB,加入到内核中的双向链表中,只不过是描述任务的主体不一样;

runnable字面意思,用于描述一个任务;线程的主体

(1)通过显示继承thread类的方式来实现,或者匿名内部类;
Thread t1=new Thread(){ @Override public void run() { System.out.println(1); }
}; t1.start();
(2)通过显示创建一个类,实现Runnable接口,然后再把这个继承runnable的实例化对象关联到Thread的实例上
public class Hello { static class myrunnable implements Runnable { public
void run() { System.out.println("我是线程"); } } public static void main(String[]
args) throws InterruptedException{ Thread t1=new Thread(new myrunnable());
t1.start(); }}
3
通过runnable匿名内部类(一个没有匿名的内部类,这只需要创建出一个实例就行了,这需要用这个实例,不需要再用其他实例了,这种写法和单独创建一个Thread类,再继承Thread没有任何区别)的方式创建一个线程,重写run方法,在直接把这个Runnable类创建的对象关联到Thread里面
public class Hello { public static void main(String[] args) throws
InterruptedException{ Runnable myrunnable=new Runnable(){ public void run() {
System.out.println("我是一个线程"); } }; Thread t1=new Thread(myrunnable);
t1.start(); }} // Thread thread =new Thread(new Runnable() { @Override public
void run() { System.out.println("我是一个线程"); } }); thread.start(); }
4 通过lamda的表达式的方式创建一个线程
public class Hello { public static void main(String[] args) throws
InterruptedException{ Thread t1=new
Thread(()->{System.out.println("我是一个线程");}); t1.start(); 函数式接口 }}
面试题:thread类中的run与start方法中的区别

答:

当我们点击程序运行的时候,首先系统会创建出一个进程,这个线程执行的代码,就是main方法,系统中原来就有很多PCB,执行代码,就会创建出一个·PCB,通过链表与其他的PCB进行连接,此时这个PCB代表的就是main方法;
但是此时执行了t.start(),就会再次创建出一个PCB,执行t.start()的时候,就会把这个PCB挂到链表上面(这两个PID是相同的,此时在这个PCB里面就会自动调用咱们的run()方法;
t.run(),不会创建出现的线程,此时也会在系统中创建出一个PCB,这个PCB是代表main函数,在这个PCB中会调用这个main方法,这个过程并不会新创建线程

调用start方法可以直接启动线程,并使线程进入就绪,当run方法执行完了,线程,也就结束了。但是如果直接执行run方法,会当作普通方法来调用,不会创建一个新线程;

2.为了理解多线程和join的用法,来写几个代码看看

1.我们先看单线程的执行效果

注释:sleep就是暂时让线程放弃CPU
public class Hello { public static void main(String[] args){ long
beg1=System.currentTimeMillis(); int a=0; for(long i=0;i<1000000000;i++) { a++;
} int b=0; for(int j=0;j<1000000000;j++) { b++; } long
beg2=System.currentTimeMillis(); System.out.println("执行时间为");
System.out.println(beg2-beg1); }}
576

2 再看多线程的执行效果
class Hello{ public static void main(String[] args) throws
InterruptedException{ long beg1=System.currentTimeMillis(); Thread t1=new
Thread(){ public void run(){ long a=0; for(long i=0;i<1000000000;i++) { a++; }
} }; Thread t2=new Thread(){ public void run(){ long b=0; for(long
j=0;j<1000000000;j++) { b++; } } }; t1.start(); t2.start(); t1.join();
t2.join(); long beg2=System.currentTimeMillis(); System.out.println("执行时间为");
System.out.println(beg2-beg1); } }
    374

由此我们可知:多线程会比单线程效率更高

join作用是:让线程1和线程二执行完毕后再执行主线程(没加join之前,线程1,线程2,和主线程是同时执行的);也就是说
此时的join是让主线程阻塞,t1,t2是并发执行的

但是如果这样
t1.start(); t1.join(); t2.start(); t2.join();
这时 t1,t2 是串行执行的。

3.其他内容补充:

1,主线程还是一直向下走,但是新线程会执行RUN方法,对于主线程来说,run方法执行完了,新线程就结束了,对于主线程来说,main方法执行完,主线程就结束了

2,线程之间,是并发执行的关系,谁先执行,谁后执行,谁执行到哪里让出CPU,都是不确定的,作为程序员是无法感知的,全权有操作系统的内核负责。例如当创建一个新线程的时候,接下来是主线程先执行,还是新线程,是不好保证的。

3.执行join方法的时候,该线程会一直阻塞,一直阻塞到对应线程结束后,才会继续执行,本质上来说是为了控制线程执行的先后顺序,而对于sleep来说,谁调用谁就会阻塞;

4,主线程把任务分成几份,每个线程计算自己的一份任务,当所有的任务被计算完毕后,主线程再来汇总(就必须保证主线程是最后执行完的线程)。

5 获得当前对象的引用 Thread.currentThread()

6
如果线程正在运行,执行计算其逻辑,此时就在就绪队列排序呢,调度器就会在就绪队列找出合适的PCB让他在CPU执行,如果某个线程调用Sleep就会让对应的PCB进入阻塞队列,无法上CPU;

7 对于sleep让其进入阻塞队列的时间是有限制的,时间到了之后,就会被系统把PCB那回到原来的就绪队列中了;

8 join被恢复的条件是对应的线程结束。

我们所说的共享资源主要指的是两个方面
1)内存,线程一和线程二,都可以共享同一块内存(同一个变量) 2)文件,线程一打开的文件,线程二也可以去使用
本质上来说,变量就是内存,两个线程可以访问同一个变量,说明这两个线程在使用同一个内存,但是对于多进程来说,进程一就不可以访问进程二的变量
Thread类中的常见用法,Thread类是用于管理线程的一个类,换句话来说,每一个线程都有唯一的Thread类进行关联

Thread的常见构造方法
Thread() 创建线程对象 Thread(Runnable target) 借助Runnable对象创建线程对象 Thread(String
name),创建线程对象,并命名; Thread(Runnable target,String name)通过runnable来进行创建线程对象,并且进行命名
有名字的构造方法就是为了方便调试

技术
©2019-2020 Toolsou All rights reserved,
TypeScript:函数类型接口8道大厂指针笔试题让你秒杀指针!!!MySQL 日期时间加减mysql 查询条件之外的数据_mysql 查询符合条件的数据查linux的操作系统版本,如何查看Linux操作系统版本?将String类型转换成Map数据类型使用uuid做MySQL主键,被老板,爆怼一顿C语言中的字符串函数和字符函数linux服务器中毒排查--基础篇C# ASCII码字符转换