import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class 读写锁 {
public static void main(String[] args) {
ReadWriteLockTest rwlt = new ReadWriteLockTest();
for (int i = 0; i < 100; i++) {
new Thread(rwlt::getNum, "R" + i).start();
}
new Thread(() -> {
for (int i = 0; i < 1000; i++) {
rwlt.setNum(i);
}
}, "W").start();
}
}
class ReadWriteLockTest {
private int num;
ReadWriteLock rwl = new ReentrantReadWriteLock();
Lock readLock = rwl.readLock();
Lock wl = rwl.writeLock();
public void getNum() {
readLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " " + num);
} finally {
readLock.unlock();
}
}
public void setNum(int num) {
wl.lock();
try {
this.num = num;
} finally {
wl.unlock();
}
}
}
题目:判断打印的 “one” or “two” ?
class TestThread8Monitor {
public static void main(String[] args) {
Number number = new Number();
Number number2 = new Number();
new Thread(new Runnable() {
@Override
public void run() {
number.getOne();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
// number.getTwo();
number2.getTwo();
}
}).start();
/*new Thread(new Runnable() {
@Override
public void run() {
number.getThree();
}
}).start();*/
}
}
class Number {
public static synchronized void getOne() {//Number.class
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
System.out.println("one");
}
public synchronized void getTwo() {//this
System.out.println("two");
}
public void getThree() {
System.out.println("three");
}
}
①非静态方法的锁默认为 this, 静态方法的锁为 对应的 Class 实例
②某一个时刻内,只能有一个线程持有锁,无论几个方法。
class Demo1 {
String name;
int num;
public static void main(String[] args) {
Demo1 demo1 = new Demo1();
Object o = new Object(); //锁对象
//分别为生产者和消费者开启两线程
Producer producer = new Producer(demo1,o);
Consumer consumer = new Consumer(demo1,o);
new Thread(consumer::con).start();
new Thread(producer::pro).start();
}
}
class Producer {
Demo1 d1;
Object o;
public Producer(Demo1 d1 , Object o) {
this.o=o;
this.d1 = d1;
}
void pro() {
while (true) {
synchronized (o) {
//使用循环判断,解决虚假唤醒问题。
while (d1.num > 10) {
System.out.println("库存满了");
try {
o.wait();
} catch (InterruptedException e) {
}
}
d1.name = "commodity";
d1.num++;
System.out.println(d1.name+d1.num);
o.notify();
}
}
}
}
class Consumer {
Demo1 d1;
Object o;
public Consumer(Demo1 d1 , Object o) {
this.o=o;
this.d1 = d1;
}
void con() {
while (true) {
synchronized (o) {
while (d1.num <= 0) {
try {
System.out.println("没库存了");
o.wait();
} catch (InterruptedException e) {
}
}
d1.num--;
d1.name="Consumer";
System.out.println(d1.name+d1.num);
o.notify();
}
}
}
}
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Demo1 {
String name;
int num;
public static void main(String[] args) {
Demo1 demo1 = new Demo1();
PCer producer = new PCer(demo1);
new Thread(producer::pro).start();
new Thread(producer::con).start();
}
}
class PCer {
Demo1 d1;
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
public PCer(Demo1 d1) {
this.d1 = d1;
}
void pro() {
while (true) {
lock.lock();
try {
while (d1.num >= 10) {
System.out.println("库存满了");
try {
condition1.await();
} catch (InterruptedException e) {
}
}
d1.name = "commodity";
d1.num++;
System.out.println(d1.name + d1.num);
//唤醒对方
condition2.signal();
} finally {
lock.unlock();
}
}
}
void con() {
while (true) {
lock.lock();
try {
while (d1.num <= 0) {
try {
System.out.println("没库存了");
condition2.await();
} catch (InterruptedException e) {
}
}
d1.name = "Consumer";
System.out.println(d1.name + d1.num);
d1.num--;
//唤醒对方
condition1.signal();
} finally {
lock.unlock();
}
}
}
}
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class 多线程控制 {
public static void main(String[] args) {
DemoCtrL dc = new DemoCtrL();
Thread thread = new Thread(()-> {
for (int i = 0; i < 10; i++) {
dc.methA();
}
});
thread.setName("A");
thread.start();
Thread thread1 = new Thread(()-> {
for (int i = 0; i < 10; i++) {
dc.methB();
}
});
thread1.setName("B");
thread1.start();
Thread thread2 = new Thread(()-> {
for (int i = 0; i < 10; i++) {
dc.methC();
}
});
thread2.setName("C");
thread2.start();
}
}
class DemoCtrL{
//控制线程标记
private int num = 1;
Lock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();
void methA(){
lock.lock();
try {
if (num != 1) {
try {
conditionA.await();
} catch (InterruptedException e) {
}
}
System.out.print(Thread.currentThread().getName());
num=2;
conditionB.signal();
}finally {
lock.unlock();
}
}
void methB(){
lock.lock();
try {
if (num != 2) {
try {
conditionB.await();
} catch (InterruptedException e) {
}
}
System.out.print(Thread.currentThread().getName());
num=3;
conditionC.signal();
}finally {
lock.unlock();
}
}
void methC(){
lock.lock();
try {
if (num != 3) {
try {
conditionC.await();
} catch (InterruptedException e) {
}
}
System.out.print(Thread.currentThread().getName());
num=1;
conditionA.signal();
}finally {
lock.unlock();
}
}
}
之前已经学习创建线程的两种方式,
import java.util.concurrent.Callable;
public class CallableDemo implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return null;
}
}
class RunnableDemo implements Runnable {
@Override
public void run() {
}
}
上述代码可以发现,Callable接口从写的方法名为call,并可以有返回值和抛异常。
class Demo {
public static void main(String[] args) {
CallableDemo cd = new CallableDemo();
//用futureTask类来接收
FutureTask<Integer> ft = new FutureTask<>(cd);
new Thread(ft).start();
try {
//get获取返回值
// 注意,get方法是在主线程中,get方法是在FutureTask线程执行结束后才执行的
//因此我们可以得知,FutureTask可用于闭锁
Integer integer = ft.get();
System.out.println(integer);
} catch (InterruptedException | ExecutionException e) {
}
}
}
class Thread1 implements Runnable {
boolean flag = false;
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
flag = true;
}
}
public class Volatile {
public static void main(String[] args) {
Thread1 t1 = new Thread1();
new Thread(t1).start();
System.out.println(t1.flag);
while (true) {
if (t1.flag) {
System.out.println("true--------");
break;
}
}
}
}
出现死循环的原因是,每个线程都会开辟一块缓存内存用于操作共享数据,在堆共享数据操作完成后再返还给主存,在上面的程序中,t1线程执行中睡了1秒,这时主线程获取了执行权开始循环,并判断flag为假,而t1睡眠结束后修改了flag的值后,主线程获取的flag值是缓存中的flag值,所以一直循环,无法结束。
解决方法:volatile关键字修饰线程共享数据volatile boolean flag = false;
。被volatile修饰的变量,被操作时不会在缓存中,而是在主存中,这样就保证了线程间操作的可见性。
i++ 的原子性问题:i++ 的操作实际上分为三个步骤“读–改--写”
int i = 10;
i = i++; //10
int temp = i;
i = i + 1;
i = temp;
原子变量:在 java.util.concurrent.atomic 包下提供了一些原子变量。
/*
* 模拟 CAS 算法
*/
class TestCompareAndSwap {
public static void main(String[] args) {
final CompareAndSwap cas = new CompareAndSwap();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
int expectedValue = cas.get();
boolean b = cas.compareAndSet(expectedValue, (int) (Math.random() * 101));
System.out.println(b);
}
}).start();
}
}
}
class CompareAndSwap {
private int value;
//获取内存值
public synchronized int get() {
return value;
}
//比较
public synchronized int compareAndSwap(int expectedValue, int newValue) {
int oldValue = value;
if (oldValue == expectedValue) {
this.value = newValue;
}
return oldValue;
}
//设置
public synchronized boolean compareAndSet(int expectedValue, int newValue) {
return expectedValue == compareAndSwap(expectedValue, newValue);
}
}
import java.util.concurrent.CountDownLatch;
/**
* CountDownLatch(倒计时闩锁)
* 闭锁:在完成某些运算时,只有其他所有线程的运算全部完成,当前运算才继续执行
* 如:多个辅线程计算不同资源,每个辅线程会得到一个结果,再由一个主线程将多个辅线程的结果汇总。
*/
public class CountDownLatchDemo {
public static void main(String[] args) {
//初始线程闭锁为10个
CountDownLatch cdl = new CountDownLatch(10);
CDLTest cdlTest = new CDLTest(cdl);
long start = System.currentTimeMillis();
//开启是个线程
for (int i = 0; i < 10; i++) {
new Thread(cdlTest).start();
}
//如果线程没执行完,主线程挂起等待,其他线程执行完。
try {
cdl.await();
} catch (InterruptedException e) { }
//闭锁为0时计算这些线程耗费的时间。
long end = System.currentTimeMillis();
System.out.println(end-start);
}
}
class CDLTest implements Runnable {
private final CountDownLatch cd;
private int sum = 0;
public CDLTest(CountDownLatch cd) {
this.cd = cd;
}
@Override
public void run() {
synchronized (this) {
try {
for (int i = 0; i < 50000; i++) {
if (i % 10000 == 0) {
try { Thread.sleep(10); } catch (InterruptedException e) { }
}
sum+=i;
}
System.out.println(Thread.currentThread().getName()+" "+sum);
}finally {//必须执行的程序用finally包裹
sum=0;
//线程计数器,执行完一个线程减减一个线程
cd.countDown();
}
}
}
}
线程池:提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提高了响应的速度。
线程池的体系结构:
java.util.concurrent.Executor : 负责线程的使用与调度的根接口
|–**ExecutorService 子接口: 线程池的主要接口
|–ThreadPoolExecutor 线程池的实现类
|–ScheduledExecutorService 子接口:负责线程的调度
|–ScheduledThreadPoolExecutor :继承 ThreadPoolExecutor, 实现 ScheduledExecutorService
返回值 | 名称 | 说明 |
---|---|---|
ExecutorService | newFixedThreadPool() | 创建固定大小的线程池 |
ExecutorService | newCachedThreadPool() | 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。 |
ExecutorService | newSingleThreadExecutor() | 创建单个线程池。线程池中只有一个线程 |
ScheduledExecutorService | newScheduledThreadPool() | 创建固定大小的线程,可以延迟或定时的执行任务。 |
官方文档说明:强烈建议程序员使用较为方便的 Executors 工厂方法 Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)(固定大小线程池)和 Executors.newSingleThreadExecutor()(单个后台线程),它们均为大多数使用场景预定义了设置。
import org.junit.jupiter.api.Test;
import java.util.Random;
import java.util.concurrent.*;
/**
* @author RealC
*/
public class 线程池练习 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
executorService.submit(() -> {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
});
executorService.shutdown();
}
/**
* ScheduledExecutorService | newScheduledThreadPool()|
* 创建固定大小的线程,可以延迟或定时的执行任务。
*
* @throws ExecutionException
* @throws InterruptedException
*/
@Test
public void test1() throws ExecutionException, InterruptedException {
//创建线程池,
ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
//分配线程任务
for (int i = 0; i < 10; i++) {
Future<Integer> re = pool.schedule(() -> {
int x = new Random().nextInt(10);
System.out.println(Thread.currentThread().getName() + "::" + x);
return x;
}, 3, TimeUnit.SECONDS);
System.out.println(re.get());
}
pool.shutdown();
}
}
ForkJoinPool的底层是工作窃取模式,其作用是:由于对多个任务分解并行处理,如果出现有些线程执行快,有些线程执行慢,在最后合并的时候,快的线程需要等待慢的线程,造成性能下降的问题。因此,执行快的线程会窃取执行慢的线程队列的任务,加入到自己的线程中来,从而提高性能效率。
其他官方解释:相对于一般的线程池实现, fork/join框架的优势体现在对其中包含的任务的处理方式上.在一般的线程池中, 如果一个线程正在执行的任务由于某些原因无法继续运行, 那么该线程会处于等待状态。 而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行。 那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程的等待时间, 提高了性能。
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.stream.LongStream;
class TestForkJoinPool {
public static void main(String[] args) {
Instant start = Instant.now();
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinSumCalculate(0L, 50000000000L);
Long sum = pool.invoke(task);
System.out.println(sum);
Instant end = Instant.now();
System.out.println("耗费时间为:" + Duration.between(start, end).toMillis());//166-1996-10590
}
@Test
public void test1() {
Instant start = Instant.now();
long sum = 0L;
for (long i = 0L; i <= 50000000000L; i++) {
sum += i;
}
System.out.println(sum);
Instant end = Instant.now();
System.out.println("耗费时间为:" + Duration.between(start, end).toMillis());//35-3142-15704
}
//java8 新特性
@Test
public void test2() {
Instant start = Instant.now();
Long sum = LongStream.rangeClosed(0L, 50000000000L)
.parallel()
.reduce(0L, Long::sum);
System.out.println(sum);
Instant end = Instant.now();
System.out.println("耗费时间为:" + Duration.between(start, end).toMillis());//1536-8118
}
}
class ForkJoinSumCalculate extends RecursiveTask<Long> {
private long start;
private long end;
//临界值
private static final long THURSHOLD = 10000L;
public ForkJoinSumCalculate(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
long length = end - start;
if (length <= THURSHOLD) {
long sum = 0L;
for (long i = start; i <= end; i++) {
sum += i;
}
return sum;
} else {
long middle = (start + end) / 2;
ForkJoinSumCalculate left = new ForkJoinSumCalculate(start, middle);
left.fork(); //进行拆分,同时压入线程队列
ForkJoinSumCalculate right = new ForkJoinSumCalculate(middle + 1, end);
right.fork(); //
return left.join() + right.join();
}
}
}