0. 进程与线程
1. jvm中的多线程机制-垃圾回收
下面举一个java程序中多线程机制的例子, 我们知道java拥有垃圾回收的机制, 而垃圾回收都是在我们"不知情"情况下发生的, 似乎与我们编写的程序走在两条不同的道上,jvm运行垃圾回收的代码并不会阻塞我们的代码运行, 其实这就是多线程.
每个类在被回收时都会去调用继承自Object
类的finalize()
方法, 我们可以做一个测试, 看他是不是多线程
2.1 finalize()方法
public class Test {
public static void main(String[] args) {
for (int i = 0; i < 1000000; i++) {
new GCDemo(i);
}
}
}
class GCDemo{
final int i;
public GCDemo(int i) {
super();
this.i = i;
}
@SuppressWarnings("deprecation")
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("Garbage Collecting"+i);
}
}
2.1 运行结果
运行结果如下, 显然, 这段代码并不是"按顺序执行的", 因为如果是的话, 那么数字应该是按顺序往下排, 现在对多线程多了些理解呢?别着急, 继续往下看
线程是Java 程序中程序执行的基本模型, Java语言和它的API为创建和管理线程提供了丰富的方法, 所有Java程序至少由一个控制线程组成(哪怕是只有空的main函数的Java程序,也是在JVM中作为一个线程运行的)
2 创建线程
Java中主要有两种创建线程的技术
- 创建一个新的类继承
Thread
类, 并且重载run()
方法 - 定义一个 实现
Runnable
接口 的类, 并且实现run()
方法
2.1 继承Thread类
该种方法主要包括三个步骤
- 继承Thread类
- 重写run()方法
- 调用start()方法
start()方法与run()方法
创建Thread 对象并不会创建一个新的线程, 实际上新的线程是由start()
函数进行创建的
start函数会做下面两件事
- 在JVM中分配函数所需的内存并且分配进程
- 调用
run()
方法, 使线程在JVM中运行(不直接调用run()函数,而是调用start函数, 它再调用run()
函数
如果你直接调用run() 方法, 它将会在main函数的线程中执行
示例代码
public class Test{
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
for (int i = 0; i < 10; i++) {
System.out.println("main"+i);
}
}
}
class MyThread extends Thread{
@Override
public void run() {
super.run();
for (int i = 0; i < 10; i++) {
System.out.println("Thread"+i);
}
}
}
匿名内部类实现
Thread类可以使用匿名内部类实现
new Thread() {
public void run() {
super.run();
for (int i = 0; i < 100; i++) {
System.out.println("Thread"+i);
}
}
}.start();
2.2 实现Runnable接口
实际上实现Runnable
接口是一种更加常用的方法, 为什么呢?因为java是不允许多线程, 但允许实现多个接口, 这意味着当我们想给某个类添加多线程的特性时, Runnbale
会比Thread
方便许多
start()方法
实际上, Runnable接口中并没有start()
方法, 那么我们如何来创建一个线程呢?
事实上, 创建线程的任务我们总是交给Thread类来完成, 要想利用Runnable接口实现的类创建线程, 需要实例化一个Thread对象, 构造参数中为Runnbale的实例化对象
示例代码
public class Stuxx {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread mt = new Thread(mr);
mt.start();
}
}
class MyRunnable implements Runnable {
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("Runnable"+i);
}
}
}
★lambda表达式实现
仔细查看Runnable 接口的声明代码, 会发现Runnbale是一个函数式接口, 我们可以直接使用lambda表达式来创建它
public class Test{
public static void main(String[] args) {
//使用匿名表达式创建线程
new Thread(()->{
for (int i = 0; i < 100; i++) {
System.out.println("main"+i);
}
}).start();
}
}
3. 设置/获取线程名
我们可以利用线程名来区别每个不同的线程
默认线程名为 Thread-i
,i从0开始计数
Thread t1=new Thread(()->{
for (int i = 0; i < 100; i++) {
System.out.println("main"+i);
}
});
System.out.println(t1.getName()); //输出Thread-0
3.1 在构造中设置
Thread t1 = new Thread("Thread1") {
public void run() {
super.run();
for (int i = 0; i < 100; i++) {
System.out.println("Thread"+i);
}
}
};
System.out.println(t1.getName());
3.2 利用方法设置/获取
Thread t1=new Thread(()->{
for (int i = 0; i < 100; i++) {
System.out.println("main"+i);
}
});
t1.setName("myThread");
System.out.println(t1.getName());
4. 守护线程(daemon thread)
什么是守护线程呢?, 举个象棋的例子, 帅或将就是其他的子的"守护进程", 当帅或将死去时, 这些守护进程都不复存在
也就是说, 当其他进程执行完毕时, 守护线程结束运行
4.1 设置守护线程
使用下面语法设置守护进程
Thread实例化对象.setDaemon(true);
4.2 示例代码
public static void main(String[] args){
Thread tb = new Thread() {
public void run() {
super.run();
for (int i = 0; i < 50; i++) {
System.out.println("ThreadDaemon"+i);
}
}
};
tb.setDaemon(true);
Thread ta = new Thread() {
public void run() {
super.run();
for (int i = 0; i < 2; i++) {
System.out.println("ThreadA"+i);
}
}
};
tb.start();
ta.start();
}
可以看到本该执行50次的守护进程提前结束
5. 加入线程
所谓加入线程, 其实可以理解为插队, 比如有t1, t2两个线程, 当我调用t2.join()加入线程时, t2就插了t1 的队,此时t1立即停止执行, 等t2执行完了, 再执行t1
5.1 直接加入
public static void main(String[] args){
Thread tb = new Thread() {
public void run() {
super.run();
for (int i = 0; i < 20; i++) {
System.out.println("ThreadBBB"+i);
}
}
};
Thread ta = new Thread() {
public void run() {
super.run();
for (int i = 0; i < 20; i++) {
try {
if (i ==10) {
tb.join();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("ThreadAAA"+i);
}
}
};
ta.start();
tb.start();
}
5.2 加入一段时间
可以给join传递一个参数(单位为毫秒), 表示我只插 这么多毫秒的队伍
public static void main(String[] args){
Thread tb = new Thread() {
public void run() {
super.run();
for (int i = 0; i < 20; i++) {
System.out.println("ThreadBBB"+i);
}
}
};
Thread ta = new Thread() {
public void run() {
super.run();
for (int i = 0; i < 20; i++) {
try {
if (i ==2) {
tb.join(30);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("ThreadAAA"+i);
}
}
};
ta.start();
tb.start();
}
可以看到线程A先执行了一会, 到达2时立即停止交给B执行30毫秒, 之后退出"插队"
转载:https://blog.csdn.net/qq_39816031/article/details/104519498