1、概念理解:

2、同步的解决方案:

1).基于代码

synchronized 关键字

  修饰普通方法:作用于当前实例加锁,进入同步代码前要获得当前实例的锁。

  修饰静态方法:作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁。

  修饰代码块:指定加锁对象,对给定对象加锁,进入同步代码块前要获得给定对象的锁。

code1

package com.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
* 同步方法
* @author Administrator
*
*/
public class SynchronizedMethod implements Runnable{ //静态共享变量 i
static int i = 0; /**
* 自增
*/
public synchronized void increase(){
i++;
} @Override
public void run() {
for (int j = 0; j < 100; j++) {
increase();
}
} public static void main(String[] args) throws InterruptedException {
SynchronizedMethod instance = new SynchronizedMethod();
ExecutorService executorService = Executors.newFixedThreadPool(2);
for (int i = 0; i < 3; i++) {
//同一实例,线程共享静态变量i
// executorService.execute(instance);
//不同实例,线程单独享有变量i,达不到同步目的
executorService.execute(new SynchronizedMethod());
/**
* 由于线程执行时间过短,在不同实例下,可能会得到类似于同步的结果。
*/
Thread.sleep(100);
} executorService.shutdown(); System.out.println(i); // }
}

code2

package com.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
* 同步代码块
* @author Administrator
*
*/
public class SynchronizedCodeBlock implements Runnable{ //静态共享变量 i
static int i = 0; @Override
public void run() {
//同步进来的对象
synchronized(this){ //SynchronizedCodeBlock.class
for (int j = 0; j < 100; j++) {
i++;
}
}
} public static void main(String[] args) throws InterruptedException {
SynchronizedCodeBlock instance = new SynchronizedCodeBlock();
ExecutorService executorService = Executors.newFixedThreadPool(2);
for (int i = 0; i < 3; i++) {
// executorService.execute(instance);
executorService.execute(new SynchronizedCodeBlock());
Thread.sleep(10);
} executorService.shutdown(); System.out.println(i); // }
}

wait与notify运用

wait():使一个线程处于等待状态,并且释放所持有的对象的lock。

sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。

notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。

notifyAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

code3

package com.test;

/**
* 多线程实现 生产者-消费者模式
* 关键点:wait和notifyAll或者notify时机的运用,确保先生产后消费
* @author Administrator
*
*/
public class Test
{
private static Integer count = 0; //数据仓库计数
private final Integer FULL = 5; //数据仓库最大存储量
private static String lock = "lock"; //锁标识 public static void main(String[] args)
{
Test t = new Test();
new Thread(t.new Producer()).start();
new Thread(t.new Consumer()).start();
new Thread(t.new Producer()).start();
new Thread(t.new Consumer()).start();
} //生产者
class Producer implements Runnable
{
@Override
public void run()
{
for (int i = 0; i < 5; i++)
{
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
synchronized (lock)
{
while (count == FULL)
{
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count++;
System.out.println(Thread.currentThread().getName() + "produce:: " + count);
//唤醒lock锁上的所有线程
lock.notifyAll();
}
}
}
} //消费者
class Consumer implements Runnable
{
@Override
public void run()
{
for (int i = 0; i < 5; i++)
{
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
synchronized (lock)
{
//如果首次消费者竞争得到锁,进入后等待
while (count == 0)
{
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
System.out.println(Thread.currentThread().getName()+ "consume:: " + count);
lock.notifyAll();
}
}
}
}
}

volatile实现线程同步

原理:volatile保证不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,新值对其他线程来说是立即可见的,并且禁止进行指令重排序。

注意:volatile不保证原子性,凡是不是原子性的操作,都不能保证可见性,也即不能保证同步

应用:

  1)对变量的写操作不依赖于当前值 类似 i++、i=j 等操作 不能对 i 用volatile。解决办法:类似操作增加 synchronized、Lock、AtomicInteger 保证原子性。

  2)该变量没有包含在具有其他变量的不变式中

  常用在多线程状态标志 flag、

ReentrantLock重入锁

重入锁:外层函数获取锁后,内层函数依然有获取该锁的代码,则重入锁无需再次获取锁,即可进入内层代码

code1

package com.lock;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock; /**
* 重入锁
* 外层函数获取锁后,内层函数依然有获取该锁的代码,则重入锁无需再次获取锁,即可进入内层代码
* ReentrantLock 和synchronized 都是 可重入锁
* @author Administrator
*
*/
public class ReentranLockTest implements Runnable{ public static ReentrantLock lock = new ReentrantLock();
public static int i = 0; @Override
public void run() {
for (int j = 0; j < 10; j++) {
lock.lock(); //加锁
try {
i++;
} finally {
lock.unlock(); //释放锁
}
}
} public static void main(String[] args) throws InterruptedException {
ReentranLockTest test = new ReentranLockTest(); ExecutorService executorService = Executors.newFixedThreadPool(2);
for (int i = 0; i < 2; i++) {
executorService.execute(test);
Thread.sleep(1000);
}
executorService.shutdown();
System.out.println(i); }
}

code2

package com.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; /**
* 可中断的重入锁
* 条件中断等待
* @author Administrator
*
*/
public class InterruptiblyLockTest { public static Lock lock = new ReentrantLock(); public void function(){
String tName = Thread.currentThread().getName();
try {
System.out.println(tName + "-开始获取锁......");
lock.lockInterruptibly();
System.out.println("获取到锁了......");
Thread.sleep(10000);
System.out.println("睡眠10秒后,开始干活......");
for (int i = 0; i < 5; i++) {
System.out.println(tName + ":" + i);
}
System.out.println("活干完了......");
} catch (Exception e) {
System.out.println(tName + "-我好像被人中断了!");
e.printStackTrace();
}finally {
lock.unlock();
System.out.println(tName + "-释放了锁");
}
} public static void main(String[] args) throws InterruptedException {
InterruptiblyLockTest test = new InterruptiblyLockTest();
//定义两个线程
Thread t0 = new Thread() {
public void run() {
test.function();
}
}; Thread t1 = new Thread() {
public void run() {
test.function();
}
}; String tName = Thread.currentThread().getName();
System.out.println(tName + "-启动t0");
t0.start();
System.out.println(tName + "-等5秒,再启动t1");
Thread.sleep(5000);
System.out.println(tName + "-启动t1");
t1.start();
//t0先占据了锁还在睡眠
System.out.println(tName + "-不等了,把t1中断掉!");
t1.interrupt(); //中断:只能中断处于等待锁的线程,不能中断已经获取锁的线程
}
}

code3

package com.lock;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock; /**
* 尝试型锁
* 拒绝阻塞
* @author Administrator
*
*/
public class TryLockTest { public ReentrantLock lock = new ReentrantLock(); /**
* tryLock
* 当前资源没有被占用,则tryLock获取锁
* 当前资源被当前锁占用,则tryLock返回true
* 当前资源被其他线程占用,则tryLock返回false
* @throws InterruptedException
*/
public void tryLockFunction() throws InterruptedException{
String tName = Thread.currentThread().getName();
if(lock.tryLock()){
try {
System.out.println(tName + "-获取到锁了");
Thread.sleep(3000);
System.out.println(tName + "工作了3秒钟......");
} finally {
lock.unlock();
System.out.println(tName + "-释放锁");
}
}else{
System.out.println(tName + "-无法获取到锁");
}
} /**
* tryLock(long timeout, TimeUnit unit)
* timeout时间内尝试请求锁,请求到了则返回true,可被中断
*
* @throws InterruptedException
*/
public void tryLockInterruptFunction() throws InterruptedException{
String tName = Thread.currentThread().getName();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd:hh:mm:ss");
System.out.println(tName + "-开始尝试获取锁,当前时间:"+format.format(new Date()));
if(lock.tryLock(3,TimeUnit.SECONDS)){
try {
System.out.println(tName + "-获取到锁了");
Thread.sleep(5000);
System.out.println(tName + "工作了3秒钟......");
} finally {
lock.unlock();
System.out.println(tName + "-释放锁");
}
}else{
System.out.println(tName + "-无法获取到锁");
System.out.println(tName + "-结束尝试获取锁,当前时间:"+format.format(new Date()));
}
} public static void main(String[] args) {
TryLockTest test = new TryLockTest();
new Thread("Lock-Thread1") {
public void run() {
try {
test.tryLockFunction();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
new Thread("Lock-Thread2") {
public void run() {
try {
test.tryLockFunction();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start(); //Lock-Thread1-获取到锁了
//Lock-Thread2-无法获取到锁
//Lock-Thread1工作了3秒钟......
//Lock-Thread1-释放锁 new Thread("LockInterrupt-Thread1") {
public void run() {
try {
test.tryLockInterruptFunction();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
new Thread("LockInterrupt-Thread2") {
public void run() {
try {
test.tryLockInterruptFunction();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start(); //LockInterrupt-Thread2-开始尝试获取锁,当前时间:2019-01-17:05:12:32
//LockInterrupt-Thread1-开始尝试获取锁,当前时间:2019-01-17:05:12:32
//LockInterrupt-Thread2-获取到锁了
//LockInterrupt-Thread1-无法获取到锁
//LockInterrupt-Thread1-结束尝试获取锁,当前时间:2019-01-17:05:12:35
//LockInterrupt-Thread2工作了3秒钟......
//LockInterrupt-Thread2-释放锁 }
}

code4

package com.lock;

import java.util.concurrent.locks.ReentrantLock;

/**
* 公平锁
* 按时间先后获取锁
* @author Administrator
*
*/
public class FairLockTest {
//创建一个公平锁
public static ReentrantLock lock = new ReentrantLock(true); public void fairLockFunction(){
while(true){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+"获取到锁......");
} finally {
lock.unlock();
}
}
} public static void main(String[] args) {
FairLockTest test = new FairLockTest();
Thread t1 = new Thread("线程1") {
public void run() {
test.fairLockFunction();
}
};
Thread t2 = new Thread("线程2") {
public void run() {
test.fairLockFunction();
}
};
t1.start();
t2.start();
} //线程1获取到锁......
//线程2获取到锁......
//线程1获取到锁......
//线程2获取到锁......
//线程1获取到锁......
//线程2获取到锁......
//线程1获取到锁......
//线程2获取到锁......
}

ThreadLocal创建线程间变量副本

参考博客:聊一聊Spring中的线程安全性

final实现volatile可见性

  通过final不可变性的特点,替代volatile的可见性。

参考博客:

https://blog.csdn.net/javazejian/article/details/72828483

https://www.cnblogs.com/duanxz/p/3709608.html?utm_source=tuicool&utm_medium=referral

http://www.cnblogs.com/duanxz/p/5066726.html

2).基于数据库

Java高并发之同步异步的更多相关文章

  1. Java高并发之锁优化

    本文主要讲并行优化的几种方式, 其结构如下: 锁优化 减少锁的持有时间 例如避免给整个方法加锁 public synchronized void syncMethod(){ othercode1(); ...

  2. java高并发之线程池

    Java高并发之线程池详解   线程池优势 在业务场景中, 如果一个对象创建销毁开销比较大, 那么此时建议池化对象进行管理. 例如线程, jdbc连接等等, 在高并发场景中, 如果可以复用之前销毁的对 ...

  3. java高并发之锁的使用以及原理浅析

    锁像synchronized同步块一样,是一种线程同步机制.让自Java 5开始,java.util.concurrent.locks包提供了另一种方式实现线程同步机制——Lock.那么问题来了既然都 ...

  4. Java高并发之线程基本操作

    结合上一篇同步异步,这篇理解线程操作. 1.新建线程.不止thread和runnable,Callable和Future了解一下 package com.thread; import java.tex ...

  5. Java高并发之设计模式

    本文主要讲解几种常见并行模式, 具体目录结构如下图. 单例 单例是最常见的一种设计模式, 一般用于全局对象管理, 比如xml配置读写之类的. 一般分为懒汉式, 饿汉式. 懒汉式: 方法上加synchr ...

  6. 1.6 JAVA高并发之线程池

    一.JAVA高级并发 1.5JDK之后引入高级并发特性,大多数的特性在java.util.concurrent 包中,是专门用于多线程发编程的,充分利用了现代多处理器和多核心系统的功能以编写大规模并发 ...

  7. Java 高并发之魂

    前置知识 了解Java基本语法 了解多线程基本知识 知识介绍 Synchronized简介:作用.地位.不控制并发的后果 两种用法:对象锁和类锁 多线程访问同步方法的7种情况:是否是static.Sy ...

  8. Java高并发之从零到放弃

    前言 本篇主要讲解如何去优化锁机制或者克服多线程因为锁可导致性能下降的问题 ThreadLocal线程变量 有这样一个场景,前面是一大桶水,10个人去喝水,为了保证线程安全,我们要在杯子上加锁导致大家 ...

  9. Java高并发之无锁与Atomic源码分析

    目录 CAS原理 AtomicInteger Unsafe AtomicReference AtomicStampedReference AtomicIntegerArray AtomicIntege ...

随机推荐

  1. SQL——行转列,列转行

    行转列,列转行是我们在开发过程中经常碰到的问题.行转列一般通过CASE WHEN 语句来实现,也可以通过 SQL SERVER 2005 新增的运算符PIVOT来实现.用传统的方法,比较好理解.层次清 ...

  2. MySQL如何查询当月数据

    自己写了一个比较简单效率又高的方法,分享给大家: WHERE addTime BETWEEN DATE_FORMAT(NOW(),'%Y-%m-01') AND NOW() 方法就是过滤 本月1日到当 ...

  3. 解决Pandoc wasn't found.pdflatex not found on PATH

    解决nbconvert failed: Pandoc wasn't found.解决nbconvert failed: pdflatex not found on PATH 问题1描述 500 : I ...

  4. Angular JS + Express JS入门搭建网站

    3月份开始,接到了新的任务,跟UI开发有关,用的是Angular JS,Express JS等技术.于是周末顺便学习下新技术. 组里产品UI架构如下: 其中前端,主要使用Angular JS框架,另外 ...

  5. Makedown语法说明

    Markdown 语法说明 (简体中文版) / (点击查看快速入门) 概述 宗旨 兼容 HTML 特殊字符自动转换 区块元素 段落和换行 标题 区块引用 列表 代码区块 分隔线 区段元素 链接 强调 ...

  6. 119. Pascal's Triangle II (Amazon) from leetcode

    Given a non-negative index k where k ≤ 33, return the kth index row of the Pascal's triangle. Note t ...

  7. javascript 时间格式化方法

    对jquery进行扩展的方法: //对时间格式化(jquery方法扩展) Date.prototype.Format = function (fmt) { //author: meizz var o ...

  8. Jenkins配置(Jenkins如何与maven项目进行连用)

    一同事,在问关于Jenkins搭建后,他是如何与我们项目进行连用的,如何通过Jenkins去编译我们的项目的,现在介绍下如何通过Jenkins持续编译我们的项目 配置过程 1.确定我们已经搭建好了Je ...

  9. 【BZOJ1453】[WC] Dface双面棋盘(LCT维护联通块个数)

    点此看题面 大致题意: 给你一个\(n*n\)的黑白棋盘,每次将一个格子翻转,分别求黑色连通块和白色连通块的个数. \(LCT\)动态维护图连通性 关于这一部分内容,可以参考这道例题:[BZOJ402 ...

  10. 20145238-荆玉茗 《Java程序设计》第四次实验报告

    20145238<Java程序设计>第四次实验报告 实验四 Android环境搭建 实验内容 1.搭建Android环境 2.运行Android 3.修改代码,能输出学号 实验步骤 搭建A ...