另外这里面还夹着一个字符串 “main”,这是 System.out.println(Thread.currentThread().getName());语句的输出结果
图片的下面是Thread-2,这个是线程对象m5的名字,我故意没有给m5赋名,这里就可以看出来,如果自己不命名,则系统会自动给一个名字。
1、创建自定义类实现Runnable接口
2、重写run()方法
3、创建自定义类的对象
4、创建Thread类的对象,将第三步创建的自定义类对象作为参数传递到构造方法中
package test.MyThread;
public class MyRunnable implements Runnable {
@Override
public void run(){
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+"—"+i);
}
}
}
关于Thread.currentThread().getName()这条语句的解释:
由于实现的Runnable接口中并没有getName()方法,所以这里无法使用Thread类中的getName()方法
此时如果我们获取当前线程的名字,就需要间接调用,即通过currentThread()获取当前正在执行run()方法的线程对象,再通过getName()获取线程名字
currentThread()获得的线程对象是Thread类型,因此可以用getName()方法
package test.MyThread;
public class Demo3 {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
t1.setName(“张”);
t2.setName(“陈”);
t1.start();
t2.start();
}
}
通过Runnable接口实现的类可以继承其他类,继承Thread的类不行。可以避免由于Java单继承带来的局限性。
Runnable适合多个相同程序的代码去处理同一个资源的情况,比如
package test.MyThread;
public class RunnableThread implements Runnable{
int num = 100;
@Override
public void run(){
while(true){
if(num>0){
System.out.println(Thread.currentThread().getName()+"—"+(num–));
}
}
}
}
package test.MyThread;
public class Test1 {
public static void main(String[] args) {
RunnableThread rt = new RunnableThread();
Thread t1 = new Thread(rt,“窗口一”);
Thread t2 = new Thread(rt,“窗口二”);
Thread t3 = new Thread(rt,“窗口三”);
t1.start();
t2.start();
t3.start();
}
}
三个窗口,也就是三个线程是共用一个变量num的,即三个窗口卖票的总和是100(这里的num是票数),三个线程共用一个RunnableThread类的变量num
package test.MyThread;
public class ExtendsThread extends Thread {
int num = 100;
@Override
public void run(){
while(true){
if(num>0){
System.out.println(getName()+"—"+(num–));
}
}
}
}
package test.MyThread;
public class Test1 {
public static void main(String[] args) {
ExtendsThread e1 = new ExtendsThread();
ExtendsThread e2 = new ExtendsThread();
ExtendsThread e3 = new ExtendsThread();
e1.setName(“窗口一”);
e2.setName(“窗口二”);
e3.setName(“窗口三”);
e1.start();
e2.start();
e3.start();
}
}
这里可以看出来,三个线程,也就是三个窗口各有一百张
《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》
【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享
票,总共三百张票。每个线程都有一个自己独自的ExtendsThread类的实例变量num
这里如果想让extends继承产生的线程也共用一个变量,就需要将int num = 100改为
public static int num = 100;
因此总结一下是:Runnable可以实现多个相同的程序代码的线程去共享同一个资源,而Thread虽然可以做到但不推荐
获取线程的优先级
public final int getPriority()返回此线程的优先级。
设置线程的优先级
public final void setPriority(int newPriority)更改此线程的优先级, 参数newPriority的范围为1-10之间
注意:
线程的默认优先级为5
线程优先级的范围是1-10
线程优先级高仅仅表示的是获取CPU时间片的几率会高一些,但不代表优先级高的线程一定能抢到CPU
package test.MyThread;
public class MyPriority extends Thread {
@Override
public void run(){
for(int i=0;i<10;i++){
System.out.println(getName()+"—"+i);
}
}
}
package test.MyThread;
public class Demo4 {
public static void main(String[] args) {
MyPriority m1 = new MyPriority();
MyPriority m2 = new MyPriority();
//获取m1和m2的优先级
System.out.println(m1.getPriority());
System.out.println(m2.getPriority());
//设置m1和m2的优先级
m1.setPriority(1);
m2.setPriority(10);
//启动线程
m1.start();
m2.start();
}
}
可以看出来线程m2抢到CPU的概率高了很多
加入线程:
public final void join():其他线程等待这个线程死亡
注意事项:
在线程设置为加入线程之前,先将该线程变为就绪状态,也就是调用start()方法
package test.MyThread;
public class JoinThread extends Thread{
@Override
public void run(){
for(int i=0;i<10;i++){
System.out.println(getName()+"—"+i);
}
}
}
package test.MyThread;
public class Demo5 {
public static void main(String[] args) {
JoinThread j1 = new JoinThread();
JoinThread j2 = new JoinThread();
JoinThread j3 = new JoinThread();
j1.setName(“张”);
j2.setName(“周”);
j3.setName(“陈”);
j1.start();
try {
j1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
j2.start();
j3.start();
}
}
可以看出来,等到线程j1全部运行结束后,j2和j3才正常抢占cpu运行程序
package test.MyThread;
public class SleepThread extends Thread{
@Override
public void run(){
for(int i=0;i<10;i++){
System.out.println(getName()+"—"+i);
try {
//sleep(long millis)millis是毫秒数,一秒等于1000毫秒
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package test.MyThread;
public class Demo6 {
public static void main(String[] args) {
SleepThread s1 = new SleepThread();
SleepThread s2 = new SleepThread();
SleepThread s3 = new SleepThread();
s1.setName(“张”);
s2.setName(“周”);
s3.setName(“陈”);
s1.start();
s2.start();
s3.start();
}
}
实际在控制台打印结果的时候,按照
张—0
周—0
陈—0
这样三个为一组的顺序,每隔一秒打印一次,直至打印完毕,但是内部的顺序可以改变,比如
周—1
张—1
陈—1
因为三个进程每次抢占完CPU打印后都要休眠一秒,因此第一个进程休眠的时间内,足够其余的进程完成一次输出,进程m1打印完就休眠,不再抢夺CPU。接着m2打印完休眠,最后m3打印完休眠。不会出现m1刚刚打印完就立刻开始抢占CPU的情况。所以以三个为一组打印。但这三个进程休眠结束后,会再次抢占CPU,所以组内的顺序不一样。
线程Thread通过start()来启动线程,真正实现多线程运行,此时线程处于就绪状态。只有抢到了CPU后可以进入运行状态,Thread调用run()方法,完成线程的执行语句。
而当语句完成后就回进入死亡状态,之前的代码里面都是while循环,所以输出语句完成后还会继续循环,一直没有完成run方法。因此在运行主函数的时候会发现,程序不会结束,需要手动关闭。原因就在于这个while循环一直是true,线程一直没办法死亡,程序也就不会结束。
public final void stop():让正在运行的线程停止。run方法剩下的代码不会执行,此方法已经过时弃用了
public void interrupt():中断正在运行的线程,被中断的线程会将run方法执行完毕,并抛出异常
import java.util.Date;
public class MyStopThread extends Thread {
@Override
public void run() {
System.out.println(“开始执行时间:” + new Date());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(“执行结束时间:” + new Date());
System.out.println(“test”);
}
}
package test.MyThread;
public class ThreadStopDemo {
public static void main(String[] args) {
MyStopThread t1 = new MyStopThread();
t1.start();
}
}
开始执行的时间和执行结束的时间,中间隔了十秒
package test.MyThread;
public class ThreadStopDemo {
public static void main(String[] args) {
MyStopThread t1 = new MyStopThread();
t1.start();
try {
Thread.sleep(3000);
t1.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
这里的Thread.sleep(3000);是指主线程,也就是调用main方法的主线程,暂停三秒。然后中断线程 t1的运行
线程 t1本来是休眠十秒然后结束,这里因为interrupt中断了t1的休眠,所以这里的结束执行时间与开始执行时间只隔了三秒
礼让线程:
public static void yield()
暂停当前正在执行的线程对象,并执行其他线程
它的作用是为了让多个线程之间更加和谐一点,并不能一定保证多个线程一人一次执行
package test.MyThread;
public class YieldThread extends Thread{
@Override
public void run(){
for(int i=0;i<10;i++){
System.out.println(getName()+"—"+i);
}
}
}
package test.MyThread;
public class Demo8 {
public static void main(String[] args) {
YieldThread y1 = new YieldThread();
YieldThread y2 = new YieldThread();
y1.start();
y2.start();
}
}
类似于这样一人一句,进程占用CPU打印完后会礼让一下资源,当然礼让过后依然抢到了也是有可能的
Java中有两类线程:用户线程、守护线程
用户线程:我们在学习多线程之前所有程序代码,运行起来都是一个个的用户线程。
守护线程:所谓的守护线程,指的就是程序运行的时候在后台提供了一种通用服务的线程。比如说
垃圾回收线程,就是一个守护线程,这种线程并不属于程序不可或缺的部分。所以非守护线程结束的时候,程序也就终止了,同时会杀死进所有的守护线程。反过来说,只要程序种存在非守护线程,程序就不会终止。
守护线程的设置方法:
public final void setDaemon(boolean on)
通过这个方法将该线程对象标记为守护线程或者非守护线程。
当运行的程序只有一个线程且是守护线程的时候,Java虚拟机退出
注意:将线程设置为守护线程这一步骤,必须在启动前设置。
package test.MyThread;
public class DaemonThread extends Thread{
@Override
public void run() {
for(int i =0;i<10;i++){
System.out.println(getName()+":"+i);
}