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 ...
随机推荐
- 【65】Mybatis详解
Mybatis介绍 MyBatis是一款一流的支持自定义SQL.存储过程和高级映射的持久化框架.MyBatis几乎消除了所有的JDBC代码,也基本不需要手工去设置参数和获取检索结果.MyBatis能够 ...
- 关于hashCode与equals
首先我得说明,在我们自己写得类中你可以复写这两个方法,此时从语法的角度来说,他们没关系. 在object中 public native int hashCode(); public boolean e ...
- ViewPagerIndicator+viewpager指示器详解
前几天学习了ViewPager作为引导页和Tab的使用方法.后来也有根据不同的使用情况改用Fragment作为Tab的情况,以及ViewPager结合FragmentPagerAdapter的使用.今 ...
- LeetCode(38)-Valid Sudoku
题目: Determine if a Sudoku is valid, according to: Sudoku Puzzles - The Rules. The Sudoku board could ...
- ubuntu12.04:jdk7:手动安装
总的原则:将jdk-7u10-linux-x64.tar.gz压缩包解压至/usr/lib/jdk,设置jdk环境变量并将其修改为系统默认的jdk 将jdk-7u5-linux-x64.tar.gz拷 ...
- 熊猫猪新系统测试之二:Mac OS X 10.10 优胜美地
在第一篇windows 10技术预览版测试之后,本猫为大家呈现另一个刚刚才更新的mac操作系统:"优胜美地".苹果同样一改以猫科动物为代号命名的传统,在10.9的Mavericks ...
- c#调用野狗云 rest api
野狗云就不多介绍了,这里主要是记录一下c#调用他们提供的rest api,把数据post到野狗云存储,直接上代码 static void Main(string[] args) { string st ...
- JAVA程序员面试宝典
程序员面试之葵花宝典 面向对象的特征有哪些方面 1. 抽象:抽象就是忽略一个主题中与当前目标2. 无关的那些方面,3. 以便更充分地注意与当前目标4. 有关的方面.抽象并不5. 打算了解全部问题 ...
- Liunx权限修改命令
语法: chmod 数值 文件名 例: chmod 644 mm.txt 命令执行后,文件mm.txt的权限值为 rw-r--r-- 详解: 权限分为 读.写.执行三种,分别用字母 ...
- Jmeter 性能测试术语
1.5 术语及缩写词 测试时间:一轮测试从开始到结束所使用的时间 并发线程数:测试时同时访问被测系统的线程数.注意,由于测试过程中,每个线程都是以尽可能快的速度发请求,与实际用户的使用有极大差别,所以 ...