多线程
@
总结内容
1. 进程是什么,线程又是什么
进程就是正在运行的程序,是操作系统运行程序是产生的,它是独立存在的,进程之间互不干扰。
线程是进程的一个子集,是进程中的实际运作单位,开发人员可以通过操作线程进行多处理器编程。
2. 进程和线程的区别
- 根本区别:
进程是操作系统分配资源的基本单位;
线程是CPU调度和执行的基本单位; - 其他区别:
操作系统中可以同时运行多个进程,一个进程又可以执行多个线程。系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系
统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源。
3. 为什么要学习多线程
1)在耗时的操作时使用线程,可以提高程序运行的速度
2)在并行操作时使用线程,提高并行的速度
3)多CPU系统中使用线程,提高CPU利用率
4)改善程序结构,对一个程序进行拆分,降低程序执行难度
5)进程之间的通信十分不方便,同一个进程中的线程可以进行数据共享
4. 实现多线程的方式
在 java 中实现线程的方式有 2 种,一种是继承 Thread,一种是实现 Runnable 接口。
- 通过继承Thread实现:
继承 Thread 实现多线程,必须重写 run 方法。
继承语法:
class MyThread extends Thread{
private int ticket = 5;
public void run(){
for (int i=0;i<10;i++)
{
if(ticket > 0){
System.out.println("ticket = " + ticket--);
}
}
}
}
public class ThreadDemo{
public static void main(String[] args){
new MyThread().start();
new MyThread().start();
new MyThread().start();
}
}
运行结果:
ticket = 5
ticket = 4
ticket = 5
ticket = 5
ticket = 4
ticket = 3
ticket = 2
ticket = 1
ticket = 4
ticket = 3
ticket = 3
ticket = 2
ticket = 1
ticket = 2
ticket = 1
每个线程单独卖了5张票,即独立的完成了买票的任务,但实际应用中,比如火车站售票,需要多个线程去共同完成任务,在本例中,即多个线程共同买5张票。
- 通过实现Runnable:
实现 Runnable 接口,必须重写 run 方法。
实现语法:
class MyThread implements Runnable{
private int ticket = 5;
public void run(){
for (int i=0;i<10;i++)
{
if(ticket > 0){
System.out.println("ticket = " + ticket--);
}
}
}
}
public class RunnableDemo{
public static void main(String[] args){
MyThread my = new MyThread();
new Thread(my).start();
new Thread(my).start();
new Thread(my).start();
}
}
运行结果:
ticket = 5
ticket = 2
ticket = 1
ticket = 3
ticket = 4
在第二种方法(Runnable接口实现多线程)中,买票输出的顺序并不是54321,这是因为线程执行时会被抢占资源,所以输出结果不唯一。ticket并不是原子操作。
- 总结:
在第一种方法中,我们new了3个Thread对象,即三个线程分别执行三个对象中的代码,因此便是三个线程去独立地完成卖票的任务;而在第二种方法中,我们同样也new了3个Thread对象,但只有一个Runnable对象,3个Thread对象共享这个Runnable对象中的代码,因此,便会出现3个线程共同完成卖票任务的结果。如果我们new出3个Runnable对象,作为参数分别传入
5. 两种实现多线程方法的区别
-
区别一:
Runnable是使用实现接口的方式来实现多线程的,Thread是通过继承的方式来实现多线程的
Runnable实现接口的方式更好,推荐使用,主要原因是因为实现接口的方式有利于解耦,方便代码的维护工作 -
区别二:
Java只支持“接口”的多继承,不支持“类“”的多继承;而继承在java中具有单根性,子类只能继承一个父类,一句话就是单继承多实现 -
区别三:
Runnable增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的 -
区别四:
Runnable适合多个相同程序的线程区处理同一资源的情况
6. 多线程的同步
- 原子性操作概念:
如果希望一系列操作(在代码中可以认为是很多句语句),要么都执行,要么都不执行,我们把这种操作叫做原子性操作。 - 同步方式
(1)、通过synchronized关键字同步对象的方式实现(synchronized(同步对象){ // 需要同步的代码 })
public class SynchronizedDemo implements Runnable {
private String name;
static Integer count = 20;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static Integer getCount() {
return count;
}
public static void setCount(Integer count) {
SynchronizedDemo.count = count;
}
public SynchronizedDemo() {
}
public SynchronizedDemo(String name) {
this.name = name;
}
@Override
public void run() {
while (count > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (this) {
if (count > 0) {
count--;
System.out.println(Thread.currentThread() + " 开始吃苹果, 还剩" + count + "个苹果");
}
}
}
}
}
public Test {
public static void main(String[] args) {
SynchronizedDemo sd = new SynchronizedDemo();
new Thread(sd, "光头强").start();
new Thread(sd, "熊大").start();
new Thread(sd, "熊二").start();
}
}
运行结果:
"C:\Program Files\Java\jdk-11.0.9\bin\java.exe"
Thread[光头强,5,main] 开始吃苹果, 还剩4个苹果
Thread[熊二,5,main] 开始吃苹果, 还剩3个苹果
Thread[熊大,5,main] 开始吃苹果, 还剩2个苹果
Thread[熊大,5,main] 开始吃苹果, 还剩1个苹果
Thread[熊二,5,main] 开始吃苹果, 还剩0个苹果
- 通过同步方法实现(synchronized 方法返回类型方法名(参数列表){// 其他代码})
public class SynchronizedDemo implements Runnable {
private String name;
static Integer count = 5;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static Integer getCount() {
return count;
}
public static void setCount(Integer count) {
SynchronizedDemo.count = count;
}
public SynchronizedDemo() {
}
public SynchronizedDemo(String name) {
this.name = name;
}
@Override
public synchronized void run() {
while (count > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count > 0) {
count--;
System.out.println(Thread.currentThread() + " 开始吃苹果, 还剩" + count + "个苹果");
}
}
}
}
public Test {
public static void main(String[] args) {
SynchronizedDemo sd = new SynchronizedDemo();
new Thread(sd, "光头强").start();
new Thread(sd, "熊大").start();
new Thread(sd, "熊二").start();
}
}
运行结果:
"C:\Program Files\Java\jdk-11.0.9\bin\java.exe"
Thread[光头强,5,main] 开始吃苹果, 还剩4个苹果
Thread[熊二,5,main] 开始吃苹果, 还剩3个苹果
Thread[熊大,5,main] 开始吃苹果, 还剩2个苹果
Thread[熊大,5,main] 开始吃苹果, 还剩1个苹果
Thread[熊二,5,main] 开始吃苹果, 还剩0个苹果
多线程常用方法
(1)wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
(2)sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
(3)notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
(4)Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
总结
以上就是对多线程的总结了,代码仅供参考,欢迎讨论交流。