Java多线程(详细)

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中主要有两种创建线程的技术

  1. 创建一个新的类继承Thread类, 并且重载run()方法
  2. 定义一个 实现Runnable接口 的类, 并且实现run()方法

2.1 继承Thread类

该种方法主要包括三个步骤

  1. 继承Thread类
  2. 重写run()方法
  3. 调用start()方法

start()方法与run()方法

创建Thread 对象并不会创建一个新的线程, 实际上新的线程是由start()函数进行创建的

start函数会做下面两件事

  1. 在JVM中分配函数所需的内存并且分配进程
  2. 调用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次的守护进程提前结束
在这里插入图片描述

发布了15 篇原创文章 · 获赞 11 · 访问量 5274

猜你喜欢

转载自blog.csdn.net/qq_39816031/article/details/104519498
今日推荐