进程:进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列,进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。
线程:是一条执行路径,是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位,一个进程可以由很多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理。
一个正在运行的软件(如迅雷)就是一个进程,一个进程可以同时运行多个任务( 迅雷软件可以同时下载多个文件,每个下载任务就是一个线程), 可以简单的认为进程是线程的集合。
线程是一条可以执行的路径。
对于单核CPU而言:多线程就是一个CPU在来回的切换,在交替执行。
对于多核CPU而言:多线程就是同时有多条执行路径在同时(并行)执行,每个核执行一个线程,多个核就有可能是一块同时执行的。
进程与线程的关系:
-
一个程序就是一个进程,而一个程序中的多个任务则被称为线程。进程是表示资源分配的基本单位,又是调度运行的基本单位。,亦即执行处理机调度的基本单位。 进程和线程的关系:
-
一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程是操作系统可识别的最小执行和调度单位。
-
资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量,即每个线程都有自己的堆栈和局部变量。
-
处理机分给线程,即真正在处理机上运行的是线程。
-
线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
为什么要使用多线程:
生活案例:加入学生A扫完1间教室需要1个小时,校长要求学生A一小时之内扫完四间教室,学生A该怎么办,此时学生A可以叫上学生BCD一起打扫教室,就可以一小时内扫完四间教室。
多线程的应用场景:
1.顺序编程:程序从上往下执行,第一行代码没有执行完,第二行只能等待第一行代码执行完才能继续执行。
/*
PACKAGE_NAME:PACKAGE_NAME
USER:18413
DATE:2021/10/15 11:49
PROJECT_NAME:untitled
面向代码面向君,不负代码不负卿————蒋明辉 */
public class MyThread extends Thread {
public static void main(String[] args) throws InterruptedException {
eat();
sleep();
}
public static void eat() throws InterruptedException {
System.out.println("我先吃早餐");
Thread.sleep(5000);
System.out.println("我再吃晚餐");
}
public static void sleep() throws InterruptedException {
System.out.println("我先睡一觉");
Thread.sleep(5000);
System.out.println("我醒了");
}
}
并发编程
/*
PACKAGE_NAME:PACKAGE_NAME
USER:18413
DATE:2021/10/15 11:49
PROJECT_NAME:untitled
面向代码面向君,不负代码不负卿————蒋明辉 */
import java.util.Date;
public class Test {
public static void main(String[] args){
new Eat().start();
new Sleep().start();
}
}
class Eat extends Thread {
@Override
public void run() {
System.out.println("开始吃饭?...\t" + new Date());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束吃饭?...\t" + new Date());
}
}
class Sleep extends Thread {
@Override
public void run() {
System.out.println("开始睡觉?...\t" + new Date());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束睡觉?...\t" + new Date());
}
}
实现多线程的几种方式:
1.继承Thread类
Thread源码:
public class Thread implements Runnable{
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
public static void sleep(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
sleep(millis);
}
}
实现代码:
/*
PACKAGE_NAME:PACKAGE_NAME
USER:18413
DATE:2021/10/15 11:49
PROJECT_NAME:untitled
面向代码面向君,不负代码不负卿————蒋明辉 */
public class MyThread extends Thread {
public static void main(String[] args) throws InterruptedException {
new MyThread().start();
new MyThread().start();
new MyThread().start();
new MyThread().start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
2.实现Runnabl接口
Runnable接口源码:
/*
* Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang;
/**
* The <code>Runnable</code> interface should be implemented by any
* class whose instances are intended to be executed by a thread. The
* class must define a method of no arguments called <code>run</code>.
* <p>
* This interface is designed to provide a common protocol for objects that
* wish to execute code while they are active. For example,
* <code>Runnable</code> is implemented by class <code>Thread</code>.
* Being active simply means that a thread has been started and has not
* yet been stopped.
* <p>
* In addition, <code>Runnable</code> provides the means for a class to be
* active while not subclassing <code>Thread</code>. A class that implements
* <code>Runnable</code> can run without subclassing <code>Thread</code>
* by instantiating a <code>Thread</code> instance and passing itself in
* as the target. In most cases, the <code>Runnable</code> interface should
* be used if you are only planning to override the <code>run()</code>
* method and no other <code>Thread</code> methods.
* This is important because classes should not be subclassed
* unless the programmer intends on modifying or enhancing the fundamental
* behavior of the class.
*
* @author Arthur van Hoff
* @see java.lang.Thread
* @see java.util.concurrent.Callable
* @since 1.0
*/
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
实现源码:
/*
PACKAGE_NAME:PACKAGE_NAME
USER:18413
DATE:2021/10/15 11:49
PROJECT_NAME:untitled
面向代码面向君,不负代码不负卿————蒋明辉 */
public class MyThread02 implements Runnable{
int num=100;
public static void main(String[] args){
MyThread02 myThread01 = new MyThread02();
MyThread02 myThread02 = new MyThread02();
new Thread(myThread01).start();
new Thread(myThread02).start();
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+" "+num--);
}
}
}
3.实现Callable接口
public class Main {
public static void main(String[] args) throws Exception {
// 将Callable包装成FutureTask,FutureTask也是一种Runnable
MyCallable callable = new MyCallable();
FutureTask<Integer> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
// get方法会阻塞调用的线程
Integer sum = futureTask.get();
System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() + "=" + sum);
}
}
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId() + "\t" + new Date() + " \tstarting...");
int sum = 0;
for (int i = 0; i <= 100000; i++) {
sum += i;
}
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId() + "\t" + new Date() + " \tover...");
return sum;
}
}
线程的状态:
-
创建
-
就绪:调用了start方法,等待CPU调度
-
运行:调用run方法
-
阻塞:暂时停止线程,wait,sleep,join
-
死亡:线程销毁(线程正常执行完毕,发生异常或者被打断interrupt导致线程终止)
线程的常用方法:
public class Thread implements Runnable {
// 线程名字
private volatile String name;
// 线程优先级(1~10)
private int priority;
// 守护线程
private boolean daemon = false;
// 线程id
private long tid;
// 线程组
private ThreadGroup group;
// 预定义3个优先级
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;
// 构造函数
public Thread();
public Thread(String name);
public Thread(Runnable target);
public Thread(Runnable target, String name);
// 线程组
public Thread(ThreadGroup group, Runnable target);
// 返回当前正在执行线程对象的引用
public static native Thread currentThread();
// 启动一个新线程
public synchronized void start();
// 线程的方法体,和启动线程没毛关系
public void run();
// 让线程睡眠一会,由活跃状态改为挂起状态
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos) throws InterruptedException;
// 打断线程 中断线程 用于停止线程
// 调用该方法时并不需要获取Thread实例的锁。无论何时,任何线程都可以调用其它线程的interruptf方法
public void interrupt();
public boolean isInterrupted()
// 线程是否处于活动状态
public final native boolean isAlive();
// 交出CPU的使用权,从运行状态改为挂起状态
public static native void yield();
public final void join() throws InterruptedException
public final synchronized void join(long millis)
public final synchronized void join(long millis, int nanos) throws InterruptedException
// 设置线程优先级
public final void setPriority(int newPriority);
// 设置是否守护线程
public final void setDaemon(boolean on);
// 线程id
public long getId() { return this.tid; }
// 线程状态
public enum State {
// new 创建
NEW,
// runnable 就绪
RUNNABLE,
// blocked 阻塞
BLOCKED,
// waiting 等待
WAITING,
// timed_waiting
TIMED_WAITING,
// terminated 结束
TERMINATED;
}
}
具体的方法可以查看java开发文档
线程安全问题:多个线程操作同一资源的时候会出现线程安全的问题
/*
PACKAGE_NAME:PACKAGE_NAME
USER:18413
DATE:2021/10/15 11:49
PROJECT_NAME:untitled
面向代码面向君,不负代码不负卿————蒋明辉 */
public class MyThread02 implements Runnable{
int tickets=100;
public static void main(String[] args){
MyThread02 myThread01 = new MyThread02();
new Thread(myThread01).start();
new Thread(myThread01).start();
}
@Override
public void run() {
while(true){
if(tickets>0){
System.out.println(Thread.currentThread().getName()+":"+tickets--);
}else {
break;
}
}
}
}
会出现重复操作资源的现象。
如何解决:
同步代码块:
/*
PACKAGE_NAME:PACKAGE_NAME
USER:18413
DATE:2021/10/15 11:49
PROJECT_NAME:untitled
面向代码面向君,不负代码不负卿————蒋明辉 */
public class MyThread02 implements Runnable{
int tickets=100;
public static void main(String[] args){
MyThread02 myThread = new MyThread02();
new Thread(myThread).start();
new Thread(myThread).start();
}
@Override
public void run() {
while(true){
synchronized (this){
if(tickets>0){
System.out.println(Thread.currentThread().getName()+":"+tickets);
tickets--;
}
}
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yRxHjiyy-1634299373537)(C:\Users\18413\AppData\Roaming\Typora\typora-user-images\image-20211015182033801.png)]
同步方法:
未使用同步方法:
/*
PACKAGE_NAME:PACKAGE_NAME
USER:18413
DATE:2021/10/15 18:32
PROJECT_NAME:untitled
面向代码面向君,不负代码不负卿————蒋明辉 */
public class SynchronizedTest02 {
public void method1() {
System.out.println("Method 1 start");
try {
System.out.println("Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 1 end");
}
public void method2() {
System.out.println("Method 2 start");
try {
System.out.println("Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 2 end");
}
public static void main(String[] args) {
final SynchronizedTest02 test = new SynchronizedTest02();
new Thread(test::method1).start();
new Thread(test::method2).start();
}
}
使用同步方法:
/*
PACKAGE_NAME:PACKAGE_NAME
USER:18413
DATE:2021/10/15 18:32
PROJECT_NAME:untitled
面向代码面向君,不负代码不负卿————蒋明辉 */
public class SynchronizedTest {
public synchronized void method1() {
System.out.println("Method 1 start");
try {
System.out.println("Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 1 end");
}
public synchronized void method2() {
System.out.println("Method 2 start");
try {
System.out.println("Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 2 end");
}
public static void main(String[] args) {
final SynchronizedTest test = new SynchronizedTest();
new Thread(test::method1).start();
new Thread(test::method2).start();
}
}
当两个线程去操作同一方法时:
/*
PACKAGE_NAME:PACKAGE_NAME
USER:18413
DATE:2021/10/15 18:32
PROJECT_NAME:untitled
面向代码面向君,不负代码不负卿————蒋明辉 */
public class SynchronizedTest {
public synchronized void method1() {
System.out.println("Method 1 start");
try {
System.out.println("Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 1 end");
}
public synchronized void method2() {
System.out.println("Method 2 start");
try {
System.out.println("Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 2 end");
}
public static void main(String[] args) {
final SynchronizedTest test1 = new SynchronizedTest();
final SynchronizedTest test2 = new SynchronizedTest();
new Thread(test1::method1).start();
new Thread(test2::method2).start();
}
}
结果:两个线程执行时不会互相干扰执行,互不影响
死锁:图中的小明和小王,小王想要手里的遥控飞机,小明想要小王手里的遥控汽车,但是小王和小明拿着手里的东西不肯放手,造成了生活中常见的死锁问题。
死锁即是多个线程执行时,互相持有对方线程所需的资源,且保持自己的资源不释放。
死锁产生的条件:
-
互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
-
请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
-
不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
-
循环等待条件:在发生死锁时,线程之间存在着循环等待,等待对方释放资源。
解决死锁的基本方法:
- 破坏请求条件:资源一次性分配,一次性分配所有资源
- 破坏请求保持条件:自要有一个资源得不到分配,也不会个这个线程分配其他资源
- 破坏不剥夺条件:当前线程获取了部分资源,但是得不到其他资源的时候,线程释放其占有的资源