Java多线程JUC
1. volatile 关键字
多线程访问的时候,一个比较严重的问题就是内存不可见,其实在内存访问的时候每一个线程都有一个自己的缓冲区,每次在做修改的时候都是从主存取到数据,然后放到自己的缓冲区中,在做完修改之后放回主存。这样每一个线程之间的变量是不可见的。造成读到的数据可能始终就是错误的,因此有一个关键字可以使得这个共享变量称为透明的。就好像所有的操作就直接是在内存中操作一样,因为他一直不停的去同步主存的数据。
2.原子性
i++ 这个运算,其实在底层低用的就是临时变量的方式,这样的话虽然是一个表达式,但是在多线程的时候就会出现安全问题。
package atomic;
/**
* @Author: lwen
* @Date: Created in 2017/8/19 15:26
* @Description:
*/
class Test implements Runnable{
private volatile int i=0;
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i++);
}
}
public class Main {
public static void main(String[] args) {
Test t=new Test();
for (int i = 0; i < 10; i++) {
new Thread(t).start();
}
}
}
最后运行的结果,显然就是 volatile 这个关键字他只是让变量课件也就是都是在主存中操作,但是他没有互斥的作用简单来说没有锁来控制,他们都及时的从主存里面拿到了数据,但是他们拿到最新的数据了,正在运算的时候有人提交了结果,所以导致重复元素。最根本原因是 i++ 有三步操作,读,运算,写
1
3
2
0
4
7
6
5
8
8
Process finished with exit code 0
java 底层提供了一些原子性的变量,例如 AtomicInteger 这些东西他们既然是源自的首先就是可见的。这些东西的底层主要是使用了 CAS ( CompareAndSet ) 算法,CAS 算法主要是操作系统在硬件上提供的支持。
这个算法有三个重要的参数:第一个就是 V 就是运算前从内存读取的值 A 写入前从内存中读取的值 B 最终需要写入的值。在写入前做一次判断当且仅当 V == A 时才会写入 B 否则什么操作也不做。
3.ConcurrentHahsMap 安全的 HashMap
这是一个线程安全的 HashMap ,说道线程安全的 HahsMap 自然就有 HashTable 但是这个效率非常的低,主要就是因为他的封锁粒度太大,他锁的是整个 HashTable 也就是两个不相干的 HashTable 也是互斥访问的,在 jdk1.5 以后使用的就是 ConcurrentHahsMap 这个东西那个时候主要使用的锁分段机制,也就是在原来的基础上把 HashTable 分16段,每一段对应一个 HashMap 这样的话两个互不相干的 HashMap 是可以同步访问的。
4.CountDownLatch 闭锁
所谓的闭锁说白了就是该线程会等到洽谈所有线程的代码都运行结束了才开始运行,他的底层实现就是维护一个变量,这个变量就是当前存活的线程的数量,当他减成0了也就是其他的线程都运行完了,此时这个闭锁线程可以开始。例如说我们开十个线程做某一件事情,我们在主线程中统计这十个线程的总的运行时间。
package latch;
import com.sun.javafx.sg.prism.web.NGWebView;
import java.util.concurrent.CountDownLatch;
/**
* @Author: lwen
* @Date: Created in 2017/8/19 16:06
* @Description:
*/
class Latch implements Runnable{
private CountDownLatch latch;
public Latch(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
System.out.println(Thread.currentThread());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown(); //线程结束以后,需要给维护的变量减一
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
int start= (int) System.currentTimeMillis();
CountDownLatch downLatch= new CountDownLatch(10); //定义十个线程
Latch latch=new Latch(downLatch);
for (int i = 0; i < 10; i++) {
new Thread(latch).start();
}
downLatch.await(); //当线程没有完全结束的时候,主线程需要等待
int end= (int) System.currentTimeMillis();
System.out.println(end-start);
}
}
4.线程的第三种创建方式
一般我们创建线程我们使用的都是继承 Thread 类,或者实现 Runnable 接口,但是还是主要使用的是实现 Runnable 接口,但是注意这两个方式他们嗾使没有返回值的东西。也就是我么不能多线程没有办法返回一些结果。这里就出现了创建线程的第三种方式,也就是可以得到返回值的方式,就是使用 Callable 接口,这个接口的使用需要使用 FutureTask 类,而这个类实现了 Runnable 和 Future 接口,他的具体的使用方式和 Runnable 接口还是有一点不一样的。
package future;
import org.omg.PortableInterceptor.INACTIVE;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @Author: lwen
* @Date: Created in 2017/8/19 16:23
* @Description:
*/
class Future implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int sum=0;
for (int i = 0; i < 1000; i++) {
sum+=i;
}
return sum;
}
}
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Future future=new Future();
FutureTask<Integer> task=new FutureTask<Integer>(future);
new Thread(task).start();
System.out.println(task.get());;//获取最终的返回值,但是注意这个地方的这个方法其实就是一个闭锁,前面的那个县城没有执行完,这个地方是不会执行的
}
}
5.高级同步
解决同步问题总共就有三种方式,分别就是同步代码块,同步函数,和同步锁。
也就是手动的声明 lock 加锁,然后使用 unlock 释放锁
package lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author: lwen
* @Date: Created in 2017/8/19 16:33
* @Description:
*/
class Ticket implements Runnable{
private int ticket=100;
Lock lock=new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
try {
if (ticket != 0) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName() + ":" + ticket);
}
}finally {
lock.unlock(); //解锁必须放在finally里面保证能够执行到
}
}
}
}
public class Main {
public static void main(String[] args) {
Ticket t=new Ticket();
new Thread(t,"窗口一").start();
new Thread(t,"窗口二").start();
new Thread(t,"窗口三").start();
}
}
6.生产者和消费者的线程同步问题
package product;
import java.util.logging.Level;
/**
* @Author: lwen
* @Date: Created in 2017/8/19 16:57
* @Description:
*/
class Clerk {
private int production=0;
public synchronized void get() throws InterruptedException {
while (production>=1){ //notifyAll 可能会形成假唤醒问题,所以说必须要使用while一直判断而不能使用if
System.out.println("已满");
this.wait();
}
System.out.println(Thread.currentThread().getName()+":"+ ++production);
this.notifyAll();
}
public synchronized void sale() throws InterruptedException {
while (production<=0){
System.out.println("卖完");
this.wait();
}
System.out.println(Thread.currentThread().getName()+":"+ --production);
this.notifyAll();
}
}
class Product implements Runnable{
private Clerk clerk;
public Product(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
clerk.get();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable{
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
clerk.sale();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
Clerk clerk=new Clerk();
Product p=new Product(clerk);
Consumer consumer=new Consumer(clerk);
new Thread(p,"生产者1").start();
new Thread(p,"生产者2").start();
new Thread(consumer,"消费者1").start();
new Thread(consumer,"消费者2").start();
}
}
7.读写锁
排斥写写和读写同时进行
package readandwrite;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @Author: lwen
* @Date: Created in 2017/8/19 17:29
* @Description:
*/
class ReadWriteLockDemo {
private int number=0;
private ReadWriteLock lock=new ReentrantReadWriteLock();
public void read(){
lock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+":"+number);
}finally {
lock.readLock().unlock();
}
}
public void write(int number) throws InterruptedException {
lock.writeLock().lock();
Thread.sleep(20);
try {
this.number=number;
System.out.println("write");
}finally {
lock.writeLock().unlock();
}
}
}
public class Main {
public static void main(String[] args) {
ReadWriteLockDemo readWriteLockDemo=new ReadWriteLockDemo();
new Thread(new Runnable() {
@Override
public void run() {
try {
readWriteLockDemo.write(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
for (int i = 0; i < 20; i++) {
new Thread(new Runnable() {
@Override
public void run() {
readWriteLockDemo.read();
}
}).start();
}
}
}
4.线程池
package pool;
import java.util.concurrent.*;
/**
* @Author: lwen
* @Date: Created in 2017/8/19 17:56
* @Description:
*/
class Test implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
class Test1 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int sum=0;
for (int i = 0; i <= 100; i++) {
sum+=i;
}
return sum;
}
}
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//注意线程池里面装的都是线程,这些线程到时候再次使用的时候不需要再次创建,也就是代表操作系统不需要再次分配资源,分配pid等等
//而不是说里面装的是任务,这里仅仅就是线程而已
ExecutorService executor= Executors.newFixedThreadPool(10); //固定大小的线程池
ExecutorService executor1=Executors.newSingleThreadExecutor(); //一个线程的线程池
ExecutorService executor2=Executors.newCachedThreadPool(); //动态变化的线程池
for (int i = 0; i < 20; i++) {
executor.submit(new Test());
}
Future<Integer> future=executor1.submit(new Test1());
System.out.println(future.get());
executor.shutdown(); //只有当线程池关闭的时候 程序才会停止
executor1.shutdown();
executor2.shutdown();
}
}
8.线程调度:
线程调度可以决定在多久之后执行什么操作。
package schedule;
import java.util.Random;
import java.util.concurrent.*;
/**
* @Author: lwen
* @Date: Created in 2017/8/19 18:11
* @Description:
*/
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ScheduledExecutorService service= Executors.newScheduledThreadPool(5);
for (int i = 0; i < 5; i++) {
Future<Integer> future=service.schedule(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int random=new Random().nextInt(100);
System.out.println(Thread.currentThread().getName()+":"+random);
return random;
}
},1, TimeUnit.SECONDS);
System.out.println(future.get());
}
service.shutdown();
}
}
Java多线程JUC的更多相关文章
- java多线程----JUC集合”01之 框架
java集合的架构.主体内容包括Collection集合和Map类:而Collection集合又可以划分为List(队列)和Set(集合). 1. List的实现类主要有: LinkedList, A ...
- Java多线程—JUC原子类
根据修改的数据类型,可以将JUC包中的原子操作类可以分为4类. 1. 基本类型: AtomicInteger, AtomicLong, AtomicBoolean ;2. 数组类型: AtomicIn ...
- Java多线程系列--“JUC锁”03之 公平锁(一)
概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...
- Java多线程系列--“JUC锁”04之 公平锁(二)
概要 前面一章,我们学习了“公平锁”获取锁的详细流程:这里,我们再来看看“公平锁”释放锁的过程.内容包括:参考代码释放公平锁(基于JDK1.7.0_40) “公平锁”的获取过程请参考“Java多线程系 ...
- Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例
概要 本章介绍JUC包中的CyclicBarrier锁.内容包括:CyclicBarrier简介CyclicBarrier数据结构CyclicBarrier源码分析(基于JDK1.7.0_40)Cyc ...
- Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock
本章对ReentrantLock包进行基本介绍,这一章主要对ReentrantLock进行概括性的介绍,内容包括:ReentrantLock介绍ReentrantLock函数列表ReentrantLo ...
- Java多线程系列--“JUC锁”01之 框架
本章,我们介绍锁的架构:后面的章节将会对它们逐个进行分析介绍.目录如下:01. Java多线程系列--“JUC锁”01之 框架02. Java多线程系列--“JUC锁”02之 互斥锁Reentrant ...
- java多线程系类:JUC线程池:03之线程池原理(二)(转)
概要 在前面一章"Java多线程系列--"JUC线程池"02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包 ...
- java多线程系类:JUC线程池:02之线程池原理(一)
在上一章"Java多线程系列--"JUC线程池"01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我 ...
随机推荐
- ubuntu追加磁盘空间
在用wubi安装的时候,按默认的是20G空间,明显不够用,从Windows上追加空间 首先用win7自带的磁盘分区工具,从任意一个空余空间较多的磁盘划出一块新分区(无损数据)(如NTFS),作为ubu ...
- caffe︱深度学习参数调优杂记+caffe训练时的问题+dropout/batch Normalization
一.深度学习中常用的调节参数 本节为笔者上课笔记(CDA深度学习实战课程第一期) 1.学习率 步长的选择:你走的距离长短,越短当然不会错过,但是耗时间.步长的选择比较麻烦.步长越小,越容易得到局部最优 ...
- Nginx HTTP模块指令
alias 指令 该指令用于在url和系统路径之间的映射. location /a/{ alias /b/; } error_page 定义错误页面 error_page 404 /404.html; ...
- VxWorks下USB驱动总结1
1.USB设备 物理特征:4条电缆,电源线.地线.数据线.脉冲线; 速 度:低速1.5Mbps,全速12Mbps,高速480Mbps; 规范版本:1998年USB1.1,2000年USB2.0; 连 ...
- 版本控制工具--svn和git的使用(一) -----版本控制的好处以及分类
版本控制工具 版本控制VCS(Version Control Systems)是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统.这个系统可以自动帮我们备份文件的每一次更改,并且可以 ...
- class-k近邻算法kNN
1 k近邻算法2 模型2.1 距离测量2.2 k值选择2.3 分类决策规则3 kNN的实现--kd树3.1 构造kd树3.2 kd树搜索 1 k近邻算法 k nearest neighbor,k-NN ...
- 彻底禁用Chrome的“请停用以开发者模式运行的扩展程序”提示
前言 作为一个前端程序员,难免会有一些专属自己的小扩展,没必要每一个都发到Chrome应用商店去,虽然可以勾选"开发者模式"来运行本地插件,但是每次启动都会有一个烦人的" ...
- Qtree3题解(树链剖分(伪)+线段树+set)
外话:最近洛谷加了好多好题啊...原题入口 这题好像是SPOJ的题,挺不错的.看没有题解还是来一篇... 题意: 很明显吧.. 题解: 我的做法十分的暴力:树链剖分(伪)+线段树+\(set\)... ...
- emacs配置
原配置 (global-set-key [f9] 'compile-file) (global-set-key [f10] 'gud-gdb) (global-set-key (kbd "C ...
- 对网易云音乐参数(params,encSecKey)的分析
我们如果对网易云音乐进行爬虫的话,我们会发现,提交的参数是(params,encSecKey),然而这两个参数是一串很长的东西 我们要对网易云进行爬虫,那么就一定要将这两个参数弄明白,然后才可以进行爬 ...