java并发编程基础——线程同步
线程同步
一、线程安全问题
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
线程安全问题往往发生在多个线程调用同一方法或者操作同一变量,但是我们要知道其本质就是CPU对线程的随机调度,CPU无法保证一个线程执行完其逻辑才去调用另一个线程执行。
package threadtest;
public class ThreadTest implements Runnable{
static int i = 0;
public void incre() {
i++;
}
@Override
public void run() {
for(int j=0;j<1000000;j++) {
incre();
}
}
public static void main(String[] args) throws InterruptedException {
ThreadTest tt = new ThreadTest();
Thread t1 = new Thread(tt);
Thread t2 = new Thread(tt);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
由于 i++不是原子操作,先读取值,再加1 赋值,所以当在读取i值的时候线程切换了,导致两个线程读取的i相同,导致线程安全问题。
1363390 //结果小于2000000
二、线程同步
java多线程支持引入了同步监视器来解决线程同步问题,通过synchronized关键字,主要有同步方法和同步代码块
执行同步代码前必须先获得对同步监视器的锁定(任何时刻都只有一个线程可以获得同步监视器的锁定)
java5开始提供了更强大的同步机制,同步锁Lock
1、同步方法: synchronized修饰方法
package threadtest;
public class ThreadTest implements Runnable{
static int i = 0;
public synchronized void incre() {
i++;
}
@Override
public void run() {
for(int j=0;j<1000000;j++) {
incre();
}
}
public static void main(String[] args) throws InterruptedException {
ThreadTest tt = new ThreadTest();
Thread t1 = new Thread(tt);
Thread t2 = new Thread(tt);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
同步方法的同步监视器就是方法所属的对象本身
2000000 //结果正常
synchronized修饰静态方法
package threadtest;
public class ThreadTest implements Runnable{
static int i = 0;
/**
* 同步静态方法的同步监视器是该类对应的class对象
*/
public static synchronized void incre() {
i++;
}
@Override
public void run() {
for(int j=0;j<1000000;j++) {
incre();
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new ThreadTest());
Thread t2 = new Thread(new ThreadTest());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
上面类中synchronized修饰的静态方法,同步监视器是该类对应的class对象,i是类属性,多个线程调用不同实例,i也是线程安全的。
2000000
2、同步代码块
除了使用关键字修饰实例方法和静态方法外,还可以使用同步代码块,在某些情况下,我们编写的方法体可能比较大,同时存在一些比较耗时的操作,而需要同步的代码又只有一小部分,如果直接对整个方法进行同步操作,可能会得不偿失,此时我们可以使用同步代码块的方式对需要同步的代码进行包裹,这样就无需对整个方法进行同步操作了
package threadtest;
public class ThreadTest implements Runnable{
static int i = 0;
/**
* 同步代码块,synchronized(obj),obj就是同步监视器
*/
public void incre() {
synchronized(this) {
i++;
}
}
@Override
public void run() {
for(int j=0;j<1000000;j++) {
incre();
}
}
public static void main(String[] args) throws InterruptedException {
ThreadTest tt = new ThreadTest();
Thread t1 = new Thread(tt);
Thread t2 = new Thread(tt);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
同步代码块修饰静态方法
package threadtest;
public class ThreadTest implements Runnable{
static int i = 0;
/**
* 同步代码块,synchronized(obj),obj就是同步监视器
*/
public static void incre() {
synchronized(ThreadTest.class) {
i++;
}
}
@Override
public void run() {
for(int j=0;j<1000000;j++) {
incre();
}
}
public static void main(String[] args) throws InterruptedException {
//ThreadTest tt = new ThreadTest();
Thread t1 = new Thread(new ThreadTest());
Thread t2 = new Thread(new ThreadTest());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
3、同步锁Lock
java5开始提供了通过显示定义同步锁对象来实现同步。
在实现线程安全中,比较常用的是ReentrantLock(可重入锁),它是Lock接口的实现类。
package threadtest;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadTest implements Runnable{
private final ReentrantLock lock = new ReentrantLock();
static int i = 0;
public void incre() {
lock.lock();//加锁
try {
i++;
} finally {
lock.unlock();
}
}
@Override
public void run() {
for(int j=0;j<1000000;j++) {
incre();
}
}
public static void main(String[] args) throws InterruptedException {
ThreadTest tt = new ThreadTest();
Thread t1 = new Thread(tt);
Thread t2 = new Thread(tt);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
2000000//线程安全
4、死锁
当两个线程同时等待对方释放同步监视器就会发生死锁,java虚拟机没有检测,也没有采取措施来处理死锁情况,所以多线程编程时应该采取措施避免死锁出现。
一旦出现死锁,程序不会发生任何异常情况,也没有任何提示,只是所有线程处于阻塞状态,无法继续
下面程序就是发生死锁
package threadtest;
/**
* 一个简单的死锁例子,大概的思路:两个线程A和B,两把锁X和Y,现在A先拿到锁X,然后sleep()一段时间,我们知道sleep()是不会释放锁资源的。然后如果这段时间线程B拿到锁Y,也sleep()一段时间的话,那么等到两个线程都醒过来的话,那么将互相等待对方释放锁资源而僵持下去,陷入死锁。flag的作用就是让A和B获得不同的锁。
* @author rdb
*
*/
public class ThreadTest implements Runnable{ Object o1 = new Object();
Object o2 = new Object();
private boolean flag = true ; @Override
public void run() {
if(flag) {
flag = false;
synchronized (o1) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {
System.out.println("**************");
}
}
}else {
flag = true;
synchronized (o2) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1) {
System.out.println("**************");
}
}
}
} public static void main(String[] args) throws InterruptedException {
ThreadTest tt = new ThreadTest();
Thread t1 = new Thread(tt);
Thread t2 = new Thread(tt);
t1.start();
t2.start(); } }
java并发编程基础——线程同步的更多相关文章
- Java并发编程基础-线程安全问题及JMM(volatile)
什么情况下应该使用多线程 : 线程出现的目的是什么?解决进程中多任务的实时性问题?其实简单来说,也就是解决“阻塞”的问题,阻塞的意思就是程序运行到某个函数或过程后等待某些事件发生而暂时停止 CPU 占 ...
- java并发编程:线程同步和锁
一.锁的原理 java中每个对象都有一个内置锁.当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this)有关的锁.获得一个对象的锁也称为获取锁,当程序运 ...
- java并发编程基础——线程通信
线程通信 当线程在系统内运行时,程序通常无法准确的控制线程的轮换执行,但我们可以通过一些机制来保障线程的协调运行 一.传统的线程通信 传统的线程通信主要是通过Object类提供的wait(),noti ...
- java并发编程基础——线程的创建
一.基础概念 1.进程和线程 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程.(进程是资源分配的最小单位) 线程:同一类线程共享代码和数据 ...
- java并发编程基础——线程相关的类
线程相关类 java还为线程安全提供了一些工具类. 一.ThreadLocal类(Thread Local Variable) ThreadLocal类,是线程局部变量的意思.功用非常简单,就是为每一 ...
- java并发编程基础——线程池
线程池 由于启动一个线程要与操作系统交互,所以系统启动一个新的线程的成本是比较高的.在这种情况下,使用线程池可以很好的提升性能,特别是程序中涉及创建大量生命周期很短暂的线程时. 与数据库连接池类似,线 ...
- Java并发编程:线程的同步
Java并发编程:线程的同步 */--> code {color: #FF0000} pre.src {background-color: #002b36; color: #839496;} J ...
- Java并发编程基础
Java并发编程基础 1. 并发 1.1. 什么是并发? 并发是一种能并行运行多个程序或并行运行一个程序中多个部分的能力.如果程序中一个耗时的任务能以异步或并行的方式运行,那么整个程序的吞吐量和可交互 ...
- 并发-Java并发编程基础
Java并发编程基础 并发 在计算机科学中,并发是指将一个程序,算法划分为若干个逻辑组成部分,这些部分可以以任何顺序进行执行,但与最终顺序执行的结果一致.并发可以在多核操作系统上显著的提高程序运行速度 ...
随机推荐
- 通过Mellanox ConnectX NIC使用XDP加速
通过Mellanox ConnectX NIC使用XDP加速 Accelerating with XDP over Mellanox ConnectX NICs XDP(eXpress Data Pa ...
- 如何为应用选择最佳的FPGA(下)
如何为应用选择最佳的FPGA(下) How to select an FPGA board? FPGA板的选择在很大程度上受FPGA本身的影响,也受整个板的特性和性能的影响.们已经在上面的章节中讨论了 ...
- 【转】【NX二次开发】UFUN进度中断,单击停止可中断此操作
队长的博客: https://www.cnblogs.com/nxopen2018/p/13174207.html 显示此对话框,点击可中断操作: 用到的ufun函数: UF_ABORT_ask_fl ...
- NX二次开发-获取WCS标识
函数:UF_CSYS_ask_wcs() 函数说明:获取工作坐标系对象的标识. 用法: 1 #include <uf.h> 2 #include <uf_csys.h> 3 e ...
- 面试官:MySQL的可重复读级别能解决幻读问题吗?
引言 之前在深入了解数据库理论的时候,了解到事务的不同隔离级别可能存在的问题.为了更好的理解所以在MySQL数据库中测试复现这些问题.关于脏读和不可重复读在相应的隔离级别下都很容易的复现了. 但是对于 ...
- 【题解】滑雪 luogu1434 记忆化搜索
记忆化搜索入门题 题目 Michael喜欢滑雪.这并不奇怪,因为滑雪的确很刺激.可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你.Michael想知道在 ...
- 简单聊聊Ehcache缓存
最近工作没有那么忙,有时间来写写东西.今年的系统分析师报名已经开始了,面对历年的真题,真的难以入笔,所以突然对未来充满了担忧,还是得抓紧时间学习技术. 同事推了一篇软文,看到了这个Ehcache,感觉 ...
- 解决使用gomod后goland导包报红问题
解决使用gomod后goland导包报红问题 项目环境: ubuntu14+goland 问题详情: 在root用户下执行go mod init {module name}使用了gomod,并编译了项 ...
- 大型情感类技术连续剧-徒手撸一个 uTools(二)
前言 上篇手把手教你实现一个支持插件化的 uTools 工具箱我们介绍过了如何通过 electron 实现 utools 的插件功能体系,并按照 utools 的交互和设计做出了一套可以支持插件化的桌 ...
- 4.5 RNN循环神经网络(recurrent neural network)
自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取: https://www.cnblogs.com/bclshuai/p/11380657.html 1.1 RNN循环神经网络 ...