Java多线程基础

1、什么是多线程

想要了解多线程就需要知道什么是进程什么是线程,简单的来说进程是指一个程序运行时操作系统给这个程序分配的CPU、内存、网络、磁盘等一系列的操作系统的资源的总称。而一个进程又是由若干个线程组成的,一个进程中至少存在一个线程。

2、线程的几种状态

了解完什么 是线程后我们再来简单了解线程的几种基本状态。新建(New),就绪(Runnable),运行(Running),阻塞(Blocked),死亡(Dead)相关关系如下图所示Java多线程基础_第1张图片
新建(New):线程对象被创建。
就绪(Runnable):线程对象等待操作系统分配资源。
运行(Running):线程对象的到了CPU资源。
阻塞(Blocked):堵塞状态是线程由于某种原因放弃CPU使用权。临时停止执行。直到线程进入就绪状态,才有机会转到执行状态。
死亡(Dead):线程运行完了或者因异常退出了run()方法,该线程结束生命周期。

3、Java中如何实现多线程

在Java中实现多线程有两种方式分别是继承Thread类和继承Runable接口。

3.1、继承Thread类实现多线程

通过继承Thread实现多线程,重写Thread类中的run()方法在其中完成业务代码的编写,继承了Thread类的类就是一个线程类(可以直接实例化后调用start()方法使线程进入就绪态等待操作系统调度)。

通过简单的例子展示相关操作:

/** 继承Thread类创建线程
 * 须要重写run()方法
*/
public class Demo_01 {
	public static void main(String[] args) {
		System.out.println("Begin");
		//创建线程
		PrintFlagThread pft = new PrintFlagThread();
		//线程进入就绪状态,等待操作系统调度
		pft.start();
		System.out.println("End");
	}
}
class PrintFlagThread extends Thread{
	//重写Thread的run方法
	@Override
	public void run() {
		for(int i =1; i<101;i++) {
			System.out.print("*");
			if(i%10==0) {
				System.out.println();
			}
		}
	}
}

3.2、实现Runable接口实现多线程

一个类通过实现Runable接口实现多线程,通过重写Runable接口中的run()方法完成业务逻辑的编写,实现了Runable接口的类不能算是一个真正的线程类需要配合Thread类完成线程对象的创建。

通过简单的例子展示相关操作:

/** 实现Runable接口创建线程
 * 须要重写run()方法
*/
public class Demo_02 {
	public static void main(String[] args) {
		System.out.println("Begin");
		PrintFlagThread printFlagThread = new PrintFlagThread();
        Thread thread = new Thread(printFlagThread);
		//线程进入就绪状态,等待操作系统调度
		thread.start();
		System.out.println("End");
	}
}
class PrintFlagThread implements Runable{
	//实现Runable接口中的run方法
	@Override
	public void run() {
		for(int i =1; i<101;i++) {
			System.out.print("*");
			if(i%10==0) {
				System.out.println();
			}
		}
	}
}

4、多线程中常见方法及其作用

  • currentThread()setPriority(10)静态方法,返回代码段正在被哪个线程调用的信息
  • setName():为当前线程命名。
  • setPriority():设置当前线程优先级,线程的优先级分为1-10这10个等级,如果小于1 或大于10,则抛出异常throw new IllegalArgumentException(),默认是5。(线程的优先级仍然无法保障线程的执行次序。只不过,优先级高的线程获取CPU资源的概率较大,优先级低的并非没机会执行。)

下面通过简单的例子来展示这个方法的作用。

public class Demo_05 {
	public static void main(String[] args) {
		Thread thread1 = new Thread(new MyThread());
		// 设置线程名称
		thread1.setName("线程甲");
		// 设置当前线程优先级
		thread1.setPriority(10);
		
		Thread thread2 = new Thread(new MyThread());
		thread2.setName("线程乙");
		thread2.setPriority(5);
		
		Thread thread3 = new Thread(new MyThread());
		thread3.setName("线程丙");
		thread3.setPriority(1);
		
		// 使线程进入就绪态
		thread1.start();
		thread2.start();
		thread3.start();
	}
}
class MyThread implements Runnable{
	@Override
	public void run() {
		//打印当前线程名称10次
		for(int i = 0; i<10 ;i++) {
			//通过currentThread()方法得到当前线程
			Thread currentThread = Thread.currentThread();
			//通过线程对象获得线程对象名称并输出
			System.out.println(currentThread.getName()+"的第"+i+"次打印");
		}
	}	
}

运行结果如下:Java多线程基础_第2张图片

从三次的运行结果不难看出,setPriority()对线程的优先级只起到影响作用并不是起到决定性的作用。

  • yield():使当前正在执行的线程向另一个线程交出运行权
  • join():将线程调用此方法的线程加入到主线程中,当当前线程执行完成后恢复主线程的执行。(如系统目前运行线程A,在线程A里面调用了线程B.join方法,则接下来线程B会抢先在线程A面前执行,等到线程B全部执行完后才继续执行线程A。)

下面通过例子展示这两个方法的作用:

public class Demo_08 {
	public static void main(String[] args) {
		PringCharThread pct = new PringCharThread(10, 'A');
		Thread t = new Thread(pct);
		PringCharThread pct1 = new PringCharThread(10, 'P');
		Thread t1 = new Thread(pct1);
		PringCharThread pct2 = new PringCharThread(10, 'W');
		Thread t2 = new Thread(pct2);
		Thread t3 = new Thread(new PrintNumberThread(10));
		t.start();
		t1.start();
		t2.start();
		t3.start();
	}
}
class PringCharThread implements Runnable {
	private int count;
	private char ch;
	public PringCharThread(int count, char ch) {
		this.ch = ch;
		this.count = count;
	}
	@Override
	public void run() {
		for (int i = 1; i <= count; i++) {
			System.out.printf("%s%d\t", ch, i);
			if (i % 10 == 0) {
				System.out.println();
			}
			if (i == count >> 1) {
				// 使当前正在执行的线程向另一个线程交出运行权,
				Thread.yield();
			}
		}
	}
}
class PrintNumberThread implements Runnable {
	private int count;
	public PrintNumberThread(int count) {
		this.count = count;
	}
	@Override
	public void run() {
		for (int i = 1; i <= count; i++) {
			System.out.printf("%d\t", i);
			if (i % 10 == 0) {
				System.out.println();
				if (i == count >> 1) {
					Thread thread = new Thread(new PringCharThread(20, '?'));
					thread.start();
					try {
						thread.join();// 将线程therad加入到当前线程中,当thread执行完成后恢复当前线程的执行
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}

运行结果
第一次:
W1 W2 1 2 3 4 5 6 7 8 9 10 A1 A2 A3 A4 A5 P1 P2 P3 P4 P5 A6 A7 A8 A9 A10
W3 W4 W5 P6 P7 P8 P9 P10
W6 W7 W8 W9 W10
第二次:
P1 P2 P3 P4 P5 1 2 3 4 5 6 7 8 9 10
A1 A2 A3 A4 A5 W1 A6 P6 P7 P8 P9 P10 A7 A8 A9 A10
W2 W3 W4 W5
W6 W7 W8 W9 W10

由于是多线程实现输出结果格式无法有效控制,顾做如上展示。由运行结果可以看出,字符打印线程让出执行权,待数字打印线程执行完成后继续完成字符的打印。

一般在synchronized 同步代码块里使用 wait()、notify/notifyAll() 方法,说明当前线程一定是获取了锁的。

  • notify()/notifyall():唤醒一个或多个正处于等待状态的线程,然后继续往下执行,直到执行完synchronized 代码块的代码或是中途遇到wait() ,再次释放锁。
  • wait():会释放当前的锁,然后让出CPU,进入等待状态。

同样通过一个小例子展示它们的用法:

public class Demo_02 {
	public static void main(String[] args) {
		// 创建Object传入线程使他们共同持有同一个对象,进而完成同步锁的功能
		Object lockobj = new Object();
		// 创建打印数字线程类
		Thread printNumberThread = new Thread(new PrintNumber(lockobj));
		// 创建打印字母线程类
		Thread printCharThread = new Thread(new PrintChar(lockobj));
		printNumberThread.start();
		printCharThread.start();
	}
}
//打印数字的方法
class PrintNumber implements Runnable {
	private Object obj;

	public PrintNumber(Object obj) {
		this.obj = obj;
	}
	@Override
	public void run() {
		synchronized (obj) {
			for (int i = 1; i <= 52; i++) {
				System.out.print(i);
				if (i % 2 == 0) {
					// 在输出偶数数字后,输出空格前使当前线程唤醒另一个线程并让当前线程进入等待
					obj.notify();// 让当前线程通知
					if (i < 52) {
						try {
							obj.wait();// 让当前线程进入等待
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					System.out.print("  ");
				}
			}
		}
	}
}
class PrintChar implements Runnable {
	private Object obj;

	public PrintChar(Object obj) {
		this.obj = obj;
	}
	@Override
	public void run() {
		synchronized (obj) {
			for (int i = 'A'; i <= 'Z'; i++) {
				System.out.print((char) i);
				obj.notify();// 让当前线程通知
				if (i < 'Z') {
					try {
						obj.wait();// 让当前线程进入等待
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}

两个线程一个打印数字一个打印字母,当打印到偶数数字时调用notify()通知字母打印线程,然后调用wait()方法等待字母打印线程执行,字母打印线程打印一个字母,然后通知数字打印线程,并停下来等,重复以上步骤。得到如下结果:

12A 34B 56C 78D 910E 1112F 1314G 1516H 1718I 1920J 2122K 2324L 2526M 2728N 2930O 3132P 3334Q 3536R 3738S 3940T 4142U 4344V 4546W 4748X 4950Y 5152 Z


当然还有很多方法如sleep(S):(让线程“睡眠”S毫秒)这里不一一列举,至此关于多线程一些基本操作就介绍完了。

你可能感兴趣的:(技术分享,java,多线程,thread)