多线程知识总结

一、线程与进程
1、程序是对数据描述与操作的代码的集合;如:Office中的Word、暴风影音等。
2、进程是程序的一次动态执行过程,它对应了从代码加载、执行到执行完毕的完整过程,这个过程也是进程本身从产生、发展到死亡的过程。
进程的特点:
a、进程是系统运营的基本单位;
b、每个进程都有自己独立的一块内存空间和自足系统资源;
c、每个进程的内部数据和状态都是完全独立的
3、线程:
线程是进程执行运算的最小单位,是进程内部的一个执行单位,每个进程在其执行过程中至少有一个线程。如果一个进程中同时产生了多个线程,则称之为多线程。
线程按处理级别分为核心线程和用户级线程。
(1)核心线程是和系统任务任务相关的线程,处理不同进程之间的多个线程。
(2)开发程序时.由于程序的需要而编写的线程即为用户级线程。
4、线程与进程的区别
(1)一个进程中至少有一个线程
(2)资源分配给进程,同一进程下的所有线程共享该资源
(3)处理机分配给线程,即真正在处理机上运行的是线程
5、随机性的原理:因为cpu的快速切换造成,哪个线程获取到了cpu的执行权,哪个线程就执行
线程要运行的代码都统一存放在了 run() 方法中。
二、创建线程
1、创建线程的两种方式:
(1)继承java.lang.Thread类(线程类),重写子类的run()方法
1,定义类继承Thread类;
2,目的是复写run方法,将要让线程运行的代码都存储到run方法中;
3,通过创建Thread类的子类对象,创建线程对象;
4,调用线程的start方法,开启线程,并执行run方法。
(2)实现java.lang.Runnable接口
步骤:
1,定义类实现Runnable接口。
2,覆盖接口中的run方法(用于封装线程要运行的代码)。
3,通过Thread类创建线程对象;
4,将实现了Runnable接口的子类对象作为实际参数传递给Thread类中的构 造函数。
为什么要传递呢?因为要让线程对象明确要运行的run方法所属的对象。
5,调用Thread对象的start方法。开启线程,并运行Runnable接口子类中的run方法。
两者的区别:
第一种方式编写简单,可以直接操作线程,适用于单重继承的情况
当一个线程继承了另一个类是,就只能用实现Runnable接口的方法,这样还可以使多个线程之间是有同一个Runnable对象。
三、线程的状态(5种状态)
在这里插入图片描述
a、新生状态(New Thread) //被创建
b、可运行状态(就绪状态Runnable) //有cpu执行资格,没有执行权
c、运行状态(Runningtime) //不仅有执行资格还有执行权
d、阻塞状态(Blocked)
e、死亡状态(Dead)

public class ThreadDemo05 implements Runnable{
    @Override
    public void run(){
        System.out.println("线程1正在运行");
        try {
            Thread.sleep(1000);
            System.out.println("线程1休眠中,处于阻塞状态");
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("线程1被中断");
        }
    }
    public static void main(String[] args) {
        ThreadDemo05 td1 = new ThreadDemo05();
        Thread td = new Thread(td1,"线程1");
        System.out.println("线程1处于新建状态");
        td.start();
        System.out.println("线程1处于就绪状态状态");
    }
}

四、线程的调度
1、线程的优先级(priority) 方法(setPriority(int grade))
线程的优先级用1—10表示,10表示优先级最高,默认值是5.每个优先级都对应一个Thread类的公用静态常量。
NORM_PRIORITY = 5;
MIN_PRIORITY=1;
MAX_PRIORITY=10;

线程的优先级可以用过**setPriority(int grade)**方法更改(此方法参数必须是1-10的整数)
2、实现线程调度的方法
(1)join()方法:
是当前线程暂停执行,等待调用方法的线程结束后再执行本线程。有三种重载形式。
public final void join()
public final void join(long mills)
public final void join(long mills,int nanos)
(2)sleep()方法
语法格式:public static void sleep(long mills)
sleep()方法会让当前线程休眠(暂停执行)mills 毫秒,线程有运行中的状态进入不可运行状态,休眠时间过后会再次进入可运行状态。
(3)yield()方法
语法格式:public static void yeild()
yeild()方法可让当前线程暂停执行,允许其他线程执行,但该线程仍处于可运行状态,并不变为阻塞状态。系统选择其他相同或更高优先级线程执行,若无其他相同或者更高优先级线程,则该线程继续执行。
sleep()和yeild()方法的区别:
在这里插入图片描述
五、线程同步
好处:解决了线程安全问题。
弊端:相对降低性能,因为判断锁需要消耗资源,产生了死锁。

定义同步是有前提的:
1,必须要有两个或者两个以上的线程,才需要同步。
2,多个线程必须保证使用的是同一个锁。
当一些线程同时运行需要共享一些数据的时候,此时需要考虑其他线程的状态和行为,否则就不能保证程序运行的正确性。例如:窗口售票系统

**
 * 线程同步问题   模拟3个窗口售票100张
 */
public class ThreadDemo4 implements Runnable{
    int ticket = 100;
    public static void main(String[] args) {
            ThreadDemo4 td = new ThreadDemo4();
            Thread td1 = new Thread(td,"窗口1");
            Thread td2 = new Thread(td,"窗口2");
            Thread td3 = new Thread(td,"窗口3");
            td1.start();
            td2.start();
            td3.start();
    }

    @Override
    public void run() {
        while (true){
            //同步
            synchronized (this){
                if (ticket>0){
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"正在卖第**"+(ticket--)+"**张票");
                }else {
                    System.out.println(Thread.currentThread().getName()+"票已售完!");
                    break;
                }
            }
        }
    }
}

实现线程同步有两种方式:
1、同步函数
在方法声明中加入 synchrinized 关键字来声明同步方法。
语法格式:

访问修饰符 synchronized 返回类型 方法名 {}
	或者 synchronized 访问修饰符 返回类型 方法名 {}

同步函数:其实就是将同步关键字定义在函数上,让函数具备了同步性。
同步函数是用的哪个锁呢?
通过验证,函数都有自己所属的对象this,所以同步函数所使用的锁就是this锁。

当同步函数被static修饰时,这时的同步用的是哪个锁呢?
静态函数在加载时所属于类,这时有可能还没有该类产生的对象,但是该类的字节码文件加载进内存就已经被封装成了对象,这个对象就是该类的字节码文件对象。
所以静态加载时,只有一个对象存在,那么静态同步函数就使用的这个对象。
这个对象就是 类名.class
2、同步代码块
语法格式

 synchronized(对象) {  // 任意对象都可以。这个对象就是锁。
	需要被同步的代码;

同步代码块和同步函数的区别?
同步代码块使用的锁可以是任意对象。
同步函数使用的锁是this,静态同步函数的锁是该类的字节码文件对象。

在一个类中只有一个同步,可以使用同步函数。如果有多同步,必须使用同步代码块,来确定不同的锁。所以同步代码块相对灵活一些。

3、死锁
多线程使用同步机制时,存在“死锁”的潜在危险。如果多个线程都处于等待无法唤醒时,就构成了死锁(Deadlock),此时处于处于等待的多个线程占用系统资源,但无法运行,因此不会释放自身的资源。
避免死锁的方法:线程因某个条件未满足而受阻,不能让其继续占有资源;如果有多个对象需要互斥访问,应确定线程获得锁的顺序,并保证整个程序以相反的顺序释放锁。

六、线程通信
线程间通信:思路:多个线程在操作同一个资源,但是操作的动作却不一样。
1:将资源封装成对象。
2:将线程执行的任务(任务其实就是run方法。)也封装成对象。

等待唤醒机制:涉及的方法:
wait():将同步中的线程处于冻结状态。释放了执行权,释放了资格。同时将线程对象存储到线程池中。
notify():唤醒线程池中某一个等待线程。
notifyAll():唤醒的是线程池中的所有线程。

注意:
1:这些方法都需要定义在同步中。
2:因为这些方法必须要标示所属的锁。
你要知道 A锁上的线程被wait了,那这个线程就相当于处于A锁的线程池中,只能A锁的notify唤醒。
3:这三个方法都定义在Object类中。为什么操作线程的方法定义在Object类中?
因为这三个方法都需要定义同步内,并标示所属的同步锁,既然被锁调用,而锁又可以是任意对象,那么能被任意对象调用的方法一定定义在Object类中。

wait和sleep区别: 分析这两个方法:从执行权和锁上来分析:
wait():挂起线程,可以指定时间也可以不指定时间。不指定时间,只能由对应的notify()或者notifyAll()来唤醒。
sleep():线程休眠,必须指定时间,时间到自动从冻结状态转成运行状态(临时阻塞状态)。
wait():线程会释放执行权,而且线程会释放锁。
Sleep():线程会释放执行权,但不是不释放锁。

线程的停止:通过stop()方法就可以停止线程。但是这个方式过时了。
停止线程:原理就是:让线程运行的代码结束,也就是结束run方法。
怎么结束run方法?一般run方法里肯定定义循环。所以只要结束循环即可。
第一种方式:定义循环的结束标记。
第二种方式:如果线程处于了冻结状态,是不可能读到标记的,这时就需要通过Thread类中的interrupt()方法,将其冻结状态强制清除。让线程恢复具备执行资格的状态,让线程可以读到标记,并结束。

七、线程池:(看大佬详解)
深入理解线程池原理:https://blog.csdn.net/qq_36520235/article/details/81539770
理解线程池的原理:
https://blog.csdn.net/he90227/article/details/52576452
八、补充
---------< java.lang.Thread >----------
常用方法:
interrupt():中断线程。
setPriority(int newPriority):更改线程的优先级。
getPriority():返回线程的优先级。
toString():返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
Thread.yield():暂停当前正在执行的线程对象,并执行其他线程。
setDaemon(true):将该线程标记为守护线程或用户线程。将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。

Lock接口:多线程在JDK1.5版本升级时,推出一个接口Lock接口。
解决线程安全问题使用同步的形式,(同步代码块,要么同步函数)其实最终使用的都是锁机制。

后期版本直接将锁封装成了对象。线程进入同步就是具备了锁,执行完,离开同步,就是释放了锁。
在后期对锁的分析过程中,发现,获取锁,或者释放锁的动作应该是锁这个事物更清楚。所以将这些动作定义在了锁当中,并把锁定义成对象。

所以同步是隐示的锁操作,而Lock对象是显示的锁操作,它的出现就替代了同步。

在之前的版本中使用Object类中wait、notify、notifyAll的方式来完成的。那是因为同步中的锁是任意对象,所以操作锁的等待唤醒的方法都定义在Object类中。

而现在锁是指定对象Lock。所以查找等待唤醒机制方式需要通过Lock接口来完成。而Lock接口中并没有直接操作等待唤醒的方法,而是将这些方式又单独封装到了一个对象中。这个对象就是Condition,将Object中的三个方法进行单独的封装。并提供了功能一致的方法 **await()、signal()、signalAll()**体现新版本对象的好处。

< java.util.concurrent.locks > Condition接口:await()、signal()、signalAll();

class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition(); 
   final Condition notEmpty = lock.newCondition(); 
   final Object[] items = new Object[100];
   int putptr, takeptr, count;
   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length) 
         notFull.await();
       items[putptr] = x; 
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();
     } 
	finally {
       lock.unlock();
     }
   }
   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0) 
         notEmpty.await();
       Object x = items[takeptr]; 
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();
       return x;
     } 
finally {
       lock.unlock();
     }
   } 
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值