java多线程及线程安全详解
为什么要使用多线程:
单线程只能干一件事 而多线程可以同时干好多事(将任务放到线程里执行 效率高)
而所谓同时干并不是真正意义上的同时 只是(这里就叫CPU)cpu在每个线程中随机切换来执行 线程中要干的活
多线程编写:
1)第一种:(线程类)
class Stu1 extends Thread{
//重写 run方法
}
调用:Stu1 su = new Stu1();
su.start()//内部会自动调用run方法 把run方法放到线程上调用
2)第二种:普通任务类(由于第一种类只能是单继承 就没法实现继承其他父类并且继承线程类 所以第一种方法扩展性比较差)
直接创建线程对象 线程内干的事 放到一个自定义对象里面实现 (用到修饰设计模式)
底层代码
class Thread{
private Runnable r
public void Thread(Runnable r){ //利用有参构造将自定义对象传进来
this.r = r;
}
public void start(){
r.run();
}
}
class Stu implements Runnable{} 通过实现接口 线程类底层start调用的run方法 实际就是我们自定义类中的run方法
Thread th = new Thread(new Stu()) //直接创建线程对象
th.start();
我们用第二种方法创建多线程如下:
public static void main(String[] args) {
// 不同线程干同一件事
SaleWindow sw = new SaleWindow();
Thread t1 = new Thread(sw);
Thread t2 = new Thread(sw);
t1.setName("窗口A");
t2.setName("窗口B");
t1.start();
t2.start();
}
public class SaleWindow implements Runnable {
private int ticketCount = 10;
@Override
public void run() {
// TODO Auto-generated method stub
// 多个窗口卖票
for(int i = 0;i<10;i++){
if(ticketCount>0){
//字符串拼接信息 变量+"" 就可以拼接成字符串
System.out.println(Thread.currentThread().getName()+"卖出"+ticketCount+"张票");
ticketCount--;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
}
}
}
}
}
最后结果图:

从运行结果来看:同一张票卖给了二个人 这是在现实生活中不允许的 这样就会产生线程安全问题。
而产生线程安全问题的有三个要素必须同时满足才会产生线程安全:
1、必须有共享资源
2、必须是多线程环境
3、每个线程都适用了共享资源
而上面的例子:票是共享资源、又是多线程环境、线程执行任务的时候又使用了共享资源 所以会产生线程安全
怎么解决线程安全?
解决线程安全其核心:就是将某一个线程中的任务给锁(同步锁)起来,这个时候JVM就不得不等到本任务里的代码执行完以后在去执行另外一个线程里的任务。
(ps:在没加同步锁时jvm可以随时在线程之间随机切换,很有可能本线程的任务没有执行完就切换到别的线程上去了)
二种方法:
1、同步代码块:
public class SaleWindow implements Runnable {
private int ticketCount = 10;
@Override
public void run() {
// TODO Auto-generated method stub
// 多个窗口卖票
for (int i = 0; i < 10; i++) {//可以随意设置锁
synchronized (this) {
if (ticketCount > 0) {
// 字符串拼接信息 变量+"" 就可以拼接成字符串
System.out.println(Thread.currentThread().getName() + "卖出"
+ ticketCount + "张票");
ticketCount--;
try {
Thread.sleep(500);//每隔500毫秒 线程休眠 随后自己自动会唤醒(目的是为了调节线程速度)
} catch (InterruptedException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
}
}
}
}
}
}
2、同步方法:
public class SaleWindow implements Runnable {
private int ticketCount = 10;
//默认固定的锁对象this
//将产生线程安全的代码封装到方法里并设置成同步方法
public synchronized void syncB() {
if (ticketCount > 0) {
// 字符串拼接信息 变量+"" 就可以拼接成字符串
System.out.println(Thread.currentThread().getName() + "卖出"
+ ticketCount + "张票");
ticketCount--;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
}
}
}
@Override
public void run() {
// TODO Auto-generated method stub
// 多个窗口卖票
for (int i = 0; i < 10; i++) {
syncB();
}
}
}
结果图:

最后结果每个窗口不能卖同样的票解决了线程安全问题
线程之间的通信
(ps:线程之间的通信和线程安全无关联二者不是一回事)
怎样才能让二个线程之间进行通信?
线程之间是相互独立的互不联系 而真正意义上的通信是通过中间件(同步锁 必须是同一把锁)来达到线程之间通信的目的
案例:二个线程来回交替输出一条数据(意思就是必须按照我说一句你说一句的这个规则走)
class BingB extends Thread {
public void run(){
for(int i = 0;i<5;i++){
System.out.println("冰冰美,如花丑");
}
}
}
class RuH extends Thread{
public void run(){
for(int i = 0;i<5;i++){
System.out.println("如花美,冰冰丑");
}
}
}
public class Test {
public static void main(String[] args) {
BingB bb = new BingB();
RuH rh = new RuH();
bb.start();
rh.start();
}
}
结果图:此结果不是交替出现的

要想达到交替的目的代码如下:
class MyLock{
static Object o = new Object();
}
class BingB extends Thread {
public void run(){
for(int i = 0;i<5;i++){
//加锁(意思是必须等到本线程的任务代码执行完以后才去执行别的线程的代码)
synchronized (MyLock.o) {
System.out.println("冰冰美,如花丑");
MyLock.o.notify();//唤醒另一个线程(这里表示如花线程)
try {
MyLock.o.wait();//暂时彻底休眠本线程(不会自动唤醒 需要手动唤醒) 同时解锁 阻塞线程 代码就不会往下继续走 jvm会切换到另外一个线程中去
} catch (InterruptedException e) {
// TODO Auto-generated catch block
}
}
}
}
}
class RuH extends Thread{
public void run(){
for(int i = 0;i<5;i++){
synchronized (MyLock.o) {
System.out.println("如花美,冰冰丑");
MyLock.o.notify();//唤醒另一个线程(这里表示如冰冰程)
try {
MyLock.o.wait();//暂时彻底休眠本线程 同时解锁 阻塞线程
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
public class Test {
public static void main(String[] args) {
BingB bb = new BingB();
RuH rh = new RuH();
bb.start();
rh.start();
}
}

从以上例子可以看出产生线程安全并解决线程安全 到实现线程间的通信 都用到了 同步锁 所以 同步锁既可以解决线程安全问题 又可以解决线程之间的通信
多线程状态流程图:

写到这里有关线程的基本讲解完毕,如果内容有我自己理解错误的地方还请各位大神指教,小弟不吝求教!
java多线程及线程安全详解的更多相关文章
- Java多线程之线程池详解
前言 在认识线程池之前,我们需要使用线程就去创建一个线程,但是我们会发现有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因 ...
- JAVA多线程Thread VS Runnable详解
要求 必备知识 本文要求基本了解JAVA编程知识. 开发环境 windows 7/EditPlus 演示地址 源文件 进程与线程 进程是程序在处理机中的一次运行.一个进程既包括其所要执行的指令,也 ...
- Java 并发编程 | 线程池详解
原文: https://chenmingyu.top/concurrent-threadpool/ 线程池 线程池用来处理异步任务或者并发执行的任务 优点: 重复利用已创建的线程,减少创建和销毁线程造 ...
- Java面试问题——线程全面详解总结
一.多线程是什么?为什么要用多线程? 介绍多线程之前要介绍线程,介绍线程则离不开进程. 首先进程 :是一个正在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元: ...
- java多线程环境单例模式实现详解
Abstract 在开发中,如果某个实例的创建需要消耗很多系统资源,那么我们通常会使用惰性加载机制,也就是说只有当使用到这个实例的时候才会创建这个实例,这个好处在单例模式中得到了广泛应用.这个机制在s ...
- Java 多线程同步和异步详解
java线程 同步与异步 线程池 1)多线程并发时,多个线程同时请求同一个资源,必然导致此资源的数据不安全,A线程修改了B线 程的处理的数据,而B线程又修改了A线程处理的数理.显然这是由于全局资源造成 ...
- java多线程——同步块synchronized详解
Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(synchronzied) 实例方法同步 静 ...
- Java同步之线程池详解
带着问题阅读 1.什么是池化,池化能带来什么好处 2.如何设计一个资源池 3.Java的线程池如何使用,Java提供了哪些内置线程池 4.线程池使用有哪些注意事项 池化技术 池化思想介绍 池化思想是将 ...
- Java多线程中join方法详解
join()方法用于让当前执行线程等待join线程执行结束.其实现原理是不停的检查join线程是否存活,如果join线程存活则让当前线程永远等待. join()方法部分实现细节 while(isAli ...
随机推荐
- Cocos2D粒子发射器的纹理
每个例子发射器只能使用单个纹理发射粒子. 如果你需要在相同粒子效果中组合多重纹理,你将不得不创建多重的发射器节点并且决定谁的粒子将在其它粒子之上或之下显示.
- mysql进阶(十六)常见问题汇总
mysql进阶(十六)常见问题汇总 MySQL视图学习: http://www.itokit.com/2011/0908/67848.html 执行删除操作时,出现如下错误提示: 出现以上问题的原因是 ...
- HBase集群部署脚本
#!/bin/bash # Sync HBASE_HOME across the cluster. Must run on master using HBase owner user. HBASE_H ...
- Android高效率编码-第三方SDK详解系列(二)——Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能
Android高效率编码-第三方SDK详解系列(二)--Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能 我的本意是第二篇写Mob的shareSD ...
- ViewPagerIndicator+viewpager指示器详解
前几天学习了ViewPager作为引导页和Tab的使用方法.后来也有根据不同的使用情况改用Fragment作为Tab的情况,以及ViewPager结合FragmentPagerAdapter的使用.今 ...
- Mina源码阅读笔记(七)—Mina的拦截器FilterChain
Filter我们很熟悉,在Mina中,filter chain的用法也类似于Servlet的filters,这种拦截器的设计思想能够狠轻松的帮助我们实现对资源的统一处理.我们先大致连接下mina中的f ...
- obj-c编程16:键值编码(KVC)
我们可以借助obj-c中的键值编码(以后简称KVC,Key-Value Coding)来存取类的属性,通过指定所要访问的属性名字符串标示符,可以使用存取器方法来获取或设置类的属性.下面的例子,借助于K ...
- python标准库Beautiful Soup与MongoDb爬喜马拉雅电台的总结
Beautiful Soup标准库是一个可以从HTML/XML文件中提取数据的Python库,它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式,Beautiful Soup将会节省数小 ...
- Spring Cloud入门教程-Ribbon实现客户端负载均衡
简介 我们继续以之前博客的代码为基础,增加Ribbon组件来提供客户端负载均衡.负载均衡是实现高并发.高性能.可伸缩服务的重要组成部分,它可以把请求分散到一个集群中不同的服务器中,以减轻每个服务器的负 ...
- 【深度学习】目标检测算法总结(R-CNN、Fast R-CNN、Faster R-CNN、FPN、YOLO、SSD、RetinaNet)
目标检测是很多计算机视觉任务的基础,不论我们需要实现图像与文字的交互还是需要识别精细类别,它都提供了可靠的信息.本文对目标检测进行了整体回顾,第一部分从RCNN开始介绍基于候选区域的目标检测器,包括F ...