1. 线程安全

多个线程在访问同一个对象的时候不需要其他额外的同步手段或措施就能保证该对象被正确的访问并产生正确的执行结果。那么这个对象就是线程安全的。

线程安全的代码必须具备一个特征:代码本身封装了所有必要的正确性保障手段(如互斥同步),使用该代码的开发人员无需关心多线程的问题也不用自己采用任何措施来保证多线程的正确调用。

线程不安全的代码在多个线程中使用时必须作同步处理,否则可能产生不可预期的后果。

2. Java中的线程安全

可以将Java语言中各种操作共享的数据分为以下五类:不可变、绝对线程安全、相对线程安全、线程兼容和线程对立。

2.1 不可变

不可变(Immutable) 的对象一定是线程安全的,不需要再采取任何的线程安全保障措施。

只要一个不可变的对象被正确地构建出来,永远也不会看到它在多个线程之中处于不一致的状态。

多线程环境下,应当尽量使对象成为不可变,来满足线程安全。

不可变的类型:

  • final 关键字修饰的基本数据类型
  • String
  • 枚举类型
  • Number 部分子类,如 Long 和 Double 等数值包装类型,BigInteger 和BigDecimal 等大数据类型。但同为 Number 的原子类 AtomicInteger 和AtomicLong 则是可变的。

如果共享数据是一个基本数据类型,只需要在定义的时候声明为final即可;

如果是共享数据是一个对象,则需要保证对象的行为不会对其状态产生任何影响才行(最简单的做法就是把对象中带有状态的变量都声明为final)。

对于集合类型,可以使用 Collections.unmodifiableXXX() 方法来获取一个不可变的集合。

public class ImmutableExample {public static void main(String[] args) {Map<String, Integer> map = new HashMap<>();Map<String, Integer> unmodifiableMap = Collections.unmodifiableMap(map);unmodifiableMap.put("a", 1);}
}

Collections.unmodifiableXXX() 先对原始的集合进行拷贝,需要对集合进行修改的方法都直接抛出异常。

public V put(K key, V value) {throw new UnsupportedOperationException();
}

2.2 绝对线程安全

不管运行环境如何,调用者都不需要任何额外的同步措施的类可以称作是绝对线程安全的,但是这通常是需要付出相对较大的代价的。

2.3 相对线程安全

对这个对象单独的操作是线程安全,在调用单个操作的时候不需要做其他额外的保障措施,但是对于一些特定顺序的连续调用,就可能需要在调用端使用额外的同步手段来保证调用的正确性。

总之相对线程安全就是多个线程对这个对象单独操作的时候是线程安全的,但是如果多个线程操作这个对象的不同行为时就需要调用端使用同步的手段来保证调用的正确顺序了。

在Java语言中,大部分的线程安全类都是属于这种类型,例如Vector、HashTable、Collections的synchronizedCollection()等。

对于下面的代码,如果删除元素的线程删除了 Vector 的一个元素,而获取元素的线程试图访问一个已经被删除的元素,那么就会抛出ArrayIndexOutOfBoundsException。

public class VectorUnsafeExample {private static Vector<Integer> vector = new Vector<>();public static void main(String[] args) {while (true) {for (int i = 0; i < 100; i++) {vector.add(i);} ExecutorService executorService = Executors.newCachedThreadPool();executorService.execute(() -> {for (int i = 0; i < vector.size(); i++) {vector.remove(i);}});executorService.execute(() -> {for (int i = 0; i < vector.size(); i++) {vector.get(i);}});executorService.shutdown();}}
}

如果要保证上面的代码能正确执行下去,就需要对删除元素和获取元素的代码进行同步:

executorService.execute(() -> {synchronized (vector) {for (int i = 0; i < vector.size(); i++) {vector.remove(i);}}
});
executorService.execute(() -> {synchronized (vector) {for (int i = 0; i < vector.size(); i++) {vector.get(i);}}
});

2.4 线程兼容

线程兼容是指对象本身并不是线程安全的,但是可以通过在调用端正确地使用同步手段来保证对象在并发环境中可以安全地使用。

如Vector和HashTable相对应的集合类ArrayList和HashMap等。

2.5 线程对立

线程对立是指物理调用端是否采取了同步措施,都无法再多线程环境中并发使用的代码

3. 守护线程

Java提供了两种线程:守护线程和用户线程

守护线程又称“服务进程”、“精灵线程”或“后台线程”。

是指在程序运行时再后台提供的一种通用服务的线程,这种线程并不属于程序中不可或缺的部分。

  • 任何一个守护线程都是整个JVM中所有非守护线程的“保姆”

3.1 守护线程和用户线程唯一不同点

如果用户线程已经全部退出运行,只剩下守护线程存在了,JVM也就退出了。

只要有任何的用户线程还在运行,JVM就不会终止。

3.2 自己设置守护线程

Java语言中,守护线程一般具有较低的优先级,并非只由JVM内部提供,用户在编写程序时也可以自己设置守护线程。

如将一个用户线程设为守护线程的方法:即,在调用start()方法启动线程之前调用对象的setDaemon(true)方法

若以上参数设置为false,则表示是用户进程模式。

注意:当在一个守护线程中产生了其它线程,那么这些新产生的线程默认还是守护线程,

class ThreadDemo extends Thread{public viod run(){System.out.println(Thread.currentThread().getName()+":begin");try{Thread.sleep(1000);}catch(InterruptedExceptioin e){e.printStackTrace();}System.out.println(Thread.currentThread().getName()+":end");}
}
public class Test{public static void main(String[] args){System.out.println("test:begins");Thread t1=new Thread();t1.setDeamon(true);t1.start();System.out.println("test:end");}
}

test:begin

test:end

Thread-0:begin

未输出Thread-0:end,由于在启动线程前把它设为守护线程了。当程序中只存在守护线程时,JVM是可以退出的。

因此当test方法调用结束后,mian线程将退出,此时线程t1还出与休眠状态,没有运行结束。但由于JVM中只剩守护线程了,JVM会自动关闭。

3.3 守护线程的例子

典型例子就是垃圾回收器

只要JVM启动,它始终在运行,实时监控和管理系统中可以被回收的资源。

4. join()

作用:让调用该方法的线程在执行完run()之后,再执行join()后面的代码。

简单来说:就是将两个线程合并,用于实现同步功能

具体:可以通过线程A的join()开等待线程A的结束,或者使用线程A的join(2000)方法来等待A的结束,

public class Test{public static void main(String[] args){Thread t=new Thread(new ThreadImp());t.start();try{t.join();if(t.isAlive())//t已经结束System.out.println("t has not finished");elseSystem.out.println("t has finished");System.out.println("joinFinish");}catch(InterruptedException e){e.printStackTrace();}}
}class ThreadImp implments Runnable{public void run(){try{System.out.println("Begin ThreadImp");Thread.sleep(5000);System.out.println("End ThreadImp");}catch(InterruptedException e){e.printStackTrace();}}
}

Begin ThreadImp

t has finished

joinFinish

End ThreadImp

线程安全、守护线程、join()相关推荐

  1. JAVA笔记13__创建线程/线程休眠/等待线程终止/线程中断/守护线程

    /*** 线程:是进程的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个进程(单线程程序)* 多线程两种实现方法:1.继承Thread类 2.实现Runnable接口 ...

  2. 额!Java中用户线程和守护线程区别这么大?

    作者 | 王磊 来源 | Java中文社群(ID:javacn666) 转载请联系授权(微信ID:GG_Stone) 在 Java 语言中线程分为两类:用户线程和守护线程,而二者之间的区别却鲜有人知, ...

  3. 网络编程-线程,守护线程,线程互斥锁-26

    网络编程-线程,守护线程,线程互斥锁-26 内容: 进程部分: 进程间通信=>IPC 生产者消费者模型() 线程部分: 1. 线程理论() 2.开启线程的两种方式() 3. 线程对象其他相关的属 ...

  4. 线程的优先级、等待线程、守护线程

    线程的优先级.等待线程.守护线程 我们可以通过 public final void setPriority(int newPriority) 来设置线程的优先级,但是优先级并不是绝对的,只是相对来说比 ...

  5. java中的后台线程、前台线程、守护线程区别

    java中的后台线程.前台线程.守护线程区别 区别和联系 区别 联系 区别和联系 区别 后台线程和守护线程是一样的. 后台线程不会阻止进程的终止,而前台线程会, 可以在任何时候将前台线程修改为后台线程 ...

  6. python守护线程_Python守护线程用法实例

    本文实例讲述了Python守护线程用法.分享给大家供大家参考,具体如下: 如果你设置一个线程为守护线程,就表示你在说这个线程是不重要的,在进程退出的时候,不用等待这个线程退出.如果你的主线程在退出的时 ...

  7. Java线程之守护线程(Daemon)

    http://blog.csdn.net/mq612/article/details/1520571 守护线程(Daemon) Java有两种Thread:"守护线程Daemon" ...

  8. Java线程之守护线程(Daemon) .

    http://blog.csdn.net/mq612/article/details/1520571 守护线程(Daemon) Java有两种Thread:"守护线程Daemon" ...

  9. python开两个守护线程_python 守护线程

    守护线程 如果python线程是守护线程,那么以为着这个线程是"不重要"的,"不重要"意味着如果他的父进程结束了但该守护线程没有运行完,守护进程就会被强制结束. ...

  10. java并发:初探用户线程和守护线程

    用户线程和守护线程 用户线程 用户线程执行完,jvm退出.守护线程还是可以跑的 /*** A <i>thread</i> is a thread of execution in ...

最新文章

  1. canvas 文字颜色_实现一个canvas小画板
  2. 免费赠送年终汇报总结模板福利合集
  3. datavideo切换台说明书_【新品发布】datavideo SE-650 高清四通道切换台
  4. 欧拉项目第三题之最大质数因子
  5. sql server锁异常_SQL Server中异常处理的背景
  6. 几篇关于Cecil混淆程序集的文章
  7. 18 Strings for Mac(Xcode文件翻译工具)
  8. 如何做好产品需求设计和开发
  9. 甘肃刘家峡赤壁“结”出多彩冰瀑
  10. Linux版的led显示屏控制软件,中航Led显示屏控制软件下载
  11. Amplify Shader Editor手册 Unity ASE(中文版)
  12. 免费高清图片素材网站
  13. 【Matlab文件操作】打开、创建、更改和删除文件与文件夹以及获取文件信息
  14. js监听只读文本框_js设置input文本框只读
  15. # 汉洛塔问题的解决思路及其代码
  16. 在kindle上阅读网络小说的正确方法——Kindle网文助手
  17. [ 认知心理学 ] 帮我家呆瓜整理的《认知心理学》资料,麻了 --- 考研复习 --- 期末复习
  18. a豆:在讲一个珍重每一个人的故事
  19. php fpm 错误日志在哪,php fpm如何开启错误日志
  20. Java BIO的基本介绍

热门文章

  1. 单点登录之实战CAS5.1.x(六)——REST协议
  2. 使用nginx+Apache负载均衡及动静分离
  3. oracle错误处理及实操-【INS-20802】
  4. PostgreSQL csvlog 源码分析
  5. Centos-Server-LNMP整合---源安装
  6. 微软北大联合提出换脸AI和脸部伪造检测器,演绎现实版「矛与盾」?
  7. 大学生html5设计大赛方案,2018年大学生三维设计大赛策划书范文
  8. 设计模式(第一次复习)
  9. linux deepin ubuntu apt安装openjdk-8-jdk
  10. 【网址收藏】Porter:面向裸金属环境的 Kubernetes 开源负载均衡器