(一)Java多线程入门

一、进程和线程

1.1 进程

进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建、运行到消亡的全过程。

1.2 线程

线程与进程类似,但是是比进程更小的执行单位,一个进程在执行过程中可以创建多个线程。与进程不同的是同类的一组线程共享同一块内存区域和一组系统资源。所以系统在产生一个线程,或者在各个线程之间切换时,花销比进程小。也因此,线程被称为轻量级进程。

1.3 为什么需要多线程?

开发高并发系统的基础,为的是提高系统的并发能力和性能。

1.4 为什么提倡多线程而不是多进程?

两点:
1、线程是程序执行的最小单位
2、线程间切换花销比进程小

二、几个重要概念

2.1 同步和异步

同步和异步通常是形容一次方法的调用,同步是需要等待返回之后才能进行后续的操作。异步则不然(比较经典的场景就是消息队列)。

2.2 并发(Concurrency)和并行(Parallelism)

它们都表示两个或者多个任务一起执行。但是偏重点不一样,并发偏重多个任务交替执行,多个任务可能还是串行的。而并行是真正意义上的“同时执行”。
多线程在单核CPU上是顺序执行的,也就是交替执行(并发)。多核CPU的话,因为每个CPU有自己的运算器,所以可以同时执行(并行)。

2.3 高并发

高并发(High Concurrency)是互联网分布式架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。

高并发相关的一些指标:响应时间(Response Time)、吞吐量(Throughout)、每秒查询率QPS(Query Per Second)、并发用户数等。

2.4 临界区

临界区用来表示一种公共资源或者说共享数据,可以被多个线程使用。但是每一次,只能有一个线程使用它,一旦临界区被占用,其他线程要想使用此资源,就等等待。在并发程序中,临界区资源是保护的对象。

2.5 阻塞和非阻塞

非阻塞是指在不能马上得到结果前,该函数不会阻塞当前线程,而会立刻返回,而阻塞则相反。

三、创建多线程的三种方式

前两种实际很少使用,一般使用多线程池的方式比较多。

① 继承Thread类

public class MyThread extends Thread {
	@Override
	public void run() {
		super.run();
		System.out.println("MyThread");
	}
    public static void main(String[] args) {
		MyThread mythread = new MyThread();
        // 启动线程
		mythread.start();
		System.out.println("运行结束");
	}
}

运行结果:

运行结束
MyThread

从上面的运行结果可以看出:线程是一个子任务,CPU以不确定的方式,或者说以随机的时间来调用线程的run方法。

② 实现Runnable接口

推荐实现Runnable接口方式开发多线程,因为Java是单继承,但是可以实现多个接口。

public class MyRunnable implements Runnable {
	@Override
	public void run() {
		System.out.println("MyRunnable");
	}
    public static void main(String[] args) {
		Runnable runnable=new MyRunnable();
		Thread thread=new Thread(runnable);
		thread.start();
		System.out.println("运行结束!");
	}
}

运行结果:

运行结束
MyThread

③ 使用线程池

使用线程池的方式是最推荐的一种方式,另外,《阿里巴巴Java开发手册》强调过,创建线程必须由线程池提供。

在这里插入图片描述

四、实例变量和线程安全

4.1 不共享数据的情况

/**
 * 
 * @author aaa
 * @date 2018年10月30日
 * @Description: 多个线程之间不共享变量,线程安全的情况
 */
public class MyThread extends Thread {

	private int count = 5;

	public MyThread(String name) {
		super();
		this.setName(name);
	}

	@Override
	public void run() {
		super.run();
		while (count > 0) {
			count--;
			System.out.println("由 " + MyThread.currentThread().getName() + " 计算,count=" + count);
		}
	}

	public static void main(String[] args) {
		MyThread a = new MyThread("A");
		MyThread b = new MyThread("B");
		MyThread c = new MyThread("C");
		a.start();
		b.start();
		c.start();
	}
}

运行结果:

由 A 计算,count=4
由 B 计算,count=4
由 C 计算,count=4
由 B 计算,count=3
由 A 计算,count=3
由 B 计算,count=2
由 C 计算,count=3
由 C 计算,count=2
由 C 计算,count=1
由 B 计算,count=1
由 B 计算,count=0
由 A 计算,count=2
由 C 计算,count=0
由 A 计算,count=1
由 A 计算,count=0

可以看到,每个线程都有一个属于自己实例的变量count,它们之间互不影响。

4.2 共享数据的情况

/**
 * 
 * @author aaa
 * @date 2018年10月30日
 * @Description: 多个线程之间共享变量线程不安全的情况
 */
public class SharedVariableThread extends Thread {
	private int count = 5;

	@Override
	public void run() {
		super.run();
		count--;
		System.out.println("由 " + SharedVariableThread.currentThread().getName() + " 计算,count=" + count);
	}

	public static void main(String[] args) {

		SharedVariableThread mythread = new SharedVariableThread();
		// 下列线程都是通过mythread对象创建的
		Thread a = new Thread(mythread, "A");
		Thread b = new Thread(mythread, "B");
		Thread c = new Thread(mythread, "C");
		Thread d = new Thread(mythread, "D");
		Thread e = new Thread(mythread, "E");
		a.start();
		b.start();
		c.start();
		d.start();
		e.start();
	}
}

运行结果:

由 A 计算,count=4
由 D 计算,count=0
由 E 计算,count=1
由 B 计算,count=2
由 C 计算,count=2

可以看出,这里出现了错误,我们想要的是依次递减的结果。为什么呢?

因为在大多数jvm中,count–操作分为如下三步:

  1. 取得原有count值
  2. 计算i-1
  3. 对i进行赋值

所以多个线程同时访问会出问题。

怎么避免 ?

  1. 利用 synchronized 关键字(保证任意时刻只能有一个线程执行该方法)
  2. 利用 AtomicInterger 类(JUC中的Atomic 原子类)

另外这里不能用 volatile 关键字,因为 该关键字只能保证可见性,不能保证原子性。

五、Thread一些常用的方法

5.1 Thread.currentThread()

返回当前执行线程的对象引用

5.2 Thread.currentThread().getId()

返回线程的标识符

5.3 Thread.currentThread().getName()

返回线程的名称

5.4 Thread.currentThread().getPriority()

返回线程的优先级

5.5 Thread.currentThread().isAlive()

测试这个线程是否还处于活动状态

什么事活动状态?

活动状态就是线程已经启动且尚未终止。线程处于正在运行或者准备运行的状态。

5.6 Thread.sleep(long millis)

使当前运行的线程休眠(暂停执行),单位毫秒

5.7 Thread.currentThread().interrupt()

中断这个线程

5.8 Thread.interrupted() 和 Thread.currentThread().isInterrupted()

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

Thread.currentThread().isInterrupted():测试线程Thread是否已经是中断状态,但是不清除状态标志

5.9 Thread.currentThread().setName(String name)

更改线程的名称

5.10 Thread.currentThread().isDaemon()

测试这个线程是否是守护线程

5.11 Thread.currentThread().setDaemon(boolean on)

标记线程为daemon线程或用户线程

5.12 Thread.currentThread().join()

主线程生成并发起子线程,一般子线程用于复杂的计算,这时主线程需要等待子线程执行完成后再结束,就需要join()方法。

5.13 Thread.yield()

放弃当前的CPU资源,也即是让出CPU执行权

5.14 Thread.currentThread().setPriority(int newPriority)

更改线程的优先级

六、如何停止一个线程 ?

stop(),suspend(),resume()(仅用于与suspend()一起使用)这些方法已被弃用
在这里插入图片描述

6.1 使用interrupt()方法

public class MyThread extends Thread {
	@Override
	public void run() {
		super.run();
		for (int i = 0; i < 5000000; i++) {
			System.out.println("i=" + (i + 1));
		}
	}
		public static void main(String[] args) {
		try {
			MyThread thread = new MyThread();
			thread.start();
			Thread.sleep(2000);
			thread.interrupt();
		} catch (InterruptedException e) {
			System.out.println("main catch");
			e.printStackTrace();
		}
	}
}

运行上述代码发现,线程并不会终止

改进一下,用interrupted()方法判断线程是否终止,如是是就break。

public class InterruptThread2 extends Thread {
	@Override
	public void run() {
		super.run();
		for (int i = 0; i < 500000; i++) {
			if (this.interrupted()) {
				System.out.println("已经是停止状态了!我要退出了!");
				break;
			}
			System.out.println("i=" + (i + 1));
		}
		System.out.println("看到这句话说明线程并未终止------");
	}

	public static void main(String[] args) {
		try {
			InterruptThread2 thread = new InterruptThread2();
			thread.start();
			Thread.sleep(2000);
			thread.interrupt();
		} catch (InterruptedException e) {
			System.out.println("main catch");
			e.printStackTrace();
		}
	}
}

for循环虽然停止执行了,但是for循环下面的语句还是会执行,说明线程并未停止。

6.2 使用return停止线程

public class MyThread extends Thread {

	@Override
	public void run() {
			while (true) {
				if (this.isInterrupted()) {
					System.out.println("ֹͣ停止了!");
					return;
				}
				System.out.println("timer=" + System.currentTimeMillis());
			}
	}
	public static void main(String[] args) throws InterruptedException {
		MyThread t=new MyThread();
		t.start();
		Thread.sleep(2000);
		t.interrupt();
	}

}

七 、线程优先级

如果有很多线程处于就绪状态,系统会根据优先级来决定首先使哪个线程进入运行状态。

线程优先级具有继承特性比如A线程启动B线程,则B线程的优先级和A是一样的。

线程优先级具有随机性也就是说线程优先级高的不一定每一次都先执行完。

Thread类中包含的成员变量代表了线程的某些优先级。如Thread.MIN_PRIORITY(常数1),Thread.NORM_PRIORITY(常数5),Thread.MAX_PRIORITY(常数10)。

其中每个线程的优先级都在Thread.MIN_PRIORITY(常数1) 到Thread.MAX_PRIORITY(常数10) 之间,在默认情况下优先级都是Thread.NORM_PRIORITY(常数5)。

线程优先级具有继承特性测试代码:

public class MyThread1 extends Thread {
	@Override
	public void run() {
		System.out.println("MyThread1 run priority=" + this.getPriority());
		MyThread2 thread2 = new MyThread2();
		thread2.start();
	}
}
public class MyThread2 extends Thread {
	@Override
	public void run() {
		System.out.println("MyThread2 run priority=" + this.getPriority());
	}
}
public class Run {
	public static void main(String[] args) {
		System.out.println("main thread begin priority="
				+ Thread.currentThread().getPriority());
		Thread.currentThread().setPriority(6);
		System.out.println("main thread end   priority="
				+ Thread.currentThread().getPriority());
		MyThread1 thread1 = new MyThread1();
		thread1.start();
	}
}

运行结果:

main thread begin priority=5
main thread end   priority=6
MyThread1 run priority=6
MyThread2 run priority=6

MyThread1和MyThread2继承了main thread的优先级6.

八、Java多线程分类

8.1 多线程分类

用户线程:运行在前台,执行具体的任务,比如程序的主线程、连接网络的子线程等

守护线程:运行在后台,为其他前台线程服务,也可以说是非守护线程的“佣人”

特点:一旦所有用户线程都结束运行,守护线程会随JVM一起结束

应用:数据库连接池中的检测线程,JVM虚拟机启动后的检测线程

最常见的守护线程:垃圾回收线程

8.2 如何设置守护线程 ?

可以通过Thread.currentThread().setDaemon(true)设置当前线程为守护线程

注意事项:

  1. setDaemon(true)必须在start()方法前,否则会抛出IllegalThreadStateException异常
  2. 在守护线程中产生的新线程也是守护线程
  3. 不是所有的任务都可以分配给守护线程来执行,比如读写操作或者计算逻辑
public class MyThread extends Thread {
	private int i = 0;

	@Override
	public void run() {
		try {
			while (true) {
				i++;
				System.out.println("i=" + (i));
				Thread.sleep(100);
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
public class Run {
	public static void main(String[] args) {
		try {
			MyThread thread = new MyThread();
			thread.setDaemon(true);
			thread.start();
			Thread.sleep(5000);
			System.out.println("我离开thread对象也不再打印了,也就是停止了!");
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

运行结果:

i=48
i=49
i=50
我离开thread对象也不再打印了,也就是停止了!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值