文档章节

Java多线程学习笔记

此鱼不得水
 此鱼不得水
发布于 2015/04/17 18:02
字数 2942
阅读 14
收藏 0
点赞 0
评论 0

                            Java多线程

开始于2015410日:

Java的线程机制是抢占式的,这表示调度机制会周期性的中断线程,将上下文切换到另一个任务,从而为每个线程都提供时间片,使得每个线程都会分配到数量合理的时间去驱动他的任务。

如果当多个线程共同访问一个可变的状态变量的时候没事使用合适的同步,那么线程就会出现错误。有三种方式可以修复这个错误:

  1. 不在线程之间共享该状态变量。

  2. 将状态变量修改为不可改变的变量。

  3. 在访问状态变量的时候使用同步。

线程安全性的定义:当多个线程访问某个类的时候,这个类始终科比变现出来正确的行为,那么就说这个类是线程安全的。

无状态线程一定是安全的。访问过程之中没有共享的变量也没有对其他对象的引用,所以一定是安全的。

以关键字synchronized修饰的方法就是一个横跨整个方法体的同步代码块,其中该同步代码块的锁就是方法调用所在的对象。静态的synchronized方法以Class对象作为锁。

对于ThreadLocal的学习以及使用(详见ThreadLocalRef.html)。

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

线程隔离的秘密,就在于ThreadLocalMap这个类。ThreadLocalMapThreadLocal类的一个静态内部类,它实现了键值对 的设置和获取(对比Map对象来理解),每个线程中都有一个独立的ThreadLocalMap副本,它所存储的值,只能被当前线程读取和修改。 ThreadLocal类通过操作每一个线程特有的ThreadLocalMap(这个Map与平时Util包下的Map不一样,要区分对待)副本,从而实现了变量访问在不同线程中的隔离。因为每个线程的变量都是自 己特有的,完全不会有并发错误。还有一点就是,ThreadLocalMap存储的键值对中的键是this对象指向的ThreadLocal对象,而值就 是你所设置的对象了。

对于native方法的理解:native关键字修饰的方法是Java调用了其他语言的方法,比如调用了C语言的相关方法,这里用native修饰的方法不能再出现abstract修饰符,因为被native方法修饰的方法已经有了实现体,而被abstract修饰的方法却没有实现体。往往在JDK源码之中会出现native修饰符(例如在Thread中大量出现了native),这是因为很多跟底层相关的东西都是用C语言来实现的,在JVM里面使用了部分C语言的方法来达到某些效果。而Java要想调用相关的方法,就可以直接用native关键字来修饰,这些方法往往在JVM中已经被写进去了,所以我们大可放心使用。

选择锁的原则:

同步是基于实际对象而不是对象引用的,多个变量可以引用同一个对象,变量也可以改变其值从而指向其他对象。因此,当选择一个对象锁时候,我们要根据实际对象而不是引用来考虑。作为一个原则,不要选择一个可能会在锁的作用域中改变的实例变量作为锁对象。

设置优先级:尽管JDK有多个优先级,但是他与多数操作系统都不能很好的映射。比如Windows上有7个优先级且不是固定的,所以这种映射关系也是不确定的。所以建议在使用优先级的时候只使用MAX_PRIORITY,NORM_PRIORITY,MIN_PRIORITY三种级别。优先级为7的线程就会比优先级为6的线程有更高的机会去执行。当一个优先级比较低的线程很难获得CPU的执行时候,就称这种行为为CPU饥饿,所以程序员要保证每一个程序都不会产生CPU饥饿才行。详情见(Java线程P141)

在Java的线程中所有对象都自动含有单一的锁(也成为监视器)。当在对象上调用任意的synchronized方法的时候,此对象都被加锁。这时候该对象上的其他synchronized方法只有等到前一个方法调用完毕并释放了锁的时候才可以进行调用。例如一个Demo类中有如下方法:

           synchronized void f(){}

           synchronized void g(){}

对于以上方法,如果某个任务调用了f(),对于同一个对象而言,就只有等到f()调用结束之后并释放了锁之后其他任务才能调用f()个g().所以对于某个特定的对象来说,其所有的synchronized方法共享一个锁,这可以用来防止多个任务同时访问被编码为对象内存。

原子操作是不能被线程调度机制中断的操作,一旦操作开始,那么他一定可以在可能发生的“上下文切换”之前执行完毕。

如果你声明一个域是violate的,那么只要对这个域产生了写操作,那么所有的操作都会看到这个修改。即使是用了本地缓存,情况也确实如此,violate域会议立即被写入到主存中,而读取操作就发生在主存中。如果多个任务在同时访问某个域,那么这个域就应该是violate的,否则,这个域就应该只能经同步来访问。同步也会导致向主存中刷新,因此如果一个域完全由synchronized方法语句保护,那么久不必将其设置为violate

线程的四种状态:

 新建(new):当线程被创建的时候,他只会短暂的处于这种状态。此时他已经分配了必须的系统资源,并执行了初始化。此时线程已经有资格获得CPU的资源了,之后调度器会将这个线程转变为可运行状态或者阻塞状态。

 就绪(Runnable):这种状态下,只要调度器把时间分配给线程,线程就可以运行。也就是说,在任意时刻,线程可以运行也可以不运行。只要调度器能分配时间片给线程,他就可以运行。这不同于死亡和阻塞状态。

 阻塞(Blocked):线程能够运行,但是有个条件阻止他运行。当线程处于阻塞状态时,调度器会忽略该线程,不会分配给线程任何CPU时间,直到线程重新就如就绪状态,他才有可能执行操作。

 死亡(dead):处于死亡或者终止状态的线程将不再是可调度的,并且再也不会得到CPU时间,他的任务已经结束,或者不再是可运行的。线程任务死亡的通常方式是从run()方法返回,但是任务的线程还可以被中断。

  在使用wait()nodify()方法进行操作的时候,如果在同步块中使用这些东西,进行同步的锁必须和wait(),nodify()方法使用同一个锁。在wait()方法调用之前会释放相关的锁,方法返回的时候又重新获取到对象锁,也就意味着在wait()的时候可以有其他线程进来这个相关的代码块或者方法,从而会有更多的线程处于等待状态。当使用了nodify()以后这些等待的线程之中会有一种一个重新获得对象锁并且运行起来。当使用了nodeifyAll()方法以后这些等待的线程会全部唤醒,但是其中只有一个会重新获得对象锁继续往下执行。而sleep()则始终都持有对象锁。

  目前在JVM中最常用的线程调度方式:因为JVM中有11个优先级,所以暂且设置有14个链表存在。其中11个优先级分别对应一个链表(Runnable),一个链表用来存放所有初始状态的链表(new),一个链表用来存放所有阻塞状态的链表(Blocked),一个链表用来存放所有死掉的链表(dead),这样所有状态的线程在链表中都有相应的对应位置了。当有高优先级的线程获得调度的时候处于低优先级的链表都处于Runnable状态。假如说某一时刻切换到了低优先级的链表时候,链表情况如下:AB→null;在A执行了一点时间后线程又切到了高优先级的链表上去执行,然后高优先级的链表某一时刻进入阻塞状态,这时候线程又切回到了低优先级的链表上。此时因为这个链表是这样的:B→A→null,所以这个时候处于表头的B任务优先得到了执行,当CPU又将时间片切换到别的链表的时候A任务又来到了表头。(详见Java线程P145)

 

 

  Java调度线程的两种模型:

绿色线程模型:在这种模型中,线程是由虚拟机来调度的。他们大多数都遵循在上面提到的以优先级为基础的理想化调度。

本地线程模型:在这种模型之中,线程是由运行虚拟机的操作系统进行调度的。尽管他们都遵循我们 在此讨论的调度策略,但是因为操作系统不同,这种模型导致了Java线程调度之间的很多细微差别。

系统调用:要使用内核来完成某项工作调用,在Java中这包括对大多数流进行读/写操作,以及创建套接字。

关于守护线程:

典型的:垃圾回收线程就是一个守护线程。

Thread中有两个跟守护线程相关的方法, void setDeamon(Boolean on):将一个线程设置成为守护线程。Boolean isDeamon():判断是不是守护线程。注意:在调用setDeamon()之前线程一定不能运行,如果一个线程运行了,你就不能使一个用户线程转换成一个守护线程。

线程池:线程池中拥有有限数量的线程,但是其中的每一个线程都可以依次运行多个对象。当某个特定的虚拟机不能支持程序需要的线程个数时候,线程池是很有用的。这里是一个关于线程池参考的博客:http://www.oschina.net/question/565065_86540

避免死锁的最简单原则:一个同步方法永远不要调用另外一个同步方法。(尽管是一个被推荐的原则,但是并不理想).还有一种方式可以避免死锁:就是与我们要使用的对象相关联但是更高级的对象加锁。避免死锁的最有用原则是确保以同样的顺序获得锁。

线程的内容大致就这么多了,以后有机会再总结吧。


© 著作权归作者所有

共有 人打赏支持
此鱼不得水
粉丝 2
博文 41
码字总数 23991
作品 0
洛阳
111 多线程JUC包下代码分析

Java多线程系列目录(共43篇) AtomicLongFieldUpdater:通过反射+CAS实现对传入对象的指定long字段实现类似AtomicLong的操作 http://www.cnblogs.com/skywang12345/p/javathreadscategory.ht...

素雷
2017/10/31
0
0
【Java并发性和多线程】Java并发性和多线程介绍

本文为转载学习 原文链接:http://tutorials.jenkov.com/java-concurrency/index.html 译文链接:http://ifeve.com/java-concurrency-thread/ 在过去单CPU时代,单任务在一个时间点只能执行单...

heroShane
2014/01/28
0
0
读书笔记之《Java并发编程的艺术》-并发编程基础

读书笔记部分内容来源书出版书,版权归本书作者,如有错误,请指正。 欢迎star、fork,读书笔记系列会同步更新 git https://github.com/xuminwlt/j360-jdk module j360-jdk-thread/me.j360....

Hi徐敏
2015/11/11
0
8
【Practical API Design学习笔记】同步与死锁

像Java这类语言,写多线程是很经常的事情,但是在多线程中,能够保证在不同环境中都不死锁是非常不容易的。没有一种方法能够保证不死锁,但是一些设计模式和建议对此可能是有用的。 1、文档线...

亭子happy
2013/05/28
0
0
Java多线程学习(二)synchronized关键字(2)

系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Ja...

一只蜗牛呀
04/16
0
0
Java多线程学习(五)线程间通信知识点补充

系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Ja...

一只蜗牛呀
04/16
0
0
12.5-全栈Java笔记:Java网络编程(三)

上节回顾:在学习了Socket在建立客户端和服务器单项通讯中,分别创建独立的Socket,并通过Socket的属性。 那么如何将两个Socket进行连接,从而达到客户端和服务器之间建立输入输出流进行通信...

全栈Java
06/26
0
0
Java多线程学习(四)等待/通知(wait/notify)机制

系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Ja...

一只蜗牛呀
04/16
0
0
从程序员走向java架构师的书籍推荐!

作为Java程序员来说,最痛苦的事情莫过于可以选择的范围太广,可以读的书太多,往往容易无所适从。我想就我自己读过的技术书籍中挑选出来一些,按照学习的先后顺序,推荐给大家,特别是那些想...

欧阳海阳
07/16
0
0
Java面试需要准备哪些多线程并发的技术要点

一、概念 什么是线程 一个线程要执行任务,必须得有线程 一个进程(程序)的所有任务都在线程中执行的 一个线程执行任务是串行的,也就是说一个线程,同一时间内,只能执行一个任务 多线程原理 同一...

码蚁说架构
05/31
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

fiddle 4 初始化

下载 配置fiddle 4 如果证书导出失败,执行下面脚本 D:\programs\Fiddler>makecert.exe -r -ss my -n "CN=DO_NOT_TRUST_FiddlerRoot, O=DO_NOT_TRUST, OU=Created by http://www.fiddler2.c......

柯里昂
5分钟前
0
0
rabbitmq学习记录(六)交换机Exchange-direct

实现功能:一条消息发送给多个消费者 交换机模式:direct 相比于之前的fanout模式,可以进一步的筛选获取消息的消费者。 fanout模式下,只要消费者监听的队列,已经与接收生产者消息的交换机...

人觉非常君
22分钟前
0
0
Java 之 枚举

Java 中声明的枚举类,均是 java.lang.Enum 类的子类,Enun 类中的常用方法有: name() 返回枚举对象名称 ordinal() 返回枚举对象下标 valueOf(Class enumType, String name) 转换枚举对象 ...

绝世武神
30分钟前
0
0
使用爬虫实现代理IP池之放弃篇

啥叫代理IP以及代理IP池 概念上的东西网上搜索一下就好了,这里简单科普一下(大部分会读这篇文章的人,基本是不需要我来科普的),白话说就是能联网并提供代理访问互联网的服务器,它提供的...

一别丶经年
46分钟前
0
0
sqoop导入数据到Base并同步hive与impala

使用Sqoop从MySQL导入数据到Hive和HBase 及近期感悟 基础环境 Sqool和Hive、HBase简介 Sqoop Hive HBase 测试Sqoop 使用Sqoop从MySQL导入数据到Hive 使用复杂SQL 调整Hive数据类型 不断更新 ...

hblt-j
今天
0
0
Dart 服务端开发 文件上传

clent端使用angular组件 upload_component.html form id="myForm" method="POST" enctype="multipart/form-data"> <input type="file" name="fileData"> <!-- file field --></form>......

scooplol
今天
0
0
apache和tomcat同时开启,乱码问题

tomcat和apache同时开启,会走apache的转发,执行的是AJP/1.3协议。所以在tomcat的配置文件server中, <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" useBodyEncodingForU......

Kefy
今天
0
0
使用ssh-keygen和ssh-copy-id三步实现SSH无密码登录 和ssh常用命令

ssh-keygen 产生公钥与私钥对. ssh-copy-id 将本机的公钥复制到远程机器的authorized_keys文件中,ssh-copy-id也能让你有到远程机器的home, ~./ssh , 和 ~/.ssh/authorized_keys的权利 第一步...

xtof
今天
0
0
orcale 查询表结构

SELECT t.table_name, t.colUMN_NAME, t.DATA_TYPE || '(' || t.DATA_LENGTH || ')', t1.COMMENTS FROM User_Tab_Cols t, User_Col_Comments t1WHERE t.table_name......

wertwang
今天
0
0
华为nova3超级慢动作酷玩抖音,没有办法我就是这么强大

华为nova3超级慢动作酷玩抖音,没有办法我就是这么强大!华为nova3超级慢动作酷玩抖音,没有办法我就是这么强大! 在华为最新发布的nova 3手机上,抖音通过华为himedia SDK集成了60fps、超级...

华为终端开放实验室
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部