「java线程池加锁」线程加锁的方法有哪些

博主:adminadmin 2022-12-06 19:21:08 69

今天给各位分享java线程池加锁的知识,其中也会对线程加锁的方法有哪些进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!

本文目录一览:

北大青鸟java培训:关于线程安全问题分析?

在学习java编程开发语言的过程中,我们掌握了线程与线程池等相关技术知识。

今天,北大青鸟重庆计算机学院就关于线程安全问题给大家做一个简单的说明和介绍,一起来了解一下吧。

线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。

不会出现数据不一致或者数据污染。

线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。

什么时候考虑到线程安全:一个对象是否需要线程安全,取决于该对象是否被多线程访问。

这指的是程序中访问对象的方式,而不是对象要实现的功能。

要使得对象是线程安全的,要采用同步机制来协同对对象可变状态的访问。

Java常用的同步机制是Synchronized,还包括volatile类型的变量,显示锁以及原子变量。

在多个线程中,当它们同时访问同个类时,每次执行的结果和单线程结果一致,且变量值跟预期一致,这个类则是线程安全的。

锁的特性锁机制的两种特性:互斥性:即同一时间只允许一个线程持有某个对象的锁,通过这种特性来实现多线程中的协调机制,这样在同一时间只有一个线程对需同步的代码块(复合操作)进行访问。

互斥性我们也往往称为操作的原子性。

可见性:必须确保在锁被释放之前,对共享变量所做的修改,对于随后获得该锁的另一个线程是可见的,否则另一个线程可能是在本地缓存的某个副本上继续操作从而引起不一致。

挂起、休眠、阻塞和非阻塞挂起:当线程被挂起时,其会失去CPU的使用时间,直到被其他线程(用户线程或调试线程)唤醒。

休眠:同样是会失去CPU的使用时间,但是在过了指定的休眠时间之后,它会自动激活,无需唤醒(整个唤醒表面看是自动的,但实际上也得有守护线程去唤醒,只是不需编程者手动干预)。

阻塞:在线程执行时,所需要的资源不能得到,则线程被挂起,直到满足可操作的条件。

非阻塞:在线程执行时,所需要的资源不能得到,则线程不是被挂起等待,而是继续执行其余事情,等待条件满足了后,收到了通知(同样是守护线程去做)再执行。

java多线程,对象锁是什么概念?

java线程:

1.线程中一些基本术语和概念

1.1线程的几个状态

初始化状态

就绪状态

运行状态

阻塞状态

终止状态

1.2 Daemon线程

Daemon线程区别一般线程之处是:主程序一旦结束,Daemon线程就会结束。

1.3锁的定义

为了协调多个并发运行的线程使用共享资源才引入了锁的概念。

1.4死锁

任何多线程应用程序都有死锁风险。当一组线程中的每一个都在等待一个只

有该组中另一个线程才能引起的事件时,我们就说这组线程死锁了。换一个说法

就是一组线程中的每一个成员都在等待别的成员占有的资源时候,就可以说这组

线程进入了死锁。死锁的最简单情形是:线程 A 持有对象 X 的独占锁,并且

在等待对象 Y 的锁,而线程 B 持有对象 Y 的独占锁,却在等待对象 X 的锁。

除非有某种方法来打破对锁的等待(Java 锁定不支持这种方法),否则死锁的线

程将永远等下去。

1.5.Java对象关于锁的几个方法

1.5.1 wait方法

wait方法是java根对象Object含有的方法,表示等待获取某个锁。在wait方法进入前,会释放相应的锁,在wait方法返回时,会再次获得某个锁。

如果wait()方法不带有参数,那只有当持有该对象锁的其他线程调用了notify或者notifyAll方法,才有可能再次获得该对象的锁。

如果wait()方法带有参数,比如:wait(10),那当持有该对象锁的其他线程调用了notify或者notifyAll方法,或者指定时间已经过去了,才有可能再次获得该对象的锁。

参考 thread.lock.SleepAndWait

1.5.2 notify/notifyAll方法

这里我就不再说明了。哈哈,偷点懒。

1.5.3 yield方法

yield()会自动放弃CPU,有时比sleep更能提升性能。

1.6锁对象(实例方法的锁)

在同步代码块中使用锁的时候,担当锁的对象可以是这个代码所在对象本身或者一个单独的对象担任,但是一定要确保锁对象不能为空。如果对一个null对象加锁,会产生异常的。原则上不要选择一个可能在锁的作用域中会改变值的实例变量作为锁对象。

锁对象,一种是对象自己担任,一种是定义一个普通的对象作为private property来担任,另外一种是建立一个新的类,然后用该类的实例来担任。

参考 :

thread.lock.UseSelfAsLock,使用对象自己做锁对象

thread.lock.UseObjAsLock 使用一个实例对象作锁对象

thread.lock.UseAFinalObjAsLock使用常量对象作为一个锁对象

1.7类锁

实例方法存在同步的问题,同样,类方法也存在需要同步的情形。一般类方法的类锁是一个static object来担任的。当然也可以采用类本身的类对象来作为类锁。

一个类的实例方法可以获得该类实例锁,还可以尝试去访问类方法,包含类同步方法,去获得类锁。

一个类的类方法,可以尝试获得类锁,但是不可以尝试直接获得实例锁。需要先生成一个实例,然后在申请获得这个实例的实例锁。

参考

thread.lock.UseStaticObjAsStaticLock 使用类的属性对象作为类锁。

thread.lock.UseClassAsStaticLock使用类的类对象作为类锁

1.8.线程安全方法与线程不安全方法

如果一个对象的所有的public方法都是同步方法,也就是说是public方法是线程安全的,那该对象的private方法,在不考虑继承的情况下,可以设置为不是线程安全的方法。

参考 thread.lock.SynMethrodAndNotSynMethrod

1.9类锁和实例锁混合使用

在实例方法中混合使用类锁和实例锁;可以根据前面说的那样使用实例锁和类锁。

在类方法中混合使用类锁和实例锁,可以根据前面说的那样使用类锁,为了使用实例锁,先得生成一个实例,然后实例锁。

参考 thread.lock.StaticLockAndObjLock

1.10锁的粒度问题。

为了解决对象锁的粒度过粗,会导死锁出现的可能性加大,锁的粒度过细,会程序开发维护的工作加大。对于锁的粒度大小,这完全要根据实际开发需要来考虑,很难有一个统一的标准。

1.11.读写锁

一个读写锁支持多个线程同时访问一个对象,但是在同一时刻只有一个线程可以修改此对象,并且在访问进行时不能修改。

有2种调度策略,一种是读锁优先,另外就是写锁优先。

参考 thread.lock.ReadWriteLock

1.12 volatile

在Java中设置变量值的操作,除了long和double类型的变量外都是原子操作,也就是说,对于变量值的简单读写操作没有必要进行同步。这在JVM 1.2之前,Java的内存模型实现总是从主存读取变量,是不需要进行特别的注意的。而随着JVM的成熟和优化,现在在多线程环境下volatile关键字的使用变得非常重要。在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数据的不一致。要解决这个问题,只需要像在本程序中的这样,把该变量声明为volatile(不稳定的)即可,这就指示JVM,这个变量是不稳定的,每次使用它都到主存中进行读取。一般说来,多任务环境下各任务间共享的标志都应该加volatile修饰。

2.线程之间的通讯

在其他语言中,线程之间可以通过消息队列,共享内存,管道等方式来实现

线程之间的通讯,但是java中可以不采用这样方式,关注的是线程之间的同步。

只要保证相关方法运行的线程安全,信息共享是自然就可以显现了。

2.1屏障

屏障就是这样的一个等待点: 一组线程在这一点被同步,这些线程合并各自的结果或者运行到整体任务的下一阶段。

参考:

thread.lock. BarrierUseExample

thread.lock.Barrier

2.2.锁工具类

提供对线程锁的获取,释放功能。展示了锁的获取释放过程。可以作为一个工具类来使用。

参考:thread.lock. BusyFlag

2.3.条件变量

条件变量是POSIX线程模型提供的一种同步类型,和java中的等待通知机制类似。

虽然java中已经有了等待通知机制,但是为了减少在notify/notifyAll方法中

线程调度的开销,把一些不需要激活的线程屏蔽出去,引入了条件变量。

Java中2个(多个)条件变量可以是同一个互斥体(锁对象)。

参考:thread.lock.CondVar 条件变量类

常见的应用情形:

一个锁控制多个信号通道(例如:多个变量),虽然可以采用简单java等待通知机制,但是线程调度效率不高,而且线程可读性也不是太好,这时候可以采用创建一个锁对象(BusyFlag实例),同时使用这个BusyFlag实例来创建多个条件变量(CondVar 实例)。

经常使用到CondVar类的地方是缓冲区管理,比如:管道操作之类的。先创建一个BusyFlag实例,然后创建CondVar 实例,用这个条件变量描述缓冲区是否为空,另外创建CondVar 实例作条件变量述缓冲区是否满。

现实中,马路的红绿灯,就可以采用条件变量来描述。

3. Java线程调度

3.1 Java优先级

java的优先级别共有10种,加上虚拟机自己使用的优先级别=0这种,总共11种。

大多数情况来说,java线程的优先级设置越高(最高=10),那线程越优先运行。

3.2. 绿色线程

线程运行在虚拟机内,操作系统根本不知道这类线程的存在。

线程是由虚拟机调度的。

3.3 本地线程

线程是由运行虚拟机的操作系统完成的。

3.4 Windows本地线程

操作系统,完全能够看得到虚拟机内的每一个线程,同时虚拟机的线程和操作系统的线程是一一对应的。Java的线程调度室由操作系统底层线程决定的。

在win32平台下,windows线程只有6个优先级别。和java线程优先级别对应如下:

Java线程优先级 Windows 95/nt/2000线程优先级

0 THREAD_ PRIORITY_IDLE

1(Thread.MIN_PRIORITY) THREAD_ PRIORITY_LOWEST

2 THREAD_ PRIORITY_LOWEST

3 THREAD_ PRIORITY_BELOW_NORMAL

4 THREAD_ PRIORITY_BELOW_NORMAL

5 (Thread.NORM_PRIORITY) THREAD_ PRIORITY _NORMAL

6 THREAD_ PRIORITY _ABOVE_NORMAL

7 THREAD_ PRIORITY _ABOVE_NORMA

8 THREAD_ PRIORITY _HIGHEST

9 THREAD_ PRIORITY _HIGHEST

10 (Thread.MAX_PRIORITY) THREAD_ PRIORITY _CRITICAL

3.5线程优先级倒置与继承

如果一个线程持有锁(假设该线程名字=ThreadA,优先级别=5),另外一个线程(假设该线程名字=ThreadB,优先级别=7),现在该线程(ThreadA)处于运行状态,但是线程ThreadB申请需要持有ThreadA所获得的锁,这时候,为了避免死锁,线程A提高其运行的优先级别(提高到ThreadB的优先级别=7),而线程ThreadB为了等待获得锁,降低线程优先级别(降低到ThreadA原来的优先级别=5).

上述的这种情况,对于ThreadA,继承了ThreadB的优先级别,这成为优先级别的继承;对于ThreadB暂时降低了优先级别,成为优先级别的倒置。

当然,一旦线程ThreadA持有的锁释放了,其优先级别也会回到原来的优先级别(优先级别=5)。线程ThreadB获得了相应的锁,那优先级别也会恢复到与原来的值(优先级别=7)。

3.6循环调度

具有同样优先级的线程相互抢占成为循环调度。

4.线程池

创建一个线程也是需要一定代价的,为了降低这个代价,采用了和普通对象池的思想建立线程池,以供系统使用。

线程消耗包括内存和其它系统资源在内的大量资源。除了 Thread 对象所需的内存之外,每个线程都需要两个可能很大的执行调用堆栈。除此以外,JVM 可能会为每个 Java 线程创建一个本机线程,这些本机线程将消耗额外的系统资源。最后,虽然线程之间切换的调度开销很小,但如果有很多线程,环境切换也可能严重地影响程序的性能。

使用线程池的方式是,先建立对象池,然后申请使用线程,程序线程运行,运行完毕,把线程返回线程池。

使用线程池的风险:同步错误和死锁,与池有关的死锁、资源不足和线程泄漏。

大家有空可以研究一下tomcat的线程池实现原理思想。

实际上是tomcat已经在从线程池的使用线程时候加上了事件处理机制。

个人认为,线程池之类的实现,一般不要自己实现,因为自己实现主要是稳定性等方面可能作的不够好。

可以参考 apache的jakarta-tomcat-5.5.6的相关代码,具体是:

jakarta-tomcat-connectors\util\java\org\apache\tomcat\util\threads的相关代码

5工作队列

使用工作队列的好处是不象直接使用线程池那样,当线城池中没有线程可以使用的时

候,使用者需要处于等待状态,不能进行其他任务的处理。

工作队列的工作原理是:

采用后台线程处理方式,客户端把任务提交给工作队列,工作队列有一组内部可以工作线程,这些工作线程从工作队列中取出任务运行,一个任务完成后,就从队列获取下一个任务进行处理。当工作队列中没有任务可以处理时候,工作线程就处于等待状态,直到获得新的任务时候,才进行新的处理。

java 线程池机制的原理是什么?

线程池属于对象池.所有对象池都具有一个非常重要的共性,就是为了最大程度复用对象.那么线程池的最

重要的特征也就是最大程度利用线程.

首先,创建线程本身需要额外(相对于执行任务而必须的资源)的开销.

作业系统在每创建一个线程时,至少需要创建以下资源:

(1) 线程内核对象:用于对线程上下文的管理.

(2) 用户模式执行栈.

(3) 内核模式执行栈.

这些资源被线程占有后作业系统和用户都无法使用.

相反的过程,销毁线程需要回收资源,也需要一定开销.

其次,过多的线程将导致过度的切换.线程切换带来的性能更是不可估量.系统完成线程切换要经过以下过程:

(1) 从用户模式切换到内核模式.

(2) 将CPU寄存器的值保存到当前线程的内核对象中.

(3)打开一个自旋锁,根据调度策略决定下一个要执行的线程.释放自旋锁,如果要执行的线程不是同一进

程中的线程,还需要切换虚拟内存等进程环境.

(4) 将要执行的线程的内核对象的值写到CPU寄存器中.

(5) 切换到用户模式执行新线程的执行逻辑.

所以线程池的目的就是为了减少创建和切换线程的额外开销,利用已经的线程多次循环执行多个任务从而提

高系统的处理能力.

java线程池中线程需要加锁吗

如果只是读操作,没有写操作,则可以不用加锁,此种情形下,变量加上final关键字;

如果有写操作,但是变量的写操作跟当前的值无关联,且与其他的变量也无关联,则可考虑变量加上volatile关键字,同时写操作方法通过synchronized加锁;

如果有写操作,且写操作依赖变量的当前值(如:i++),则getXXX和写操作方法都要通过synchronized加锁。

java线程池怎么加synchronized

你这个问题丢了好几天了,一直想回答也不知怎么回答才好!

你最好先搞清楚synchronized是干嘛用的,他只是起到一个同步的作用,一个标识!

他可以用在函数上面,那么这个函数内部所有代码也同步,他也可以单用封装成代码块

那么代码块里面的语句,也都将会被同步执行,你可以理解他有一个锁,这个是通俗的叫法!

线程池里面是被挂起的线程,都先暂时存放在线程池里面!

需要被唤醒才可以继续运行!

你最好多看看多线程这一章节!

java 线程池原理怎样避免线程死锁

Java线程死锁需要如何解决,这个问题一直在我们不断的使用中需要只有不断的关键。不幸的是,使用上锁会带来其他问题。让我们来看一些常见问题以及相应的解决方法: Java线程死锁 Java线程死锁是一个经典的多线程问题,因为不同的线程都在等待那些根本不可能被释放的锁,从而导致所有的工作都无法完成。假设有两个线程,分别代表两个饥饿的人,他们必须共享刀叉并轮流吃饭。他们都需要获得两个锁:共享刀和共享叉的锁。 假如线程 “A”获得了刀,而线程“B”获得了叉。线程“A”就会进入阻塞状态来等待获得叉,而线程“B”则阻塞来等待“A”所拥有的刀。这只是人为设计的例子,但尽管在运行时很难探测到,这类情况却时常发生。虽然要探测或推敲各种情况是非常困难的,但只要按照下面几条规则去设计系统,就能够避免Java线程死锁问题: 让所有的线程按照同样的顺序获得一组锁。这种方法消除了 X 和 Y 的拥有者分别等待对方的资源的问题。 将多个锁组成一组并放到同一个锁下。前面Java线程死锁的例子中,可以创建一个银器对象的锁。于是在获得刀或叉之前都必须获得这个银器的锁。 将那些不会阻塞的可获得资源用变量标志出来。当某个线程获得银器对象的锁时,就可以通过检查变量来判断是否整个银器集合中的对象锁都可获得。如果是,它就可以获得相关的锁,否则,就要释放掉银器这个锁并稍后再尝试。 最重要的是,在编写代码前认真仔细地设计整个系统。多线程是困难的,在开始编程之前详细设计系统能够帮助你避免难以发现Java线程死锁的问题。 Volatile 变量,volatile 关键字是 Java 语言为优化编译器设计的。以下面的代码为例: 一.class VolatileTest { 二.public void foo() { 三.boolean flag = false; 四.if(flag) { 5.//this could happen 陆.} 漆.} 吧.} 一个优化的编译器可能会判断出if部分的语句永远不会被执行,就根本不会编译这部分的代码。如果这个类被多线程访问, flag被前面某个线程设置之后,在它被if语句测试之前,可以被其他线程重新设置。用volatile关键字来声明变量,就可以告诉编译器在编译的时候,不需要通过预测变量值来优化这部分的代码。 无法访问的Java线程死锁有时候虽然获取对象锁没有问题,线程依然有可能进入阻塞状态。在 Java 编程中IO就是这类问题最好的例子。当线程因为对象内的IO调用而阻塞时,此对象应当仍能被其他线程访问。该对象通常有责任取消这个阻塞的IO操作。造成阻塞调用的线程常常会令同步任务失败。如果该对象的其他方法也是同步的,当线程被阻塞时,此对象也就相当于被冷冻住了。 其他的线程由于不能获得对象的Java线程死锁,就不能给此对象发消息(例如,取消 IO 操作)。必须确保不在同步代码中包含那些阻塞调用,或确认在一个用同步阻塞代码的对象中存在非同步方法。尽管这种方法需要花费一些注意力来保证结果代码安全运行,但它允许在拥有对象的线程发生阻塞后,该对象仍能够响应其他线程。 编辑推荐: 一. Java多线程优化之偏向锁原理分析 二. Java多线程实现异步调用的方法 三. 使用Java多线程机制实现下载的方法介

关于java线程池加锁和线程加锁的方法有哪些的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。

The End

发布于:2022-12-06,除非注明,否则均为首码项目网原创文章,转载请注明出处。