java线程同步的优缺点_浅谈java多线程编程

一、多线程的优缺点

多线程的优点:

1)资源利用率更好

2)程序设计在某些情况下更简单

3)程序响应更快

多线程的代价:

1)设计更复杂

虽然有一些多线程应用程序比单线程的应用程序要简单,但其他的一般都更复杂。在多线程访问共享数据的时候,这部分代码需要特别的注意。线程之间的交互往往非常复杂。不正确的线程同步产生的错误非常难以被发现,并且重现以修复。

2)上下文切换的开销

当CPU从执行一个线程切换到执行另外一个线程的时候,它需要先存储当前线程的本地的数据,程序指针等,然后载入另一个线程的本地数据,程序指针等,最后才开始执行。这种切换称为“上下文切换”(“context switch”)。CPU会在一个上下文中执行一个线程,然后切换到另外一个上下文中执行另外一个线程。上下文切换并不廉价。如果没有必要,应该减少上下文切换的发生。

二、创建java多线程

1、创建Thread的子类

创建Thread子类的一个实例并重写run方法,run方法会在调用start()方法之后被执行。例子如下:

public class MyThread extends Thread {

public void run(){

System.out.println("MyThread running");

}

}

MyThread myThread = new MyThread();

myTread.start();

也可以如下创建一个Thread的匿名子类:

Thread thread = new Thread(){

public void run(){

System.out.println("Thread Running");

}

};

thread.start();

2、实现Runnable接口

第二种编写线程执行代码的方式是新建一个实现了java.lang.Runnable接口的类的实例,实例中的方法可以被线程调用。下面给出例子:

public class MyRunnable implements Runnable {

public void run(){

System.out.println("MyRunnable running");

}

}

Thread thread = new Thread(new MyRunnable());

thread.start();

同样,也可以创建一个实现了Runnable接口的匿名类,如下所示:

Runnable myRunnable = new Runnable(){

public void run(){

System.out.println("Runnable running");

}

}

Thread thread = new Thread(myRunnable);

thread.start();

三、线程安全

在同一程序中运行多个线程本身不会导致问题,问题在于多个线程访问了相同的资源。如同一内存区(变量,数组,或对象)、系统(数据库,web services等)或文件。实际上,这些问题只有在一或多个线程向这些资源做了写操作时才有可能发生,只要资源没有发生变化,多个线程读取相同的资源就是安全的。

当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。导致竞态条件发生的代码区称作临界区。

如果一个资源的创建,使用,销毁都在同一个线程内完成,且永远不会脱离该线程的控制,则该资源的使用就是线程安全的。

四、java同步块

Java中的同步块用synchronized标记。同步块在Java中是同步在某个对象上。所有同步在一个对象上的同步块在同时只能被一个线程进入并执行操作。所有其他等待进入该同步块的线程将被阻塞,直到执行该同步块中的线程退出。

有四种不同的同步块:

实例方法

静态方法

实例方法中的同步块

静态方法中的同步块

实例方法同步:

public synchronized void add(int value){

this.count += value;

}

Java实例方法同步是同步在拥有该方法的对象上。这样,每个实例其方法同步都同步在不同的对象上,即该方法所属的实例。只有一个线程能够在实例方法同步块中运行。如果有多个实例存在,那么一个线程一次可以在一个实例同步块中执行操作。一个实例一个线程。

静态方法同步:

public static synchronized void add(int value){

count += value;

}

静态方法的同步是指同步在该方法所在的类对象上。因为在Java虚拟机中一个类只能对应一个类对象,所以同时只允许一个线程执行同一个类中的静态同步方法。

实例方法中的同步块:

public void add(int value){

synchronized(this){

this.count += value;

}

}

注意Java同步块构造器用括号将对象括起来。在上例中,使用了“this”,即为调用add方法的实例本身。在同步构造器中用括号括起来的对象叫做监视器对象。上述代码使用监视器对象同步,同步实例方法使用调用方法本身的实例作为监视器对象。一次只有一个线程能够在同步于同一个监视器对象的Java方法内执行。

下面两个例子都同步他们所调用的实例对象上,因此他们在同步的执行效果上是等效的。

public class MyClass {

public synchronized void log1(String msg1, String msg2){

log.writeln(msg1);

log.writeln(msg2);

}

public void log2(String msg1, String msg2){

synchronized(this){

log.writeln(msg1);

log.writeln(msg2);

}

}

}

静态方法中的同步块:

public class MyClass {

public static synchronized void log1(String msg1, String msg2){

log.writeln(msg1);

log.writeln(msg2);

}

public static void log2(String msg1, String msg2){

synchronized(MyClass.class){

log.writeln(msg1);

log.writeln(msg2);

}

}

}

这两个方法不允许同时被线程访问。如果第二个同步块不是同步在MyClass.class这个对象上。那么这两个方法可以同时被线程访问。

五、java线程通信

线程通信的目标是使线程间能够互相发送信号。另一方面,线程通信使线程能够等待其他线程的信号。

Java有一个内建的等待机制来允许线程在等待信号的时候变为非运行状态。java.lang.Object 类定义了三个方法,wait()、notify()和notifyAll()来实现这个等待机制。

一个线程一旦调用了任意对象的wait()方法,就会变为非运行状态,直到另一个线程调用了同一个对象的notify()方法。为了调用wait()或者notify(),线程必须先获得那个对象的锁。也就是说,线程必须在同步块里调用wait()或者notify()。

以下为一个使用了wait()和notify()实现的线程间通信的共享对象:

public class MyWaitNotify{

MonitorObject myMonitorObject = new MonitorObject();

boolean wasSignalled = false;

public void doWait(){

synchronized(myMonitorObject){

while(!wasSignalled){

try{

myMonitorObject.wait();

} catch(InterruptedException e){...}

}

//clear signal and continue running.

wasSignalled = false;

}

}

public void doNotify(){

synchronized(myMonitorObject){

wasSignalled = true;

myMonitorObject.notify();

}

}

}

注意以下几点:

1、不管是等待线程还是唤醒线程都在同步块里调用wait()和notify()。这是强制性的!一个线程如果没有持有对象锁,将不能调用wait(),notify()或者notifyAll()。否则,会抛出IllegalMonitorStateException异常。

2、一旦线程调用了wait()方法,它就释放了所持有的监视器对象上的锁。这将允许其他线程也可以调用wait()或者notify()。

3、为了避免丢失信号,必须把它们保存在信号类里。如上面的wasSignalled变量。

4、假唤醒:由于莫名其妙的原因,线程有可能在没有调用过notify()和notifyAll()的情况下醒来。这就是所谓的假唤醒(spurious wakeups)。为了防止假唤醒,保存信号的成员变量将在一个while循环里接受检查,而不是在if表达式里。这样的一个while循环叫做自旋锁。

5、不要在字符串常量或全局对象中调用wait()。即上面MonitorObject不能是字符串常量或是全局对象。每一个MyWaitNotify的实例都拥有一个属于自己的监视器对象,而不是在空字符串上调用wait()/notify()。

六、java中的锁

自Java 5开始,java.util.concurrent.locks包中包含了一些锁的实现,因此你不用去实现自己的锁了。

常用的一些锁:

java.util.concurrent.locks.Lock;

java.util.concurrent.locks.ReentrantLock;

java.util.concurrent.locks.ReadWriteLock;

java.util.concurrent.locks.ReentrantReadWriteLock;

一个可重入锁(reentrant lock)的简单实现:

public class Lock {

boolean isLocked = false;

Thread lockedBy = null;

int lockedCount = 0;

public synchronized void lock() throws InterruptedException{

Thread callingThread = Thread.currentThread();

while(isLocked && lockedBy != callingThread){

wait();

}

isLocked = true;

lockedCount++;

lockedBy = callingThread;

}

public synchronized void unlock(){

if(Thread.currentThread() == this.lockedBy){

lockedCount--;

if(lockedCount == 0){

isLocked = false;

notify();

}

}

}

}

注意的一点:在finally语句中调用unlock()

lock.lock();

try{

//do critical section code, which may throw exception

} finally {

lock.unlock();

}

七、java中其他同步方法

信号量(Semaphore):java.util.concurrent.Semaphore

阻塞队列(Blocking Queue):java.util.concurrent.BlockingQueue

public class BlockingQueue {

private List queue = new LinkedList();

private int limit = 10;

public BlockingQueue(int limit) {

this.limit = limit;

}

public synchronized void enqueue(Object item) throws InterruptedException {

while (this.queue.size() == this.limit) {

wait();

}

if (this.queue.size() == 0) {

notifyAll();

}

this.queue.add(item);

}

public synchronized Object dequeue() throws InterruptedException {

while (this.queue.size() == 0) {

wait();

}

if (this.queue.size() == this.limit) {

notifyAll();

}

return this.queue.remove(0);

}

}

八、java中的线程池

Java通过Executors提供四种线程池,分别为:

newCachedThreadPool

创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

newFixedThreadPool

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

newScheduledThreadPool

创建一个大小无限制的线程池。此线程池支持定时以及周期性执行任务。

newSingleThreadExecutor

创建一个单线程的线程池。此线程池支持定时以及周期性执行任务。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

线程池简单用法:

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class Main {

public static void main(String[] args) {

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

for (int i = 0; i < 10; i++) {

final int index = i;

cachedThreadPool.execute(new Runnable() {

public void run() {

System.out.println(index);

}

});

}

}

}

以上就是浅谈java多线程编程的详细内容,更多关于java多线程的资料请关注脚本之家其它相关文章!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/310026.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[C++STL]常用排序算法

代码如下: #include <iostream> #include <algorithm> #include <vector> using namespace std;void myPrint(int val) {cout << val << " "; }void test01() {vector<int>v;v.push_back(10);v.push_back(30);v.push_back(50);…

Fliptile POJ - 3279 (翻转)(二进制+对第一行暴力遍历翻转的位置)

题意&#xff1a;给你一个给你一个矩阵&#xff0c;有黑白两个状态。每次你可以选择一个变为其相反的状态&#xff0c;它周围的4个都会变成相反的状态。问你最少需要改变多少个使得矩阵中的状态全为白色&#xff0c;若有多个答案&#xff0c;输出字典序最小的 思路&#xff1a…

新基建火了,开源云计算渠道能做什么?

导语对于开源云计算厂商而言&#xff0c;如果希望在抢滩新基建上构建差异化竞争优势&#xff0c;具备高超的售前技能、售后体验&#xff0c;并拥有创新的技术服务能力与解决方案构建能力是实有必要的。巧了&#xff0c;这些都与渠道构建息息相关。开源云计算厂商在此前的渠道激…

python socket编程之双方相互通信简单实例_Python socket实现的简单通信功能示例

套接字(socket)是计算机网络数据结构&#xff0c;在任何类型的通信开始之前&#xff0c;网络应用程序必须创建套接字&#xff0c;可以将其比作电话的插孔&#xff0c;没有它将无法进行通信常用的地址家族AF_UNIX&#xff1a;基于文件&#xff0c;实现同一主机不同进程之间的通信…

[C++STL]常用拷贝和替换算法

代码如下: #include <iostream> #include <algorithm> #include <vector> #include <ctime> using namespace std;void myPrint(int val) {cout << val << " "; }void test01() {vector<int>v1;for (int i 0; i < 10…

Jin Ge Jin Qu hao UVA - 12563 (劲歌金曲)01背包,求装入的东西最多(相同多时价值大)

题目&#xff1a;白书p274 题意: KTV里面有n首歌曲你可以选择,每首歌曲的时长都给出了. 对于每首歌曲,你最多只能唱1遍. 现在给你一个时间限制t (t<10^9) , 问你在最多t-1秒的时间内可以唱多少首歌曲num , 且最长唱歌时间是多少time (time必须<t-1) ? 最终输出num1 和…

ASP.NET Core分布式项目实战(oauth2 + oidc 实现 client部分)--学习笔记

任务16&#xff1a;oauth2 oidc 实现 client部分实现 client 之前启动一下上一节的 server&#xff0c;启动之前需要清除一些代码注释 Program 的 MigrateDbContextpublic static void Main(string[] args) {BuildWebHost(args)//.MigrateDbContext<ApplicationDbContext&g…

java中vi是什么意思_java中的public void是什么意思?

java中的public void是什么意思&#xff1f;发布时间&#xff1a;2020-05-23 11:21:22来源&#xff1a;亿速云阅读&#xff1a;255作者&#xff1a;Leahjava中的public void是什么意思&#xff1f;相信很多新手都不太了解&#xff0c;今天小编为了让大家更加了解public void的意…

[C++STL]常用算术生成算法

代码如下: #include <iostream> #include <vector> #include <numeric> using namespace std;void test01() {vector<int>v;for (int i 0; i < 10; i){v.push_back(i);}int total accumulate(v.begin(), v.end(), 0);cout << "total …

前端异步对象的原理与使用方法

源宝导读&#xff1a;现今互联网的WEB网站&#xff0c;几乎没有不用到JS异步技术的&#xff0c;虽然大家经常用到&#xff0c;但Javascript提供的异步机制如何才能真正用好呢&#xff0c;可能很多开发小伙伴还有点含糊&#xff0c;本文将从常见的开发场景出发&#xff0c;系统的…

Millenium Leapcow POJ - 2111 (千禧年跳牛)(贪心找最长路径,记忆化)

题意&#xff1a; 给你一个矩阵&#xff0c;问你按照象棋马的走法&#xff0c;下一步比上一步的数大&#xff0c;问长度最长的序列是多长&#xff0c;然后输出序列。如果有多个最长序列输出字典序最小的那个。类似滑雪&#xff0c;找出最长路径&#xff0c;多个答案 输出字典序…

java1.8的stream_JDK1.8新特性(一):stream

搜索热词一.什么是stream&#xff1f;1.概述Java 8 API添加了一个新的抽象称为流Stream&#xff0c;可以让你以一种声明的方式处理数据。这种风格将要处理的元素集合看作一种流&#xff0c; 流在管道中传输&#xff0c; 并且可以在管道的节点上进行处理&#xff0c; 比如筛选&a…

[C++STL]常用集合算法

代码如下: #include <iostream> #include <vector> #include <numeric> #include <algorithm> using namespace std;class myPrint { public:void operator()(int val){cout << val << " ";} };void test01() {vector<int&g…

php给html传值,PHP传值到不同页面的三种常见方式及php和html之间传值问题_PHP

在项目开发中经常见到不同页面之间传值在web工作中&#xff0c;本篇文章给大家列出了三种常见的方式。接触PHP也有几个月了&#xff0c;本文总结一下这段日子中&#xff0c;在编程过程里常用的3种不同页面传值方法&#xff0c;希望可以给大家参考。有什么意见也希望大家一起讨论…

Seek the Name, Seek the Fame POJ - 2752 (理解KMP函数的失配)既是S的前缀又是S的后缀的子串

题意&#xff1a;给一个字符串S&#xff0c; 求出所有前缀pre&#xff0c;使得这个前缀也正好是S的后缀。 输出所有前缀的结束位置。 就是求前缀和后缀相同的那个子串的长度 然后从小到大输出,主要利用next数组求解。 例如 “ababcababababcabab”&#xff0c; 以下这些前缀…

2020 年了,WPF 还有前途吗?

2020年了&#xff0c;微软的技术也不断更新了, .Net Core 3.1、.Net Framework 4.8以及今年11月推出的.NET 5...win10平台也普及了很多&#xff0c;WPF可以在上面大展身手&#xff0c;可性能和内存占用还是不行&#xff0c;但是WPF强大的UI能力很吸引人。WPF已经凉了吗? 学WPF…

[C++STL]常用查找算法

代码如下: #include <iostream> #include <algorithm> #include <vector> #include <string> using namespace std;void test01() {vector<int>v;for (int i 0; i < 10; i){v.push_back(i);}vector<int>::iterator it find(v.begin(…

php asp 语法,ASP 语法

在我们的 ASP 教程中&#xff0c;每个实例都提供隐藏的 ASP 源代码。这样会使您更容易理解它们的工作原理。向浏览器写输出ASP 文件通常包含 HTML 标签&#xff0c;就像 HTML 文件。然而&#xff0c;ASP 文件也能包含服务器脚本&#xff0c;这些脚本被分隔符 包围起来。服务器脚…

#10003. 「一本通 1.1 例 4」加工生产调度(贪心)

加工生产调度 题目描述 某工厂收到了n个产品的订单&#xff0c;这n个产品分别在A、B两个车间加工&#xff0c;并且必须先在A车间加工后才可以到B车间加工。 某个产品i在A、B两车间加工的时间分别为Ai、Bi。询问怎样安排这n个产品的加工顺序&#xff0c;才能使总的加工时间最短…

不要把异常当做业务逻辑,这性能可能你无法承受

一&#xff1a;背景1. 讲故事在项目中摸爬滚打几年&#xff0c;应该或多或少的见过有人把异常当做业务逻辑处理的情况(┬&#xff3f;┬)&#xff0c;比如说判断一个数字是否为整数,就想当然的用try catch包起来&#xff0c;再进行 int.Parse&#xff0c;如果抛异常就说明不是整…