(1)进程:即正在运行的程序,是操作系统进行资源分配的基本单位。单核CPU情况下,同一时刻只能运行一个进程;宏观上并行,微观上串行。
(2)线程:又称为轻量级进程,是CPU的基本调度单位。
(3)二者的区别:
- 进程是操作系统进行资源分配的基本单位,线程是CPU调度的基本单位。
- 程序是静态的,一般表现为一个或一组文件,进程是动态的,是操作系统内部进行资源申请的最小个体。
- 进程间无法进行数据共享,但是线程之间可以。
- CPU时间片:操作系统会为每个线程分配执行时间。
- 运行数据:
堆空间:存储引用类型数据,多个线程可以共享;
栈空间:存储局部变量,每个线程都有独立的栈空间。
- 继承Thread类
//方式一:继承 Thread,重写run() 方法
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i <50 ; i++) {
System.out.println("子线程正在执行==="+i);
}
}
}
--------------------------------------------------------------------
测试:
public class TestThread {
public static void main(String[] args) {
//创建线程对象
MyThread t1=new MyThread();
//启动线程
t1.start();
for (int i = 0; i <50 ; i++) {
System.out.println("主线程正在执行==="+i);
}
}
}
- 实现Runnable接口
//方式二:实现Runnable接口,重写 run() 方法
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i=1;i<=50;i++){
System.out.println("子线程正在执行==="+i);
}
}
}
--------------------------------------------------------------------
测试:
public class TestRunnable {
public static void main(String[] args) {
//1.创建MyRunnable对象,表示线程要执行的功能
MyRunnable myRunnable =new MyRunnable();
//2.创建线程对象
Thread t1=new Thread(myRunnable,"我的线程");
//3.启动线程
t1.start();
//main线程
for (int i = 1; i <=50 ; i++) {
System.out.println("主线程正在执行==="+i);
}
}
}
4.线程的静态方法
- 线程休眠
public class SleepThread extends Thread{
@Override
public void run() {
for (int i = 1; i <=10 ; i++) {
System.out.println(Thread.currentThread().getName()+"---"+i);
try {
//线程休眠1000ms
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 线程join
public class SleepThread extends Thread{
@Override
public void run() {
for (int i = 1; i <=5 ; i++) {
System.out.println(Thread.currentThread().getName()+"---"+i);
try {
//线程休眠1000ms
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
--------------------------------------------------------------------
测试:
public class TestJoin {
public static void main(String[] args) throws InterruptedException {
JoinThread j1 =new JoinThread();
j1.start();
//join()方法会阻塞主线程,直至 j1 执行完毕,再执行main线程
j1.join();
//主线程
for (int i = 1; i <=5; i++) {
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
---------------------------------------------------------------------
运行结果:
Thread-0---1
Thread-0---2
Thread-0---3
Thread-0---4
Thread-0---5
main---1
main---2
main---3
main---4
main---5
5.守护线程
线程分为两类:用户线程(前台线程)、守护线程(后台线程),如果程序中所有的前台线程都执行完毕了,后台线程会自动结束。(Java中的垃圾回回收器就i属于守护线程)
- 示例代码-
public class DaemonThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"---"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
------------------------------------------------------------------
//测试
public class Test {
public static void main(String[] args) throws InterruptedException {
//创建线程,默认情况下是前台线程
DaemonThread t1=new DaemonThread();
//设置为守护线程
t1.setDaemon(true);
//启动线程
t1.start();
//主线程
for (int i = 0; i < 10; i++) {
System.out.println("主线程"+"---"+i);
Thread.sleep(500);
}
}
}
执行结果:
(1)并行和并发:
- 并行:进程“真”同时在执行,微观角度的同一时刻,有多个指令在执行,只会出现在多CPU多核的场景下。
- 并发:进程“假”同时执行,微观上,表现为一次执行一个进程,但宏观上,多个进程在“同时”执行。
(2)用户态和内核态
- CPU正在执行的是OS的指令时,就进入到内核态。反之,执行的是普通进程的指令时,就是用户态。
- 内核态的指令权限高,所有硬件资源都能访问,用户态的指令权限低,只能访问OS规定的资源。通过系统调用,切换用户态和内核态。
- 用户态的性能较好,内核态的性能较差。
(3)进程状态的转移
进程各状态之间转移的条件:
- 初始状态->就绪状态:进程的初始化工作完成,由操作系统的指令完成。
- 就绪状态->运行状态:进程被OS选中,分配CPU资源后;
- 运行状态->结束状态:进程的最后一条指令执行结束;
- 运行状态->就绪状态:
1.CPU资源被优先级更高的进程抢占;
2.时间片耗尽;
3.进程执行操作系统提供的系统调用,主动放弃; - 运行状态->阻塞状态:
1.等待一些外部条件,如IO设备的输入等;
2.进程进入休眠状态;
… - 阻塞状态->就绪状态:外部条件满足时,IO数据输入;休眠时间到了等
(1)同步代码块
**
* 同步代码块解决线程安全
* 保证线程的原子性
*/
public class Test {
private static int index=0;
public static void main(String[] args) throws InterruptedException {
String[] s =new String[5];
//创建线程
Runnable runnableA =new Runnable() {
@Override
public void run() {
//对临界资源上锁,保证原子性
synchronized (s){
s[index]="hello";
index++;
}
}
};
Runnable runnableB=new Runnable() {
@Override
public void run() {
synchronized (s){
s[index]="world";
index++;
}
}
};
Thread t1 =new Thread(runnableA,"A");
Thread t2 =new Thread(runnableB,"B");
t1.start();
t2.start();
//保证两个线程都执行完毕
t1.join();
t2.join();
System.out.println(Arrays.toString(s));
}
}
(2)同步方法
public class TicketThread implements Runnable{
private int ticket=100;
//创建一个锁
private Object object =new Object();
@Override
public void run() {
while (true){
if ( !sale()){
break;
}
}
}
//非静态方法
//当前锁 this
public synchronized boolean sale(){
if (ticket<=0){
return false;
}
System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票");
ticket--;
return true;
}
//静态方法
//当前锁 类对象 TicketThread.class
public static boolean sale(){
synchronized(TicketThread.class){
if (ticket<=0){
return false;
}
System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票");
ticket--;
return true;
}
}
}
7.多线程的下对[0-40000000]数组排序案例
- 产生随机数组
import java.util.Random;
public class ArrayHelper {
public static long[] generateArray(int n){
Random random =new Random(20220620);
long[] array =new long[n];
for (int i = 0; i < n; i++) {
array[i] =random.nextInt();
}
return array;
}
}
- 单线程下对[0-40000000]排序
public class SingleSort {
public static void main(String[] args) {
//计时
long start =System.currentTimeMillis();
//生成一个40000000的数组
long[] array =ArrayHelper.generateArray(4_000_0000);
//[0,1000_0000]
Arrays.sort(array,0,1000_0000);
//[1000_0001,2000_0000]
Arrays.sort(array,1000_0001,2000_0000);
//[2000_0001,3000_0000]
Arrays.sort(array,2000_0001,3000_0000);
//[3000_0001,4000_0000]
Arrays.sort(array,3000_0001,4000_0000);
long end =System.currentTimeMillis();
long elapsed =end-start;
System.out.println(elapsed+"ms");
}
}
排序耗时:4786ms
- 多线程下对[0-40000000]排序
import java.util.Arrays;
public class ConcurrentSort {
//排序的线程
static class SortWork extends Thread{
private long[] array;
private int fromIndex;
private int toIndex;
public SortWork(long[] array,int fromIndex,int toIndex){
this.array =array;
this.fromIndex =fromIndex;
this.toIndex =toIndex;
}
//排序
@Override
public void run() {
//左闭右开区间上排序
Arrays.sort(array,fromIndex,toIndex);
}
}
public static void main(String[] args) throws InterruptedException {
//计时
long start =System.currentTimeMillis();
//生成一个40000000的数组
long[] array =ArrayHelper.generateArray(4_000_0000);
//四个线程进行同时排序
//[0,1000_0000]
Thread t1 =new SortWork(array,0,1000_0000);
t1.start();
//[1000_0001,2000_0000]
Thread t2 =new SortWork(array,1000_0001,2000_0000);
t2.start();
//[2000_0001,3000_0000]
Thread t3 =new SortWork(array,2000_0001,3000_0000);
t3.start();
//[3000_0001,4000_0000]
Thread t4 =new SortWork(array,3000_0001,4000_0000);
t4.start();
t1.join();
t2.join();
t3.join();
t4.join();
long end =System.currentTimeMillis();
long elapsed =end-start;
System.out.println(elapsed+"ms");
}
}
排序耗时:2684ms
总结,虽然多线程下程序的执行效率要比单线程的执行效率要高,但并带代表线程越多越好,其一,创建线程本身也需要消耗资源,线程数越多,它消耗的cpu资源也越多,其二,线程调度也需要耗时,OS从100个线程中挑一个的时间和从10000个线程中挑一个的时间也是不同的。最后,cpu是公共资源,在设计程序时,也应想到OS中的其他程序。



