一、进程的概念
正在运行的程序,是系统进行资源分配的基本单位
目前操作系统都是支持多进程,可以同时执行多个进程,通过进程ID区分
单核CPU在同一时刻,只能有一个进程;宏观并行,微观串行
二、线程的概念
线程,又称为轻量级进程(Light Weight Process)
进程中的一条执行路径,也是CPU的基本调度单位。
一个进程由一个或多个线程组成,彼此间完成不同的工作,同时执行,称为多线程。
三、进程和线程的区别
1.进程是操作系统资源分配的基本单位,而线程是CPU的基本调度单位。
2.一个程序运行后至少有一个进程
3.一个进程可以包含多个线程,但是至少需要有一个线程,否则这个进程是没有意义。
4.进程间不能共享数据段地址,但是同进程的线程之间可以
四、线程的组成
任何一个线程都具有基本的组成部分
CPU时间片: 操作系统(OS)会为每个线程分配执行时间
运行数据:
堆空间: 存储线程需要的对象,多个线程可以共享堆中的数据。
栈空间: 存储线程需使用的局部变量,每个线程都拥有独立的栈。
线程的逻辑代码.
五、线程的特点
1. 线程抢占式执行
效率高
可防止单一线程长时间独占CPU.
2. 在单核CPU中,宏观上同时执行,微观上顺序执行
六、线程的创建方式
1. 【继承Thread类,重写run方法】
2. 【实现Runnable接口】
3. 实现Callable接口
七、创建线程(继承Thread类)
1.创建

2. 获取和设置线程的名称
(1)获取线程ID和线程名称
1. 在Thread的子类中调用this.getId()或this.getName()
2. 使用Thread.currentThread().getId()和
Thread.currentThread().getName()
(2)修改线程名称
1. 调用线程对象的setName()方法
2. 使用线程子类的构造方法赋值
例子:4个窗口各卖100张票
public class Thread_01 extends Thread{
int num=100;
@Override
public void run() {
for (int i=0;i<=100;i++){
System.out.println(Thread.currentThread().getName()+"卖的第"+i+"张票");
}
}
}
public class Test02 {
public static void main(String[] args) {
int num=100;
Thread_01 m1=new Thread_01();
m1.start();
Thread_01 m2=new Thread_01();
m2.start();
Thread_01 m3=new Thread_01();
m3.start();
Thread_01 m4=new Thread_01();
m4.start();
}
}
八、创建线程(实现Runnable接口)
1.创建

2.例子:4个窗口共卖100张票
public class MyRunnable implements Runnable{
static int num=100;
@Override
public void run() {
while (num>0){
num--;
System.out.println(Thread.currentThread().getName()+"还剩"+num+"张票");
}
}
}
public static void main(String[] args) {
MyRunnable myRunnable=new MyRunnable();
Thread t1=new Thread(myRunnable,"售票员1");
Thread t2=new Thread(myRunnable,"售票员2");
Thread t3=new Thread(myRunnable,"售票员3");
Thread t4=new Thread(myRunnable,"售票员4");
t1.start();
t2.start();
t3.start();
t4.start();
}
九、线程的状态

十、线程的常见方法
休眠:
public static void sleep(long millis)
当前线程主动休眠millis毫秒。
package com.gsh.demon_01;
/**
* @Auther: haohao
* @Date:2022/7/1723:40
*/
public class SleepThread extends Thread {
@Override
public void run() {
try {
sleep(500);//休眠(等待500毫秒)
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"这是第"+i+"次执行");
}
}
}
class ThreadTest{
public static void main(String[] args) {
SleepThread sleepThread=new SleepThread();
sleepThread.start();//主线程main会先执行,因为sellp线程每次都休眠500毫秒
for (int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"这是第"+i+"次执行");
}
}
}
放弃:
public static void yield()
当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片
package com.gsh.demon_02;
/**
* @Auther: haohao
* @Date:2022/7/1723:56
*/
public class YieldThread extends Thread{
@Override
public void run() {
for (int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"这是第"+i+"次执行");
Thread.yield();//线程放弃,不去争夺cpu放弃时间片
}
}
}
class ThreadTest{
public static void main(String[] args) {
//虽然线程放弃了当前的时间片回到就绪状态,但是cup还是会选择它,它还是有可能会执行
//不是说放弃了就一定不会执行
YieldThread yieldThread=new YieldThread();
yieldThread.start();
for (int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"这是第"+i+"次执行");
}
}
}
加入:
public final void join()
允许其他线程加入到当前线程中
package com.gsh.demon_03;
import javax.swing.*;
/**
* @Auther: haohao
* @Date:2022/7/1800:07
*/
public class JoinThread extends Thread{
@Override
public void run() {
for (int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"这是第"+i+"次执行");
}
}
}
class JoinThread02 extends Thread{
@Override
public void run() {
for (int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"这是第"+i+"次执行");
}
}
}
class ThreadTest{
public static void main(String[] args) throws InterruptedException {
JoinThread joinThread=new JoinThread();
joinThread.start();
JoinThread02 joinThread02=new JoinThread02();
joinThread02.start();
joinThread.join();
for (int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"这是第"+i+"次执行");
}
}
}
优先级:
线程对象.setPriority()
线程优先级1-10,默认为5,优先级越高,表示获取CPU的概率越搞
守护线程:
线程对象.setDaemon(true);设置为守护线程.
线程有两类:用户线程(前台线程)和守护线程(后台线程)
如果程序中所有前台线程都执行完毕了,后台线程也会自动结束.
垃圾回收属于守护线程。
package com.gsh.demon_04;
/**
* @Auther: haohao
* @Date:2022/7/1800:32
*/
public class SetDemon implements Runnable{
@Override
public void run() {
for (int i=0;i<50;i++){
System.out.println(Thread.currentThread().getName()+"执行第"+i+"次************");
}
}
}
class ThreadMethod{
public static void main(String[] args) {
SetDemon setDemon=new SetDemon();
Thread thread=new Thread(setDemon,"守护线程!");
thread.setDaemon(true);//主函数执行完后守护执行也停止;
thread.start();
for (int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"这是第"+i+"次执行");
}
}
}
十一、线程的安全
线程安全问题:

解决方法:

线程的同步方法:

线程的同步规则:

十二、死锁
死锁的概念:
当A线程拥有锁资源a时,这时A线程需要锁资源b, 而B线程拥有锁资源b,这时B线程需要锁资源a, 这样会导致A等待B线程释放资源b, B线程等待A线程释放锁资源a。 从而二个处于永久等待。从而操作死锁。

操作死锁的概念:
锁与锁之间有嵌套导致。
死锁的解决方案:
1. 尽量减少锁得嵌套。
2. 可以使用一些安全类。
3. 可以使用Lock中得枷锁,设置枷锁时间。
例子:
package com.gsh.demon_01;
/**
* @Auther: haohao
* @Date:2022/7/1814:38
*/
//死锁
//造成这个死锁的原因:
/**
* 当线程1拥有锁man时,线程1也需要锁womanm
* 这时线程2拥有锁woman而线程2需要锁man
* 这会导致线程1等待线程2释放锁woman而线程2需要线程1释放锁man从而产生死锁
*/
class BoyLock extends Thread{
@Override
public void run() {
synchronized (LockObject.man){
System.out.println(Thread.currentThread().getName()+"得到了她的心但没有得到她的爱!");
synchronized (LockObject.woman){
System.out.println(Thread.currentThread().getName()+"得到了心也得到了爱");
System.out.println("直接原地结婚吧!");
}
}
}
}
class GirlLock extends Thread{
@Override
public void run() {
synchronized (LockObject.woman){
System.out.println(Thread.currentThread().getName()+"得到了她的爱但没有得到她的心!");
synchronized (LockObject.man){
System.out.println(Thread.currentThread().getName()+"得到了心也得到了爱");
System.out.println("直接原地结婚吧!");
}
}
}
}
class LockObject{
public static Object man=new Object();
public static Object woman=new Object();
}
class Test01{
public static void main(String[] args) {
BoyLock deadLock=new BoyLock();
GirlLock deadLock1=new GirlLock();
deadLock.start();
deadLock1.start();
}
}
十三、线程通信
使用线程通信的意义:
我们无法决定哪个线程先获取CPU,这样也就无法决定哪个线程先执行
我们可能要指定那些线程先执行哪些线程后执行,这样我们们就需要使用线程通信技术.
线程通信中使用的方法

例子:
BankCard类:
package com.gsh.demon_02;
/**
* @Auther: haohao
* @Date:2022/7/1815:15
*/
public class BankCard {
//余额
private double balance;
//银行卡状态
private boolean flag=false;//true:有钱,false:没钱
public void setBalance(double balance) {
this.balance = balance;
}
public double getBalance(){
return balance;
}
//存钱
public synchronized void save(double money){
if(flag==true){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//卡里没钱存钱
this.balance=this.balance+money;
System.out.println(Thread.currentThread().getName()+"往卡中存"+money+"卡中余额"+this.balance);
//改变余额状态
flag=true;
//唤醒等待队列
this.notify();
}
//取钱
public synchronized void take(double money){
//判断卡里是否有钱
if(flag==false){
//等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//卡里有钱花钱
this.balance=this.balance-money;
System.out.println(Thread.currentThread().getName()+"在卡中取"+money+"卡中余额"+this.balance);
//改变余额状态
flag=false;
//唤醒
this.notify();
}
}
BoySave类:
package com.gsh.demon_02;
/**
* @Auther: haohao
* @Date:2022/7/1815:48
*/
public class BoySave implements Runnable{
private BankCard bankCard;
public BoySave(BankCard bankCard) {
this.bankCard=bankCard;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
bankCard.save(1000);
}
}
}
GirlTake类:
package com.gsh.demon_02;
/**
* @Auther: haohao
* @Date:2022/7/1815:48
*/
public class GirlTake implements Runnable{
private BankCard bankCard;
public GirlTake(BankCard bankCard) {
this.bankCard=bankCard;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
bankCard.take(1000);
}
}
}
测试类(CardTest):
package com.gsh.demon_02;
/**
* @Auther: haohao
* @Date:2022/7/1815:50
*/
public class CradTest {
public static void main(String[] args) {
BankCard bankCard=new BankCard();
BoySave boySave=new BoySave(bankCard);
GirlTake girlTake=new GirlTake(bankCard);
Thread m1=new Thread(boySave,"存钱");
Thread m2=new Thread(girlTake,"取钱");
m1.start();
m2.start();
}
}
十四、sleep与wait的区别
1.所在得类不同。sleep属于Thread类,wait属于Object类。
2.使用的地方: sleep可以使用再任何代码块。wait只能再同步代码块中。
3.是否释放锁资源: sleep不释放锁资源,wait会释放锁资源。
4.sleep时间到了自动唤醒,wait必须需要使用notify和notifyAll唤醒
十五、notify和notyfyAll区别?

本文详细介绍了Java中的进程与线程概念,包括进程作为资源分配单位,线程作为CPU调度单位。讲解了线程的创建方式,如继承Thread类、实现Runnable接口和Callable接口,并通过实例演示了线程的运行。同时,阐述了线程状态、常用方法如sleep、yield、join以及线程的优先级和守护线程设置。讨论了线程安全问题,死锁的概念及避免策略,以及线程间的通信方法。最后,举例说明了线程同步中的wait和sleep方法的区别。


被折叠的 条评论
为什么被折叠?



