线程基础知识、线程之间的共享和协作

3 篇文章 0 订阅
3 篇文章 0 订阅

1,什么是进程和线程?

进程是程序运行资源分配的最小单位
线程是cpu调度的最小单位,必须依赖于进程而存在,线程无处不在

2,并行与并发

我们举个例子,如果有条高速公路 A 上面并排有 8 条车道,那么最大的 并行车
辆就是 8 辆此条高速公路 A 同时并排行走的车辆小于等于 8 辆的时候,车辆就可
以并行运行。CPU 也是这个原理,一个 CPU 相当于一个高速公路 A,核心数或者线
程数就相当于并排可以通行的车道;而多个 CPU就相当于并排有多条高速公路,而
每个高速公路并排有多个车道。

当谈论 并发的时候一定要加个单位时间,也就是说单位时间内并发量是多少?
离开了单位时间其实是没有意义的。

俗话说,一心不能二用,这对计算机也一样,原则上一个 CPU 只能分配给一个
进程,以便运行这个进程。我们通常使用的计算机中只有一个 CPU,也就是说只有
一颗心,要让它一心多用同时运行多个进程,就必须使用并发技术。实现并发技术
相当复杂,最容易理解的是“时间片轮转进程调度算法”。

综合来说:
并发:指应用能够交替执行不同的任务,比如单 CPU核心下执行多线程并非是
同时执行多个任务,如果你开两个线程执行,就是在你几乎不可能察觉到的速度不
断去切换这两个任务,已达到"同时执行效果",其实并不是的,只是计算机的速度太
快,我们无法察觉到而已.
并行:指应用能够同时执行不同的任务,例:吃饭的时候可以边吃饭边打电话,
这两件事情可以同时执行
两者区别:一个是交替执行,一个是同时执行.

在这里插入图片描述

3,高并发编程的意义和注意事项

意义:

(1)充分利用CPU的资源
(2)加快响应用户的世间
(3)可以使你的代码模块化、异步化。简单化

注意事项:

(1)线程之间的安全性
(2)线程之间的死锁
(3)线程太多了会将服务器资源耗尽形成死机当机

4,Thread和Runnable的区别

Thread 才是 Java 里对线程的唯一抽象,Runnable 只是对任务(业务逻辑)
的抽象。Thread 可以接受任意一个 Runnable 的实例并执行。

用法:

    public static class TestThread extends Thread {
        @Override
        public void run() {
            super.run();
        }
    }
    
    
    TestThread testThread = new TestThread();
    testThread.start();


    public static class TestRunnable implements Runnable {

        @Override
        public void run() {

        }
    }

    TestRunnable testRunnable = new TestRunnable();
    new Thread(testRunnable).start();

5,中止

线程自然终止
要么是 run 执行完成了,要么是抛出了一个未处理的异常导致线程提前结束。
stop
暂停、恢复和停止操作对应在线程 Thread 的 API 就是 suspend() 、resume()
和 stop()。
但是这些 API 是过期的,也就是不建议使用的。不建议使用的原因主
要有:以 suspend()方法为例,在调用后,线程不会释放已经占有的资源(比如
锁),而是占有着资源进入睡眠状态,这样容易引发死锁问题。同样,stop()方
法在终结一个线程时不会保证线程的资源正常释放,通常是没有给予线程完成资
源释放工作的机会,因此会导致程序可能工作在不确定状态下。正因为 suspend()、
resume()和 stop()方法带来的副作用,这些方法才被标注为不建议使用的过期方
法。

中断
安全的中止则是其他线程通过调用某个线程 A 的 interrupt() 方法对其进行中
断操作, 中断好比其他线程对该线程打了个招呼,“A,你要中断了”,不代表
线程 A 会立即停止自己的工作,同样的 A 线程完全可以不理会这种中断请求。
因为 java 里的线程是协作式的,不是抢占式的。线程通过检查自身的中断标志
位是否被置为 true 来进行响应,
线程通过方法 isInterrupted() 来进行判断是否被中断,也可以调用静态方法
Thread.interrupted() 来进行判断当前线程是否被中断,不过 Thread.interrupted()
会同时将中断标识位改写为 false。
如果一个线程处于了阻塞状态(如线程调用了 thread.sleep、thread.join、
thread.wait 等),则在线程在检查中断标示时如果发现中断标示为 true,则会在
这些阻塞方法调用处抛出 InterruptedException 异常,并且在抛出异常后会立即
将线程的中断标示位清除,即重新设置为 false。
不建议自定义一个取消标志位来中止线程的运行。 因为 run 方法里有阻塞调
用时会无法很快检测到取消标志,线程必须从阻塞调用返回后,才会检查这个取
消标志。这种情况下,使用中断会更好,因为,
一、一般的阻塞方法,如 sleep 等本身就支持中断的检查,
二、检查中断位的状态和检查取消标志位没什么区别,用中断位的状态还可
以避免声明取消标志位,减少资源的消耗。
注意 :处于 死锁状态 的线程无法被中断

6,对Java里的线程在多一点点认识

深入理解 run()和 start()
Thread类是Java里对线程概念的抽象,可以这样理解:我们通过new Thread()
其实只是 new 出一个 Thread 的实例,还没有操作系统中真正的线程挂起钩来。
只有执行了 start()方法后,才实现了真正意义上的启动线程。
start()方法让一个线程进入就绪队列等待分配 cpu,分到 cpu 后才调用实现
的 run()方法,start()方法不能重复调用,如果重复调用会抛出异常。
而 run 方法是业务逻辑实现的地方,本质上和任意一个类的任意一个成员方
法并没有任何区别,可以重复执行,也可以被单独调用。

其他的线程相关方法
yield()方法:使当前线程让出 CPU 占有权,但让出的时间是不可设定的。也
不会释放锁资源。注意:并不是每个线程都需要这个锁的,而且执行 yield( )的线
程不一定就会持有锁,我们完全可以在释放锁后再调用 yield 方法。
所有执行 yield()的线程有可能在进入到就绪状态后会被操作系统再次选中
马上又被执行。
wait()/notify()/notifyAll():后面会单独讲述

join 方法
把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行。
比如在线程 B 中调用了线程 A 的 Join()方法,直到线程 A 执行完毕后,才会继续
执行线程 B。(此处为常见面试考点)

线程的优先级
在 Java 线程中,通过一个整型成员变量 priority 来控制优先级,优先级的范
围从 1~10,在线程构建的时候可以通过 setPriority(int)方法来修改优先级,默认
优先级是 5,优先级高的线程分配时间片的数量要多于优先级低的线程。
设置线程优先级时,针对频繁阻塞(休眠或者 I/O 操作)的线程需要设置较
高优先级,而偏重计算(需要较多 CPU 时间或者偏运算)的线程则设置较低的
优先级,确保处理器不会被独占。在不同的 JVM 以及操作系统上,线程规划会
存在差异,有些操作系统甚至会忽略对线程优先级的设定。

守护线程
Daemon(守护)线程是一种支持型线程,因为它主要被用作程序中后台调
度以及支持性工作。这意味着,当一个 Java 虚拟机中不存在非 非 Daemon 线程的
时候,Java 虚拟机将会退出。可以通过调用 Thread.setDaemon(true)将线程设置
为 Daemon 线程。我们一般用不上,比如垃圾回收线程就是 Daemon 线程。
Daemon 线程被用作完成支持性工作,但是在 Java 虚拟机退出时 Daemon 线
程中的 finally 块并不一定会执行。在构建 Daemon 线程时,不能依靠 finally 块中
的内容来确保执行关闭或清理资源的逻辑。

7,ThreadLocal与Sybchonized的比较

ThreadLocal 和 Synchonized 都用于解决多线程并发訪问。可是 ThreadLocal
与 synchronized 有本质的差别。synchronized 是利用锁的机制,使变量或代码块
在某一时该仅仅能被一个线程訪问。而 ThreadLocal 为每个线程都提供了变量的
副本,使得每个线程在某一时间訪问到的并非同一个对象,这样就隔离了多个线
程对数据的数据共享。

8,ThreadLocal的使用

ThreadLocal 类接口很简单,只有 4 个方法,我们先来了解一下:

• void set(Object value)
设置当前线程的线程局部变量的值。

• public Object get()
该方法返回当前线程所对应的线程局部变量。

• public void remove()
将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是 JDK
5.0 新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动
被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它
可以加快内存回收的速度。

• protected Object initialValue()
返回该线程局部变量的初始值,该方法是一个 protected 的方法,显然是为
了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第 1 次调用 get()
或 set(Object)时才执行,并且仅执行 1 次。ThreadLocal 中的缺省实现直接返回一
个 null。
public final static ThreadLocal RESOURCE = new
ThreadLocal();RESOURCE代表一个能够存放String类型的ThreadLocal对象。
此时不论什么一个线程能够并发访问这个变量,对它进行写入、读取操作,都是
线程安全的。

9,ThreadLocal实现解析

在这里插入图片描述

10,等待和通知的标准范式

等待方遵循如下原则。
1)获取对象的锁。
2)如果条件不满足,那么调用对象的 wait()方法,被通知后仍要检查条件。
3)条件满足则执行对应的逻辑。

synchronized (对象) {
while (条件不满足) {
对象.wait();
}
对应的处理逻辑
}

通知方遵循如下原则。
1)获得对象的锁。
2)改变条件。
3)通知所有等待在对象上的线程。

synchronized (对象) {
改变条件
对象.notifyAll();
}

11,调用 yield() 、sleep()、wait()、notify()等方法对锁有何影响?

yield()、sleep()被调用后,都不会释放当前线程所持有的锁。

调用wait()方法后,会释放当前线程所持有的锁,而且当前被唤醒后,会重新去竞争锁,锁竞争到后才会执行wait方法后面的代码。

调用notify()系列方法后,对锁无影响,线程只有在syn同步代码执行完成后才会自然而然的释放锁,所以notify()系列方法一般都是syn同步代码的最后一行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值