Thread初探
前言
以前大家写的都是单线程的程序,全是在main函数中调用方法,可以清楚的看到它的效率是特别低的,就像python中使用单线程取爬一个网站,可以说能让你等的吐血,因为数据量实在太大了,今天我们就来看看java的并发编程多线程的学习
创建线程
创建一个线程可以有多种方法,比如继承Thread类,实现Runnable接口......下面我们来详细的看看创建的方法
继承Thread
为什么继承
Thread
可以直接调用start()
方法启动线程呢,因为start()
本身就是Thread的方法,也就是继承了Thread的start()方法,因此这个类的对象可以调用start()启动线程
//继承Thread
public class MyThread extends Thread {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(this.getName()+"正在跑");
}
}
}
public class Test{
public static void main(String[] args)
{
Mythread t1=new MyThread(); //创建对象
t1.start(); //启动线程
}
}
注意: 继承
Thread
类的创建方法一个对象只能创建一个线程,并不能多个线程共用一个对象,只能一个线程对应一个对象,因此我们来看看实现Runnable
接口的类来实现多个线程共享同一个对象
实现Runnable接口
//实现Runnable接口
public class Demo implements Runnable {
@Override
public void run() {
for(int i=0;i<10;i++)
{
System.out.println(Thread.currentThread().getName()+"正在跑");
}
}
}
//测试类
public class Test{
public static void main(String[] args)
{
Demo d=new Demo(); //创建对象
Thread thread1=new Thread(d); //为对象创建一个线程
Thread thread2=new Thread(d); //创建另外一个线程
//同时启动两个线程
thread1.start();
thread2.start();
}
}
从上面可以清楚的看到实现
Runnable
接口的类一个对象可以供多个线程共享,并不像继承Thread类只为一个线程使用
简便的创建方法
直接在
main
方法中创建,如果创建的普通类的对象在外面,那么必须是final修饰,可以实现多个线程同时共享一个对象,这个和实现Runnable
接口一样,这时候就要控制同步条件了,如果在run方法中定义对象,那么,就是一个线程对应一个对象,这个就和继承Thread类一样的效果。所以可以根据条件*选择
//普通的一个类
public class Simple {
public void display()
{
for(int i=0;i<10;i++)
{
System.out.println(Thread.currentThread().getName()+"正在跑");
}
}
}
//线程测试类
public class Test {
public static void main(String[] args) {
//如果在外面必须使用final,当然也可以直写在run方法中,不过写在外面可以实现多个线程共享一个对象
//写在run方法中当前对象只能为一个线程使用,和继承Thread类一样的效果
final Simple simple=new Simple();
//下面创建使用同一个对象创建同两个线程,实现多个线程共享一个对象,和实现Runnable接口一样的效果
Thread t1=new Thread(){
public void run() {
simple.display();
};
};
Thread t2=new Thread(){
public void run() {
simple.display();
};
};
//启动这两个线程
t1.start();
t2.start();
}}
常用的方法
static void sleep(long mils)
使正在运行的线程休眠mils毫秒,但是这里需要注意的是如果线程加了锁,那么使线程休眠并不会释放锁
String getName()
得到线程的名称,上面的程序中已经使用了这个方法
void setName(String name)
设置正在运行的线程的名字为name
start()
启动线程,线程的创建并不意味着线程的启动,只有调用start()方法线程才是真正的开始运行
long getId()
返回线程的标识符
run()
线程执行的代码都放在run()方法中,在run方法中的调用是有序的,都是按照程序运行的顺序开始执行
使用
下面使用上面的方法创建一个实例
//线程的类,继承Thread
public class MyThread1 extends Thread {
public void run() { // 重载run方法,并且在其中写线程执行的代码块
for (int i = 0; i < 10; i++) {
// 获取线程的id和name
System.out.println("Thread-Name: " + this.getName()
+ " Thread-id: " + this.getId());
try {
this.sleep(1000); // 线程休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//线程测试的类
public class Test {
public static void main(String[] args) {
MyThread1 t1 = new MyThread1(); // 创建线程
t1.setName("第一个线程"); // 设置线程的名字
MyThread1 t2 = new MyThread1();
t2.setName("第二个线程");
t1.start(); // 启动线程,开始运行
t2.start();
}
}
void join()
等待该线程终止才能运行其他的线程void join(long mils)
等待该线程的时间为mils毫秒,一旦过了这个时间其他线程正常执行
使用
//线程类
public class MyThread1 extends Thread {
public void run() { // 重载run方法,并且在其中写线程执行的代码块
for (int i = 0; i < 10; i++) {
// 获取线程的id和name
System.out.println("Thread-Name: " + this.getName()
+ " Thread-id: " + this.getId());
try {
this.sleep(1000); // 线程休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
MyThread1 t1 = new MyThread1(); // 创建线程
t1.setName("第一个线程"); // 设置线程的名字
t1.start(); // 启动线程,开始运行
try {
t1.join(); //阻塞其他线程,只有当这个线程运行完之后才开始运行其他的线程
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
System.out.println("主线程正在运行");
}
}
}
//输出结果
/*
Thread-Name: 第一个线程 Thread-id: 9
Thread-Name: 第一个线程 Thread-id: 9
Thread-Name: 第一个线程 Thread-id: 9
Thread-Name: 第一个线程 Thread-id: 9
Thread-Name: 第一个线程 Thread-id: 9
Thread-Name: 第一个线程 Thread-id: 9
Thread-Name: 第一个线程 Thread-id: 9
Thread-Name: 第一个线程 Thread-id: 9
Thread-Name: 第一个线程 Thread-id: 9
Thread-Name: 第一个线程 Thread-id: 9
主线程正在运行
主线程正在运行
主线程正在运行
主线程正在运行
主线程正在运行
主线程正在运行
主线程正在运行
主线程正在运行
主线程正在运行
主线程正在运行
*/
getPriority()
得到当前线程优先级setPriority(int num)
更改线程的优先级(0-10)默认的是5,优先级越高获得cpu资源的几率就会越高
使用
//线程类
public class MyThread1 extends Thread {
public void run() { // 重载run方法,并且在其中写线程执行的代码块
for (int i = 0; i < 10; i++) {
// 获取线程的id和name
System.out.println("Thread-Name: " + this.getName()
+ " Thread-id: " + this.getId());
try {
this.sleep(1000); // 线程休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
MyThread1 t1 = new MyThread1(); // 创建线程
t1.setName("第一个线程"); // 设置线程的名字
MyThread1 t2 = new MyThread1();
t2.setName("第二个线程");
t2.setPriority(8); //设置第二个线程的优先级为8,第一个线程的优先级为5(是默认的)
t1.start();
t2.start();
}
}
/*
* 从上面的运行结果可以看出大部分的第二个线程都是在第一个线程之前开始执行的,也就是说优先级越高获得cpu执行的几率就越大
* /
setDaemon(boolean)
是否设置为守护线程,如果设置为守护线程,那么主线程销毁守护线程也会随之销毁isDaemon()
判断是否为守护线程
使用
//测试类
public class MyThread1 extends Thread {
public void run() { // 重载run方法,并且在其中写线程执行的代码块
for (int i = 0; i < 10; i++) {
// 获取线程的id和name
System.out.println("Thread-Name: " + this.getName()
+ " Thread-id: " + this.getId());
try {
Thread.sleep(1000); //休眠一秒,方便主线程运行结束
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Test {
public static void main(String[] args) {
MyThread1 t1 = new MyThread1(); // 创建线程
t1.setName("第一个线程"); // 设置线程的名字
t1.setDaemon(true);
t1.start();
for (int i = 0; i < 1; i++) {
System.out.println(i);
}
}
}
//结果:
/*
0
1
2
3
4
5
6
7
8
9
Thread-Name: 第一个线程 Thread-id: 9
*/
/*
* 从上面的结果可以看出,一旦主线程结束,那么守护线程就会自动的结束
* /