java下载basic_java-basic

java-basic 基础

Java并发包中Lock的实现原理

Lock 的简介及使用

Lock是java 1.5中引入的线程同步工具,它主要用于多线程下共享资源的控制。本质上Lock仅仅是一个接口(位于源码包中的java\util\concurrent\locks中),它包含以下方法

//尝试获取锁,获取成功则返回,否则阻塞当前线程

void lock();

//尝试获取锁,线程在成功获取锁之前被中断,则放弃获取锁,抛出异常

void lockInterruptibly() throws InterruptedException;

//尝试获取锁,获取锁成功则返回true,否则返回false

boolean tryLock();

//尝试获取锁,若在规定时间内获取到锁,则返回true,否则返回false,未获取锁之前被中断,则抛出异常

boolean tryLock(long time, TimeUnit unit)

throws InterruptedException;

//释放锁

void unlock();

//返回当前锁的条件变量,通过条件变量可以实现类似notify和wait的功能,一个锁可以有多个条件变量

Condition newCondition();

Lock有三个实现类,一个是ReentrantLock,另两个是ReentrantReadWriteLock类中的两个静态内部类ReadLock和WriteLock。

使用方法:多线程下访问(互斥)共享资源时, 访问前加锁,访问结束以后解锁,解锁的操作推荐放入finally块中。

Lock l = ...; //根据不同的实现Lock接口类的构造函数得到一个锁对象

l.lock(); //获取锁位于try块的外面

try {

// access the resource protected by this lock

} finally {

l.unlock();

}

注意,加锁位于对资源访问的try块的外部,特别是使用lockInterruptibly方法加锁时就必须要这样做,这为了防止线程在获取锁时被中断,这时就不必(也不能)释放锁。

try {

l.lockInterruptibly();//获取锁失败时不会执行finally块中的unlock语句

try{

// access the resource protected by this lock

}finally{

l.unlock();

}

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

实现Lock接口的基本思想

需要实现锁的功能,两个必备元素,一个是表示(锁)状态的变量(我们假设0表示没有线程获取锁,1表示已有线程占有锁),另一个是队列,队列中的节点表示因未能获取锁而阻塞的线程。为了解决多核处理器下多线程缓存不一致的问题,表示状态的变量必须声明为voaltile类型,并且对表示状态的变量和队列的某些操作要保证原子性和可见性。原子性和可见性的操作主要通过Atomic包中的方法实现。

线程获取锁的大致过程(这里没有考虑可重入和获取锁过程被中断或超时的情况)

1. 读取表示锁状态的变量

2. 如果表示状态的变量的值为0,那么当前线程尝试将变量值设置为1(通过CAS操作完成),当多个线程同时将表示状态的变量值由0设置成1时,仅一个线程能成功,其

它线程都会失败

2.1 若成功,表示获取了锁,

2.1.1 如果该线程(或者说节点)已位于在队列中,则将其出列(并将下一个节点则变成了队列的头节点)

2.1.2 如果该线程未入列,则不用对队列进行维护

然后当前线程从lock方法中返回,对共享资源进行访问。

2.2 若失败,则当前线程将自身放入等待(锁的)队列中并阻塞自身,此时线程一直被阻塞在lock方法中,没有从该方法中返回(被唤醒后仍然在lock方法中,并从下一条语句继续执行,这里又会回到第1步重新开始)。

3. 如果表示状态的变量的值为1,那么将当前线程放入等待队列中,然后将自身阻塞(被唤醒后仍然在lock方法中,并从下一条语句继续执行,这里又会回到第1步重新开始)

注意: 唤醒并不表示线程能立刻运行,而是表示线程处于就绪状态,仅仅是可以运行而已

线程释放锁的大致过程

1. 释放锁的线程将状态变量的值从1设置为0,并唤醒等待(锁)队列中的队首节点,释放锁的线程从就从unlock方法中返回,继续执行线程后面的代码

2. 被唤醒的线程(队列中的队首节点)和可能和未进入队列并且准备获取的线程竞争获取锁,重复获取锁的过程

注意:可能有多个线程同时竞争去获取锁,但是一次只能有一个线程去释放锁,队列中的节点都需要它的前一个节点将其唤醒,例如有队列A

公平锁和非公平锁

锁可以分为公平锁和不公平锁,重入锁和非重入锁(关于重入锁的介绍会在ReentrantLock源代码分析中介绍),以上过程实际上是非公平锁的获取和释放过程。

公平锁严格按照先来后到的顺去获取锁,而非公平锁允许插队获取锁。

公平锁获取锁的过程上有些不同,在使用公平锁时,某线程想要获取锁,不仅需要判断当前表示状态的变量的值是否为0,还要判断队列里是否还有其他线程,若队列中还有线程则说明当前线程需要排队,进行入列操作,并将自身阻塞;若队列为空,才能尝试去获取锁。而对于非公平锁,当表示状态的变量的值是为0,就可以尝试获取锁,不必理会队列是否为空,这样就实现了插队获取锁的特点。通常来说非公平锁的吞吐率比公平锁要高,我们一般常用非公平锁。

这里需要解释一点,什么情况下才会出现,表示锁的状态的变量的值是为0而且队列中仍有其它线程等待获取锁的情况。

假设有三个线程A、B、C。A线程为正在运行的线程并持有锁,队列中有一个C线程,位于队首。现在A线程要释放锁,具体执行的过程操作可分为两步:

1. 将表示锁状态的变量值由1变为0,

2. C线程被唤醒,这里要明确两点:

(1)C线程被唤醒并不代表C线程开始执行,C线程此时是处于就绪状态,要等待操作系统的调度

(2)C线程目前还并未出列,C线程要进入运行状态,并且通过竞争获取到锁以后才会出列。

如果C线程此时还没有进入运行态,同时未在队列中的B线程进行获取锁的操作,B就会发现虽然当前没有线程持有锁,但是队列不为空(C线程仍然位于队列中),要满足先来后到的特点(B在C之后执行获取锁的操作),B线程就不能去尝试获取锁,而是进行入列操作。

实现Condition接口的基本思想

Condition 本质是一个接口,它包含如下方法

// 让线程进入等通知待状态

void await() throws InterruptedException;

void awaitUninterruptibly();

//让线程进入等待通知状态,超时结束等待状态,并抛出异常

long awaitNanos(long nanosTimeout) throws InterruptedException;

boolean await(long time, TimeUnit unit) throws InterruptedException;

boolean awaitUntil(Date deadline) throws InterruptedException;

//将条件队列中的一个线程,从等待通知状态转换为等待锁状态

void signal();

//将条件队列中的所有线程,从等待通知阻塞状态转换为等待锁阻塞状态

void signalAll();

一个Condition实例的内部实际上维护了一个队列,队列中的节点表示由于(某些条件不满足而)线程自身调用await方法阻塞的线程。Condition接口中有两个重要的方法,即 await方法和 signal方法。线程调用这个方法之前该线程必须已经获取了Condition实例所依附的锁。这样的原因有两个,(1)对于await方法,它内部会执行释放锁的操作,所以使用前必须获取锁。(2)对于signal方法,是为了避免多个线程同时调用同一个Condition实例的singal方法时引起的(队列)出列竞争。下面是这两个方法的执行流程。

await方法:

1. 入列到条件队列(注意这里不是等待锁的队列)

2. 释放锁

3. 阻塞自身线程

------------被唤醒后执行-------------

4. 尝试去获取锁(执行到这里时线程已不在条件队列中,而是位于等待(锁的)队列中,参见signal方法)

4.1 成功,从await方法中返回,执行线程后面的代码

4.2 失败,阻塞自己(等待前一个节点释放锁时将它唤醒)

注意:await方法时自身线程调用的,线程在await方法中阻塞,并没有从await方法中返回,当唤醒后继续执行await方法中后面的代码(也就是获取锁的代码)。可以看出await方法释放了锁,又尝试获得锁。当获取锁不成功的时候当前线程仍然会阻塞到await方法中,等待前一个节点释放锁后再将其唤醒。

signal方法:

1. 将条件队列的队首节点取出,放入等待锁队列的队尾

2. 唤醒该节点对应的线程

注意:signal是由其它线程调用

condition

Lock和Condition的使用例程

下面这个例子,就是利用lock和condition实现B线程先打印一句信息后,然后A线程打印两句信息(不能中断),交替十次后结束

public class ConditionDemo {

volatile int key = 0;

Lock l = new ReentrantLock();

Condition c = l.newCondition();

public static void main(String[] args){

ConditionDemo demo = new ConditionDemo();

new Thread(demo.new A()).start();

new Thread(demo.new B()).start();

}

class A implements Runnable{

@Override

public void run() {

int i = 10;

while(i > 0){

l.lock();

try{

if(key == 1){

System.out.println("A is Running");

System.out.println("A is Running");

i--;

key = 0;

c.signal();

}else{

c.awaitUninterruptibly();

}

}

finally{

l.unlock();

}

}

}

}

class B implements Runnable{

@Override

public void run() {

int i = 10;

while(i > 0){

l.lock();

try{

if(key == 0){

System.out.println("B is Running");

i--;

key = 1;

c.signal();

}else{

c.awaitUninterruptibly();

}

}

finally{

l.unlock();

}

}

}

}

}

Lock与synchronized的区别

1. Lock的加锁和解锁都是由java代码配合native方法(调用操作系统的相关方法)实现的,而synchronize的加锁和解锁的过程是由JVM管理的

2. 当一个线程使用synchronize获取锁时,若锁被其他线程占用着,那么当前只能被阻塞,直到成功获取锁。而Lock则提供超时锁和可中断等更加灵活的方式,在未能获取锁的 条件下提供一种退出的机制。

3. 一个锁内部可以有多个Condition实例,即有多路条件队列,而synchronize只有一路条件队列;同样Condition也提供灵活的阻塞方式,在未获得通知之前可以通过中断线程以 及设置等待时限等方式退出条件队列。

4. synchronize对线程的同步仅提供独占模式,而Lock即可以提供独占模式,也可以提供共享模式

6、CyclicBarrier易错点:

线程数 >= CyclicBarrier数目

lock

1、操作系统重排序:

cpu主频越来越高,但是没有读取缓存都会出现cache wait。重排序能够一次得到多个cache。

2、ReentrantLock重入锁

公平锁:java.util.concurrent.locks.ReentrantLock.FairSync(队列中)

1、不会CAS status 插队。

2、如果AQS队列中有元素,直接放入AQS队列中。否则,直接执行。

3、放入后自旋锁检测是否可以执行。

非公平锁:java.util.concurrent.locks.ReentrantLock.NonfairSync (先CAS、再队列)

1、AQS CAS 尝试获取用户的当先是否status忙碌。

2、如果AQS队列中有元素,直接放入AQS队列中。否则,直接执行。

3、放入后自旋锁检测是否可以执行。

3、指令重排序

在执行程序时,为了提高性能,编译器和处理器会对指令做重排序。但是,JMM确保在不同的编译器和不同的处理器平台之上,通过插入特定类型的Memory Barrier来禁止特定类型的编译器重排序和处理器重排序,为上层提供一致的内存可见性保证。

1、编译器优化重排序:编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。

2、指令级并行的重排序:如果不存l在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。

3、内存系统的重排序:处理器使用缓存和读写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

4、Arraylist:

1、无负载因子

2、新的数组长度: java.util.ArrayList.grow() int newCapacity = oldCapacity + (oldCapacity >> 1); 老len + 老len / 2

3、默认的数组长度为 10

kafka 使用:

kafka的安装:

1、下载安装包 kafka_2.11-1.0.0.tgz

2、配置 kafka 安装目录下的conf/server.properties 文件。

############################# Server Basics #############################

# The id of the broker. This must be set to a unique integer for each broker.

# 设置kafka实例当前的broker值。

broker.id=0

############################# Socket Server Settings #############################

# 设置、打开kafka远程调用的监控

listeners=PLAINTEXT://:9092

# 设置远程调用接口和自己的ip信息。

port=9092

host.name=192.168.61.128

# The number of threads that the server uses for receiving requests from the network and sending responses to the network

num.network.threads=3

# The number of threads that the server uses for processing requests, which may include disk I/O

num.io.threads=8

# The send buffer (SO_SNDBUF) used by the socket server

socket.send.buffer.bytes=102400

# The receive buffer (SO_RCVBUF) used by the socket server

socket.receive.buffer.bytes=102400

# The maximum size of a request that the socket server will accept (protection against OOM)

socket.request.max.bytes=104857600

············

启动kafka实例:

./kafka-server-start.sh ../config/server.properties 启动并设置broker信息。

java client:

引入依赖

org.apache.kafka

kafka-clients

0.10.1.0

org.apache.kafka

kafka_2.11

0.10.1.0

productor:

package org.zjt.kafka;

import org.apache.kafka.clients.producer.Callback;

import org.apache.kafka.clients.producer.KafkaProducer;

import org.apache.kafka.clients.producer.ProducerRecord;

import org.apache.kafka.clients.producer.RecordMetadata;

import java.util.Properties;

import java.util.concurrent.ExecutionException;

public class Producer extends Thread {

private final KafkaProducer producer;

private final String topic;

private final Boolean isAsync;

public Producer(String topic, Boolean isAsync) {

Properties props = new Properties();

props.put("bootstrap.servers", "192.168.61.128:9092");

props.put("client.id", "DemoProducer");

props.put("key.serializer", "org.apache.kafka.common.serialization.IntegerSerializer");

props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

producer = new KafkaProducer<>(props);

this.topic = topic;

this.isAsync = isAsync;

}

public void run() {

int messageNo = 1;

while (true) {

String messageStr = "Message_" + messageNo;

long startTime = System.currentTimeMillis();

if (isAsync) { // Send asynchronously

producer.send(new ProducerRecord<>(topic,

messageNo,

messageStr), new DemoCallBack(startTime, messageNo, messageStr));

} else { // Send synchronously

try {

producer.send(new ProducerRecord<>(topic,

messageNo,

messageStr)).get();

System.out.println("Sent message: (" + messageNo + ", " + messageStr + ")");

} catch (InterruptedException | ExecutionException e) {

e.printStackTrace();

}

}

++messageNo;

}

}

}

class DemoCallBack implements Callback {

private final long startTime;

private final int key;

private final String message;

public DemoCallBack(long startTime, int key, String message) {

this.startTime = startTime;

this.key = key;

this.message = message;

}

public void onCompletion(RecordMetadata metadata, Exception exception) {

long elapsedTime = System.currentTimeMillis() - startTime;

if (metadata != null) {

System.out.println(

"message(" + key + ", " + message + ") sent to partition(" + metadata.partition() +

"), " +

"offset(" + metadata.offset() + ") in " + elapsedTime + " ms");

} else {

exception.printStackTrace();

}

}

}

consumer:

package org.zjt.kafka;

import kafka.utils.ShutdownableThread;

import org.apache.kafka.clients.consumer.ConsumerConfig;

import org.apache.kafka.clients.consumer.ConsumerRecord;

import org.apache.kafka.clients.consumer.ConsumerRecords;

import org.apache.kafka.clients.consumer.KafkaConsumer;

import java.util.Collections;

import java.util.Properties;

public class Consumer extends ShutdownableThread {

private final KafkaConsumer consumer;

private final String topic;

public Consumer(String topic) {

super("KafkaConsumerExample", false);

Properties props = new Properties();

props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.61.128:9092");

props.put(ConsumerConfig.GROUP_ID_CONFIG, "DemoConsumer");

props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true");

props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");

props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000");

props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.IntegerDeserializer");

props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");

consumer = new KafkaConsumer<>(props);

this.topic = topic;

}

@Override

public void doWork() {

consumer.subscribe(Collections.singletonList(this.topic));

ConsumerRecords records = consumer.poll(1000);

for (ConsumerRecord record : records) {

System.out.println("Received message: (" + record.key() + ", " + record.value() + ") at offset " + record.offset());

}

}

@Override

public String name() {

return null;

}

@Override

public boolean isInterruptible() {

return false;

}

}

启动demo:

package org.zjt.kafka;

/**

* @author juntao.zhang

* @version V1.0

* @Description: TODO

* @Package org.zjt.kafka

* @date 2018/2/12 14:33

*/

public class KafkaConsumerProducerDemo {

public static void main(String[] args) {

boolean isAsync = args.length == 0 || !args[0].trim().equalsIgnoreCase("sync");

Producer producerThread = new Producer(KafkaProperties.TOPIC, isAsync);

producerThread.start();

Consumer consumerThread = new Consumer(KafkaProperties.TOPIC);

consumerThread.start();

}

}

HashMap的cpu100%问题:

通过上述的分析,在这里总结下HashMap在1.7和1.8之间的变化:

1.7采用数组+单链表,1.8在单链表超过一定长度后改成红黑树存储

1.7扩容时需要重新计算哈希值和索引位置,1.8并不重新计算哈希值,巧妙地采用和扩容后容量进行&操作来计算新的索引位置。

1.7插入元素到单链表中采用头插入法,1.8采用的是尾插入法。

1、java内存管理

1、对象内存对齐 (车位原理)、

2、属性对齐 (java 属性对齐,long、int 和 缓存行)、

3、缓存行隔离(每个字段都在一个缓存行中,不跨缓存行)、

4、字段重排(两个int 会和 一个long 对齐)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值