Java多线程(一)

进程和线程

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。如下图,eclipse.exe是一个进程、谷歌浏览器是一个进程,QQ也是一个进程,进程是受操作系统管理的基本运行单元。



线程可以理解成是在进程中独立运行的子任务,比如在使用谷歌浏览器时,一边浏览网站,一边用浏览器下载文件同时还一边用浏览器播放音乐。多线程类似多任务操作系统,比方像Windows,用户可以一边打印文档,一边使用word编辑文档,还可以一边打开QQ聊天。CPU在这些任务之间不断切换和运行,而且切换的速度很快,给用户的感觉就像同时运行。使用多线程技术后,可以在同一时间内运行多个不同种类的任务

如下图,单任务环境中,任务1和任务2必须按顺序执行,任务1执行10秒,任务2执行1秒,虽然任务2执行的时间很短,但必须等待任务1,所以当单任务环境中,要等任务2执行完,必须等11秒,而多任务环境中,如果任务1和任务2之间的执行并不需要先后顺序,彼此也没关系,使用多线程技术可以将任务的执行可以来回切换,可以任务1线执行3秒,再任务2执行1秒,此时任务2已执行完,再切换回去执行任务1,不必等到任务1执行完毕后在执行任务2,运行的效率得到提升,使用多线程,也就是使用异步



开发多线程

实现多线程编程的方式主要有两种,一种是继承Thread类,一种是实现Runnable接口

继承Thread类

代码1-1

public class ThreadDemo extends Thread {


	@Override
	public void run() {
		System.out.println("线程运行");
	}


	public static void main(String[] args) {
		Thread thread = new ThreadDemo();
		thread.start();
		System.out.println("程序结束");
	}


}


代码1-1运行结果:

程序结束
线程运行


如代码1-1所示,我们创建一个ThreadDemo对象,但并没有调用run()方法,而仅仅是调用start(),控制台打印程序结束和线程运行,调用start()可以使线程准备运行,另外也可以得出线程开始运行后,调用run()方法比较晚


实现Runnable接口

代码1-2

public class RunnableDemo implements Runnable {


	@Override
	public void run() {
		System.out.println("线程运行");
	}


	public static void main(String[] args) {
		RunnableDemo demo = new RunnableDemo();
		Thread thread = new Thread(demo);
		thread.start();
		System.out.println("程序结束");
	}


}



代码1-2运行结果:

程序结束
线程运行


实现Runnable接口同样需要以参数传入Thread类的构造方法构造一个Thread对象,然后再调用start()方法启动线程,可以看到代码1-1和代码1-2结果一致,另外如果一个Thread对象多次调用start()方法将会报错


线程调用的随机性

Thread对象调用start()方法后,会通知“线程规划器”此线程已准备就绪,等待调用线程对象的run()方法,而即便线程开始执行调用run()方法的中途,也会切换到其他线程

代码1-3

public class NumThread extends Thread {


	private int count = 0;


	@Override
	public void run() {
		for (int i = 0; i < 3; i++) {
			System.out.println(Thread.currentThread().getName() + "打印:" + count++);
		}
	}


	public static void main(String[] args) {
		Thread[] threads = new Thread[3];
		for (int i = 0; i < 3; i++) {
			threads[i] = new NumThread();
			System.out.println(threads[i].getName() + "构造成功");
		}
		for (int i = 0; i < 3; i++) {
			threads[i].start();
		}
	}


}


代码1-3运行结果:

Thread-0构造成功
Thread-1构造成功
Thread-2构造成功
Thread-0打印:0
Thread-1打印:0
Thread-1打印:1
Thread-0打印:1
Thread-2打印:0
Thread-0打印:2
Thread-1打印:2
Thread-2打印:1
Thread-2打印:2


java.lang.Thread.currentThread()方法是获取当前的线程,而java.lang.Thread.getName()是获取当前线程的名称,另外Thread对象也可以使用java.lang.Thread.setName(String)方法设置线程的名称,如代码1-3运行结果所示,每个线程虽然都从0打印到2,但却是线程间来回切换执行


线程安全

代码1-3中,虽然3个线程执行的顺序不一样,但每个线程最终都是从0打印到2,因为每个线程都单独享有一个count变量,如果count变量不再是实例变量,而是静态变量,在对其进行计算时,可能出现线程安全问题


代码1-4

class Num {
	private int num = 5;

	public void reduce() {
		num--;
		System.out.println(Thread.currentThread().getName() + "计算count:" + num);
	}
}

class NumReduceThread extends Thread {
	private Num num;

	public NumReduceThread(Num num) {
		super();
		this.num = num;
	}

	@Override
	public void run() {
		num.reduce();
	}

}

public class NumReduce {

	public static void main(String[] args) {
		Num num = new Num();
		for (int i = 0; i < 5; i++) {
			new NumReduceThread(num).start();
		}
	}

}

代码1-4运行结果:

Thread-1计算count:2
Thread-2计算count:2
Thread-4计算count:0
Thread-3计算count:1
Thread-0计算count:2


由代码1-4运行结果可以看到,线程Thread-1、线程Thread-2、线程Thread-0打印出的count的值都是2,说明这3个线程同时对count进行处理,产生非线程安全问题,而我们希望的结果是count依次递减


修改reduce()方法为其添加关键字synchronized如代码1-5

代码1-5

	public synchronized void reduce() {
		num--;
		System.out.println(Thread.currentThread().getName() + "计算count:" + num);
	}



修改为代码1-5后运行结果:

Thread-0计算count:4
Thread-3计算count:3
Thread-4计算count:2
Thread-2计算count:1
Thread-1计算count:0


在reduce()方法前加入synchronized关键字,使多个线程在执行reduce()方法时得以排队处理。当一个线程调用reduce()方法时会先判断有没有被上锁,如果上锁,说明有其他线程正在调用reduce()方法,必须等待其他线程执行完reduce()方法后释放锁,才可以获得锁再执行reduce()方法。synchronized可以对任意对象及方法上锁,而加锁的这段代码称为“互斥区”或“临界区”


方法java.lang.Thread.isAlive()的作用是判断线程是否处于活动状态

方法java.lang.Thread.sleep(long)的作用是在指定的毫秒数内让当前正在执行的线程暂停执行

方法java.lang.Thread.getId()的作用是取得线程的唯一标识

代码1-6

class AliveThread extends Thread {
	@Override
	public void run() {
		try {

			System.out.println("线程id" + getId() + "开始运行");
			System.out.println("线程id" + getId() + "执行状态1:" + isAlive());
			Thread.sleep(1000);
			System.out.println("线程id" + getId() + "暂停1s后结束");
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

public class Alive {

	public static void main(String[] args) throws InterruptedException {
		AliveThread thread = new AliveThread();
		thread.start();
		Thread.sleep(100);
		System.out.println("线程id" + thread.getId() + "执行状态2:" + thread.isAlive());
		Thread.sleep(2000);
		System.out.println("线程id" + thread.getId() + "执行状态3:" + thread.isAlive());
	}

}

代码1-6运行结果:

线程id8开始运行
线程id8执行状态1:true
线程id8执行状态2:true
线程id8暂停1s后结束
线程id8执行状态3:false



java.lang.Thread.interrupt()为线程打上中断标记,而非中断线程,如果要中断线程需要另外的条件判断

java.lang.Thread.interrupted()测试当前线程是否已中断,执行后具有将状态标识清除为false的功能

java.lang.Thread.isInterrupted()测试线程是否已中断,但不清除标识


代码1-7
public class InterruptedThread extends Thread {
	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName() + "中断状态1:" + Thread.interrupted());
		for (int i = 0; i < Integer.MAX_VALUE / 100; i++) {
			Math.random();
		}
		System.out.println(Thread.currentThread().getName() + "中断状态2:" + Thread.interrupted());
		System.out.println(Thread.currentThread().getName() + "中断状态3:" + Thread.interrupted());
	}

	public static void main(String[] args) throws InterruptedException {
		InterruptedThread thread = new InterruptedThread();
		thread.start();
		Thread.sleep(100);
		thread.interrupt();
		System.out.println("thread执行中断");
	}
}

代码1-7运行结果:
Thread-0中断状态1:false
thread执行中断
Thread-0中断状态2:true
Thread-0中断状态3:false

由代码1-7可以看到,当thread执行interrupt()中断方法后,线程内的Thread.interrupted()返回的是true,但在调用一次Thread.interrupted()返回的是false,是因为interrupted()除了判断当前线程是否中断wait,还会将中断状态由该方法清除,也就是连续调用两次,第二次将返回false



代码1-8

public class IsInterruptedThread extends Thread {

	@Override
	public void run() {
		System.out.println(getName() + "中断状态1:" + isInterrupted());
		for (int i = 0; i < Integer.MAX_VALUE / 100; i++) {
			Math.random();
		}
		System.out.println(getName() + "中断状态4:" + isInterrupted());
		System.out.println(getName() + "执行interrupted(),返回中断状态5:" + Thread.interrupted());
		System.out.println(getName() + "中断状态6:" + isInterrupted());
	}

	public static void main(String[] args) throws InterruptedException {
		IsInterruptedThread thread = new IsInterruptedThread();
		thread.start();
		Thread.sleep(100);
		System.out.println(thread.getName() + "执行中断");
		thread.interrupt();
		System.out.println(thread.getName() + "中断状态2:" + thread.isInterrupted());
		System.out.println(thread.getName() + "中断状态3:" + thread.isInterrupted());

	}

}

代码1-8运行结果:

Thread-0中断状态1:false
Thread-0执行中断
Thread-0中断状态2:true
Thread-0中断状态3:true
Thread-0中断状态4:true
Thread-0执行interrupted(),返回中断状态5:true
Thread-0中断状态6:false


由代码1-8运行结果的中断状态5和中断状态6可以看到,运行Thread.interrupted(),会将原先的中断标识清除为false


暂停中中断

如果某一线程处于sleep状态下,调用该线程的中断方法,线程停止休眠将抛出中断异常

代码1-9

class SleepInterruptThread extends Thread {
	@Override
	public void run() {
		try {
			System.out.println(Thread.currentThread().getName() + "开始运行");
			Thread.sleep(1000 * 10);
			System.out.println(Thread.currentThread().getName() + "暂停10s后结束");
		} catch (InterruptedException e) {
			System.out.println(Thread.currentThread().getName() + "进入异常");
			e.printStackTrace();
		}
	}

}

public class SleepInterrupt {

	public static void main(String[] args) throws InterruptedException {
		SleepInterruptThread thread = new SleepInterruptThread();
		thread.start();
		Thread.sleep(1000);
		System.out.println(thread.getName() + "调用中断");
		thread.interrupt();
	}

}

代码1-9运行结果:

Thread-0开始运行
Thread-0调用中断
Thread-0进入异常
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.jerry.ch1.SleepInterruptThread.run(SleepInterrupt.java:8)


循环停止

代码1-10

class CycleInterruptThread extends Thread {


	private int count = 0;


	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName() + "开始运行");
		while (!interrupted()) {
			System.out.println(Thread.currentThread().getName() + "打印count:" + count++);
			// for循环为耗时操作
			for (int i = 0; i < Integer.MAX_VALUE / 100; i++) {
				Math.random();
			}
		}
		System.out.println(Thread.currentThread().getName() + "被中断,结束运行");
	}
}


public class CycleInterrupt {


	public static void main(String[] args) throws InterruptedException {
		CycleInterruptThread thread = new CycleInterruptThread();
		thread.start();
		Thread.sleep(1000 * 10);
		System.out.println(Thread.currentThread().getName() + "调用中断方法");
		thread.interrupt();
	}


}



代码1-10运行结果:

Thread-0开始运行
Thread-0打印count:0
Thread-0打印count:1
Thread-0打印count:2
Thread-0打印count:3
Thread-0打印count:4
Thread-0打印count:5
Thread-0打印count:6
Thread-0打印count:7
Thread-0打印count:8
Thread-0打印count:9
Thread-0打印count:10
main调用中断方法
Thread-0被中断,结束运行


线程优先级

在操作系统中,线程可以划分优先级,优先级较高的线程有较大的希望可以得到CPU资源,也就是CPU优先执行的对象,优先级分为1~10这10个等级,如果是小于1或者大于10,则会抛出异常

public final static int MIN_PRIORITY = 1;

public final static int NORM_PRIORITY = 5;

public final static int MAX_PRIORITY = 10;

线程的优先级具有继承性,如果A线程启动B线程,如果不明确指定B线程的优先级,B线程的优先级与A线程是一样的

代码1-11

class ThreadA extends Thread {

	private ThreadB threadB;

	public ThreadA(String name, ThreadB threadB) {
		super(name);
		this.threadB = threadB;
	}

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName() + "优先级:" + getPriority());
		threadB.start();
	}
}

class ThreadB extends Thread {

	public ThreadB(String name) {
		super(name);
	}

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName() + "优先级:" + getPriority());
	}
}

class ThreadC extends Thread {
	private ThreadD threadD;

	public ThreadC(String name, ThreadD threadD) {
		super(name);
		this.threadD = threadD;
	}

	@Override
	public void run() {
		threadD.start();
		System.out.println(Thread.currentThread().getName() + "优先级:" + getPriority());
	}
}

class ThreadD extends Thread {

	public ThreadD(String name) {
		super(name);
		// TODO Auto-generated constructor stub
	}

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName() + "优先级:" + getPriority());
		Thread.currentThread().setPriority(MAX_PRIORITY);
		System.out.println("将" + Thread.currentThread().getName() + "优先级设置为" + getPriority());
		System.out.println("线程E由线程D启动");
		new ThreadE("线程E").start();
	}
}

class ThreadE extends Thread {

	public ThreadE(String name) {
		super(name);
		// TODO Auto-generated constructor stub
	}

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName() + "优先级:" + getPriority());
	}
}

public class PriorityExtends {

	public static void main(String[] args) throws InterruptedException {
		ThreadB threadB = new ThreadB("线程B");
		ThreadA threadA = new ThreadA("线程A", threadB);
		threadA.start();
		Thread.sleep(100);
		Thread.currentThread().setPriority(8);
		ThreadD threadD = new ThreadD("线程D");
		ThreadC threadC = new ThreadC("线程C", threadD);
		threadC.start();
	}
}

代码1-11运行结果:

线程A优先级:5
线程B优先级:5
线程C优先级:8
线程D优先级:8
将线程D优先级设置为10
线程E由线程D启动
线程E优先级:10


代码1-12

import java.util.Map;
import java.util.TreeMap;

public class PriorityThread extends Thread {

	private long count = 0;

	@Override
	public void run() {
		while (!interrupted()) {
			count++;
		}
	}

	public long getCount() {
		return count;
	}

	public static void main(String[] args) throws InterruptedException {
		PriorityThread[] threads = new PriorityThread[10];
		for (int i = 0; i < 10; i++) {
			threads[i] = new PriorityThread();
			threads[i].setPriority(i + 1);
		}
		for (int i = 0; i < 10; i++) {
			threads[i].start();
		}
		Thread.sleep(1000);
		for (int i = 0; i < 10; i++) {
			threads[i].interrupt();
		}
		TreeMap<Long, PriorityThread> threadMap = new TreeMap<Long, PriorityThread>();
		for (int i = 0; i < 10; i++) {
			threadMap.put(threads[i].getCount(), threads[i]);
		}
		for (Map.Entry<Long, PriorityThread> entry : threadMap.entrySet()) {
			System.out.println("count值:" + entry.getKey() + "  线程名称:" + entry.getValue().getName() + "  线程优先级:" + entry.getValue().getPriority());
		}
	}

}

代码1-12运行结果:

count值:2219601  线程名称:Thread-0  线程优先级:1
count值:2418380  线程名称:Thread-1  线程优先级:2
count值:6557231  线程名称:Thread-3  线程优先级:4
count值:10153073  线程名称:Thread-2  线程优先级:3
count值:11196754  线程名称:Thread-4  线程优先级:5
count值:11611442  线程名称:Thread-6  线程优先级:7
count值:11978366  线程名称:Thread-7  线程优先级:8
count值:12100987  线程名称:Thread-5  线程优先级:6
count值:12303622  线程名称:Thread-8  线程优先级:9
count值:12406222  线程名称:Thread-9  线程优先级:10


如代码1-12可以看到,较高的优先级的count值更高,但优先级的执行也具有随机性,例如优先级为3的count值比优先级为4的count的值来的大,优先级为6的count值比优先级为7和8的count的值来的大


守护进程

Java线程中有两种线程,一种是用户线程,一种是守护线程

守护线程是一种特殊的线程,当进程不存在非守护线程了,则守护线程自动销毁。典型的守护线程就是垃圾回收线程,当进程中没有非守护线程了,则垃圾回收线程也就没有存在的必要,自动销毁。将方法java.lang.Thread.setDaemon(boolean)设置为true则是将线程设置为守护线程,默认为false

代码1-13

public class DaemonThread extends Thread {

	private long count = 0;

	@Override
	public void run() {
		for (int i = 0; i < Integer.MAX_VALUE; i++) {
			count++;
		}
	}

	public long getCount() {
		return count;
	}

	public static void main(String[] args) throws InterruptedException {
		DaemonThread[] threads = new DaemonThread[6];
		for (int i = 0; i < 6; i++) {
			threads[i] = new DaemonThread();
			threads[i].setDaemon(true);// 将线程设置为守护线程,默认为false
			threads[i].start();
		}
		Thread.sleep(300);
		for (int i = 0; i < 6; i++) {
			System.out.println(threads[i].getName() + " count:" + threads[i].getCount());
		}
	}

}

代码1-13运行结果:

Thread-0 count:167638707
Thread-1 count:168747478
Thread-2 count:145635422
Thread-3 count:148750296
Thread-4 count:150255081
Thread-5 count:145468478


yield方法

java.lang.Thread.yield()方法的作用是使当前线程放弃CPU资源,将它让给其他任务去占用CPU资源,但放弃时间不定,可能刚放弃,马上又获得CPU资源

代码1-14

public class YieldThread extends Thread {

	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + "打印i:" + i);
			if (i == 5) {
				Thread.yield();
			}
		}
	}
	
	public static void main(String[] args) {
		new YieldThread().start();
		new YieldThread().start();
	}

}

代码-14运行结果:

Thread-0打印i:0
Thread-1打印i:0
Thread-0打印i:1
Thread-1打印i:1
Thread-0打印i:2
Thread-1打印i:2
Thread-0打印i:3
Thread-1打印i:3
Thread-0打印i:4
Thread-1打印i:4
Thread-0打印i:5
Thread-1打印i:5
Thread-0打印i:6
Thread-1打印i:6
Thread-1打印i:7
Thread-1打印i:8
Thread-1打印i:9
Thread-0打印i:7
Thread-0打印i:8
Thread-0打印i:9


当i等于5的时候,线程会放弃CPU资源,交由其他线程执行,但也有可能刚放弃又马上获得CPU资源



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值