多线程系列学习笔记之(一)

168 阅读5分钟

1、那些进程和线程之间你迷糊的事???

进程:正在运行的程序

线程:进程的最小执行单位

进程包含线程

dos系统是一个单线程的程序

java是一门支持多线程的语言

我们把含有main方法的线程也称为主线程

严格来说当启动一个java程序时,启动了两个线程,一个是主线程,还有一个垃圾回收器(garbage collection)

  • 现在咱们测试下单个线程的执行情况
public class A {
	public void testA() throws Exception{
		System.out.println("testA方法开始执行");
		Thread.sleep(5000);
		System.out.println("testA方法执行完毕");
	}
}

public class B {
	public void testB() throws Exception{
		System.out.println("testB方法开始执行");
		Thread.sleep(5000);
		System.out.println("testB方法开始完毕");
	}
}

public class TestAB {
	public static void main(String[] args) throws Exception {
		A a = new A();
		B b = new B();
		a.testA();
		b.testB();
	}
}

由结果可以看出testA方法执行完毕之后才能执行testB方法,可见以上代码是单线程的

多线程可以充分利用cpu资源

对于单核cpu来说在同一时刻只能执行一个线程,但是切换速度很快,所以感觉是多个线程在同时执行

2、创建多线程常用的两种方式

  • 方式一:

1)定义一个类继承Thread类

2)重写Thread类的run方法

3) 调用start方法开启多线程,start方法会自动调用run方法

在start方法中调用了private native void start0();方法,该方法有一个native关键字,表示该方法调用了底层的方法,通知cpu开启多线程

public class MyThread1 extends Thread{
	@Override
	public void run() {//线程任务
		System.out.println("MyThread1开始执行了");
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("MyThread1执行完毕了");
	}
}

public class MyThread2 extends Thread{
	@Override
	public void run() {//线程任务
		System.out.println("MyThread2开始执行了");
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("MyThread2执行完毕了");
	}
}

public class TestMyThread12 {
	public static void main(String[] args) {
		MyThread1 m1 = new MyThread1();
		MyThread2 m2 = new MyThread2();
		m1.start();
		m2.start();
	}
}
  • 方式二:

1)定义一个类实现Runnable接口 2)创建该类对象,并传递到Thread类的构造方法中 3)调用Thread类的start方法

public class MyThread3 implements Runnable{
	@Override
	public void run() {
		System.out.println("MyThread3开始执行了");
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("MyThread3执行完毕了");
	}
}

public class MyThread4 implements Runnable{
	@Override
	public void run() {
		System.out.println("MyThread4开始执行了");
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("MyThread4执行完毕了");
	}
}

public class TestMyThread34 {
	public static void main(String[] args) {
		MyThread3 m3 = new MyThread3();
		MyThread4 m4 = new MyThread4();
		Thread t1 = new Thread(m3);
		Thread t2 = new Thread(m4);
		t1.start();
		t2.start();
	}
}

3、关于两种实现方式的使用问题

  • 什么时候用接口什么时候用继承?

1.如果是is的关系时使用继承,例如男人是人

2.如果是has的关系时使用接口,例如:飞机有导航的功能,手机也有导航的功能 如果分不清楚时一律使用接口,因为一个类只能有一个父类,但是可以实现多个接口

4、在多线程环境下,如果某个线程抛出异常会不会影响其他线程?

  • 测试一下 你就知道
public class MyThread5 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println("MyThread5的i=" + i);
			if(i == 5) {
				System.out.println(3 / 0);
			}
		}
	}
}

public class TestMyThread5 {
	public static void main(String[] args) {
		MyThread5 m5 = new MyThread5();
		m5.start();
		
		for (int i = 0; i < 10; i++) {
			System.out.println("主线程中的i=" + i);
		}
	}
}

由以上代码可以看出MyThread5中抛出的异常并没有影响主线程的执行

Thread-0,如果不给线程命名时,会自动命名Thread-0,Thread-1,Thread-2...

如果不给主线程命名时,会自动命名为"main"

5、线程命名

设置名称用setName

获取名称用getName

方式一:继承Thread类对线程设置名称

public class MyThread6 extends Thread{
	public MyThread6(String name) {
		super(name);
	}
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(getName() + ":" + i);
		}
	}
}

public class TestMyThread6 {
	public static void main(String[] args) {
		MyThread6 m6 = new MyThread6("李四线程");
		m6.setName("张三线程");
		m6.start();
	}
}

方式二:实现Runnable接口对线程起名

public class MyThread7 implements Runnable{
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			//Thread.currentThread()获取当前线程对象
			System.out.println(Thread.currentThread().getName() + ":" + i);
		}
	}
}

public class TestMyThread7 {
	public static void main(String[] args) {
		MyThread7 m7 = new MyThread7();
		Thread t = new Thread(m7, "张三线程");
		t.setName("李四线程");
		Thread.currentThread().setName("王五线程");
		t.start();
		
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + ":" + i);
		}
	}
}

6、设置线程的优先级

通过setPriority方法给线程设置优先级,参数是int类型,范围是1到10,值越大优先级越高

public class MyThread8 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 1000; i++) {
			System.out.println(getName() + ":" + i);
		}
	}
}

public class TestMyThread8 {
	public static void main(String[] args) {
		MyThread8 m1 = new MyThread8();
		m1.setName("线程a");
		m1.setPriority(Thread.MIN_PRIORITY);//在开发时,如果使用数字尽量定义成常量
		
		MyThread8 m2 = new MyThread8();
		m2.setName("线程b");
		m2.setPriority(Thread.NORM_PRIORITY);
		
		MyThread8 m3 = new MyThread8();
		m3.setName("线程c");
		m3.setPriority(Thread.MAX_PRIORITY);
		
		m1.start();
		m2.start();
		m3.start();
		
	}
}

7、守护线程

m.setDaemon(true);//让普通线程变成守护线程,守护着主线程,一旦主线程结束,那么该线程必须结束

垃圾回收器是一个典型的守护线程

public class MyThread9 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 1000; i++) {
			System.out.println(getName() + ":" + i);
		}
	}
}

public class TestMyThread9 {
	public static void main(String[] args) {
		MyThread9 m = new MyThread9();
		m.setDaemon(true);//让普通线程变成守护线程,守护着主线程,一旦主线程结束,那么该线程必须结束
		m.start();
		for (int i = 0; i < 1000; i++) {
			System.out.println("主线程:" + i);
			if(i == 50) {
				System.out.println(3 / 0);
			}
		}
	}
}

8、线程的生命周期

生命周期:指的是对象从创建到消亡的过程,在每个过程中调用的方法称为生命周期方法

public class TestMyThread6 {
	public static void main(String[] args) {
		MyThread6 m6 = new MyThread6();
		Thread t = new Thread(m6,"李四线程");
		t.start();
		//如果一个线程已经执行完毕,不能再次执行它,否则会抛出Illegal(非法)Thread(线程)State(状态)Exception(异常)
		t.start();
	}
}

9、如何让线程停止

示例代码:

public class MyThread7 extends Thread{
	public void run() {
		for (int i = 0; i < 100; i++) {
			System.out.println("线程:" + i);
			if(i == 50){
//				stop();
//				return;//结束整个方法
				break;
			}
		}
		System.out.println("关闭资源代码");
	}
}

设置标志位的代码:

stop方法,和return方法都让整个线程任务结束,如果有关闭资源的代码时不会被执行,所以如果提前让线程结束最好是定义一个标志位或者使用break

public class MyThread7 extends Thread{
	public void run() {
		boolean flag = true;
		for (int i = 0; flag; i++) {
			System.out.println("线程:" + i);
			if(i == 50){
//				stop();
//				return;//结束整个方法
				flag = false;
//				break;
			}
		}
		System.out.println("关闭资源代码");
	}
}

记得点赞关注👉:推荐自己的Github地址:github.com/Lmobject