Java多线程(1)

线程是什么

Java中一个线程就是一个 “执行流”. 每个线程有自己的任务. 多个线程之间 “同时” 各自完成着自己的任务.

线程和进程的区别

  1. 进程包含线程,一个进程里可以有一个线程,也可以有多个线程.
  2. 进程和线程都是为了处理并发编程这样的场景.但是进程有问题,频繁的穿件和释放的时候效率低,相比之下,线程更轻量,创建和释放效率更高.(轻量的原因是减少了申请释放资源的过程)
  3. 操作系统创建进程,要给进程分配资源,进程是操作系统分配资源的进本单位.而操作系统创建线程,是要在CPU上调度执行,线程是操作系统调度执行的基本单位.
  4. 进程具有独立性.每个进程有各自的虚拟地址空间,一个进程挂了,不会影响到其他进程.而同一个进程中的多个线程则是共用的一个内存空间,一个线程挂了,就有可能影响到其他线程,甚至导致整个进程奔溃.

Java中创建线程的方式

多线程程序和普通程序的区别:

  • 每个线程都是一个独立的执行流
  • 多个线程之间是 “并发” 执行的

先来感受一个多线程程序

import java.util.Random;
public class ThreadDemo {
    private static class MyThread extends Thread {
        @Override
        public void run() {
        	//用户产生随机数
            Random random = new Random();
            while (true) {
                // 打印线程名称 Thread.currentThread()用来获取当前线程实例
                System.out.println(Thread.currentThread().getName());
                try {
                    // 随机停止运行 0-9 秒	Thread.sleep();让程序进入阻塞状态.参数为时间,单位为毫秒
                     Thread.sleep(random.nextInt(10));
               } catch (InterruptedException e) {
                    e.printStackTrace();
               }
           }
       }
   }
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();
        t1.start();
        t2.start();
        t3.start();
        Random random = new Random();
        while (true) {
            // 打印线程名称
            System.out.println(Thread.currentThread().getName());
            try {
            	//让main线程随机休眠.
                Thread.sleep(random.nextInt(10));
           } catch (InterruptedException e) {
                // 随机停止运行 0-9 秒
                e.printStackTrace();
           }
       }
   }
}

1.继承Thread类

创建子类,继承自Thread,并重写run方法
并不是创建好子类之后就会创建线程的,而是实例化之后调用start()方法才创建了线程

class MyThread extends Thread{
    @Override
    public void run() {
        for (int i=0;i<10;i++) {
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                //线程被异常中断
                e.printStackTrace();
            }
        }
    }
}
public class Test1 {
    public static void main(String[] args) {
    	//MyThread t = new MyThread();//可以用Thread类接受实例,也可以使用MyThread接收实例
        Thread t = new MyThread();
        t.start();
        for (int i=0;i<20;i++) {
            System.out.println("main");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

2.实现 Runnable 接口

创建一个类实现Runnable接口,实现run方法
运行方式:

  1. new一个实现了Runnable接口的类实例
  2. new 一个Thread(new Runnable);将 Runnable 对象作为 target 参数传入
  3. 调用类的.start()方法
  • 此类方式比较推荐:Runnable单纯的只是描述了一个任务,至于任务是用什么方式来执行,Runnable并不关心(高内聚低耦合)
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("MyRunnable");
    }
}
public class Test2 {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable());
        t.start();
    }
}

对比上面两种方法:

  1. 继承 Thread 类, 直接使用 this 就表示当前线程对象的引用.
  2. 实现 Runnable 接口, this 表示的是 MyRunnable 的引用. 需要使用 Thread.currentThread()来引用线程实例

3.使用匿名内部类

此种方式输入方法1的变形

public class Test3 {
    public static void main(String[] args) {
	    //匿名内部类创建 Thread 子类对象
        Thread thread = new Thread() {
            @Override
            public void run() {
                System.out.println("Test3");
            }
        };
        thread.start();
    }
}

4.使用匿名内部类

此种方式输入方法2的变形

public class Test4 {
    public static void main(String[] args) {
    	//匿名内部类创建 Runnable 子类对象
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("t1");
            }
        });
        t1.start();
    }
}

3和4看似相同,实则完全是两种方式
3.匿名的是继承Thread的实现类类
4.匿名的则是实现Runnable的接口类

5.lambda表达式.

属于4.方式的lambda表达式方式.
此种方式虽然更简洁,但是对于初学者相对不友好

public class Test5 {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> System.out.println("lambda"));
        t1.start();
    }
}

Thread介绍

了解完线程的创建,我们再来看看Java的Thread类.

1.Thread常见的构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用 Runnable 对象创建线程对象
Thread(String name)创建线程对象,并给线程对象命名
Thread(Runnable target, String name)使用 Runnable 对象创建线程对象,并命名
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");

2.Thread 的几个常见属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()
  • ID 是线程的唯一标识,不同线程不会重复.
  • 名称是使用各种调试工具时,可以使用名称来区别线程.
  • 状态表示线程当前所处的一个情况,指官方文档中的线程状态.
  • 优先级高的线程理论上来说更容易被调度到,但并不是一定调用优先极高的线程.
  • 后台线程,JVM就属于后台线程,会在一个进程的所有非后台线程结后,才会结束运行.
  • 是否存活,可以简单理解为 run 方法是否运行结束.
  • 可以理解为有一个标志位,用于控制线程是否被中断,而此方法则是根据标志位来判断线程是否被中断.

Thread方法介绍

1.启动线程

start()方法

	Thread t1 = new Thread(() -> System.out.println("lambda"));
    t1.start();

start()方法和run()方法的区别.

  • 调用start()方法是区别于当前线程另外开启一个新的线程去运行对象中的run方法.当前线程的后续代码是和新开的线程"并发"运行的.
  • 调用run()方法则是在当前线程中调用run()方法运行.并没有开启一个新的线程.当前线程中的后续代码则需要等待run()方法运行完才可以执行.

2.中断线程

  • 通过共享的标记来进行沟通,设置标志位,通过在其他线程控制标志位,来终止线程
public class Test9 {
    private static volatile boolean isQuit = false;

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            int i=0;
            while(!isQuit) {
                System.out.println("t1: "+(i++));
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        isQuit = true;
        System.out.println("终止t1线程");
    }
}

  • 调用 interrupt() 方法来通知线程中断.

先来看两个获取线程标志位的方法

1.Thread.interrupted();

静态方法(一个程序只有一个标志位)

2.Thread.currentThread().isInterrupted();

实例方法,获取当前线程实例的标志位(每个线程有自己的标志位,推荐使用)

public class Test10 {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            int i=0;
            while(!Thread.currentThread().isInterrupted()) {
                System.out.println("t1: "+(i++));
                try {
                    Thread.sleep(700);
                } catch (InterruptedException e) {
                    //e.printStackTrace();
                    break;
                }
            }
        });
        t1.start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //在主线程中调用 interrupt方法
        t1.interrupt();//中断t1
        System.out.println("终止t1线程");
    }
}

调用线程的t1.interrupt()方法,来改变标志位,该方法有两种情况,

  1. 线程就绪状态,就会设置标志位为true.
  2. 线程阻塞状态(sleep),就会触发一个异常(InterruptException).
    (解决方式)可以在catch块中使用break;中断线程

3.等待一个线程

join()方法
在a线程中调用b.join(),就是a线程阻塞等待b线程
join()方法在默认情况下是死等,可以给方法设置参数.单位为毫秒(超时时间)

public class Test11 {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            for (int i=0;i<10;i++) {
                System.out.println("t1: "+(i++));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
        try {
            //main阻塞等待t1
            t1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main线程阻塞等待t1线程");
    }
}

4.获取当前线程引用

Thread.currentThread();
静态方法:返回当前线程对象的引用

public class Test12{
    public static void main(String[] args) {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
   }
}

5.休眠当前线程

Thread.sleep(3 * 1000);
静态方法:参数为休眠时间,单位为毫秒
作用:休眠调用该方法的线程.
需要注意的是这里设置的时间是在这个时间内不可以唤醒该线程,而不是时间结束就唤醒该线程.休眠时间结束后会进入就绪队列等待调用.

  • 16
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

魚小飛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值