Java基础——多线程
7年加工作经验的程序员,从大厂跳槽出来,遭遇了什么?
Java基本-多线程
多个线程一同做统一件事变,压缩时刻,提拔效力
进步资本应用率
加速程序相应,提拔用户体验
竖立线程
1. 继承Thread类
- 步骤
- 继承Thread类,重写run要领
- 挪用的时刻,直接new一个对象,然后调start()要领启动线程
- 特性
- 由因而继承体式格局,所以不发起运用,由于Java是单继承的,不够天真
- Thread类实质也是完成Runnable接口(public class Thread implements Runnable)
2. 完成Runnable接口
- 步骤
- 完成Runnable接口,重写run()要领
- 竖立Runnable完成类的实例,并用这个实例作为Thread的target来竖立Thread对象
- 挪用Thread类实例对象的start()要领启动线程
- 特性
- 只是完成,保留了继承的其他类的才能
- 假如须要接见当前线程,必需运用Thread.currentThread()要领
3. 完成 Callable接口
- 步骤
- 完成Callable接口,重写call()要领
- 竖立Callable完成类的实例,运用FutureTask类来包装Callable对象
- 并用FutureTask实例作为Thread的target来竖立Thread对象
- 挪用Thread类实例对象的start()要领启动线程
- 挪用FutureTask类实例对象的get()要领猎取异步返回值
- 特性
- call要领可以抛出非常
- 只是完成,保留了继承的其他类的才能
- 假如须要接见当前线程,必需运用Thread.currentThread()要领
- 经由过程FutureTask对象可以相识使命实行情况,可取消使命的实行,还可猎取实行效果
4. 匿名内部类完成
- 申明
- 实质照样前面的要领,只是运用了匿名内部类来完成,简化代码
- Callable接口之所以把FutureTask类的实例化写出来,是由于须要经由过程task对象猎取返回值
参数通报
1. 经由过程组织要领通报数据
经由过程前面的进修,可以看到,不论何种竖立对象的体式格局,都须要新竖立实例,所以我们可以经由过程组织函数传入参数,并将传入的数据运用类变量保存起来
- 特性
- 在线程运转之前,数据就已传入了
- 运用组织参数,当参数较多时,运用不方便
- 差异参数前提须要差异的组织要领,使得组织要领较多
2. 经由过程变量和要领通报数据
在线程类内里定义一些列的变量,然后定义set要领,在新建实例以后,挪用set要领通报参数
- 特性
- 在参数较多时运用方便,按需通报参数
3. 经由过程回调函数通报数据
运用线程要领本身发生的变量值作为参数,去调取外部的要领,猎取返回数据的体式格局
- 特性
- 具有猎取数据的主动权
线程同步
要跨线程保护准确的可见性,只要在几个线程之间同享非 final 变量,就必需运用线程同步
1. ThreadLocal
ThreadLocal应用空间换时刻,经由过程为每一个线程供应一个自力的变量副本,避免了资本守候,处理了变量并发接见的争执问题,进步了并发量。完成了线程间的数据断绝,然则线程间没法同享统一个资本
public class StudyThread {
public static void main(String[] args) throws ExecutionException, InterruptedException {
SyncTest syncTest = new SyncTest();
ConcurrentHashMap<String, String> testConMap = new ConcurrentHashMap<>();
for (int i = 0; i < 10; i++) {
ThreadTest2 threadTest2 = new ThreadTest2();
threadTest2.setSyncTest(syncTest);
Thread threadTest = new Thread(threadTest2);
threadTest.start();
}
}
}
//完成Runnable
class ThreadTest2 implements Runnable {
private SyncTest syncTest;
public void setSyncTest(SyncTest syncTest) {
this.syncTest = syncTest;
}
@Override
public void run() {
syncTest.threadLocalTest(Thread.currentThread().getName());
}
}
class SyncTest {
private static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
public void threadLocalTest(String name) {
try {
System.out.println(name + "进入了threadLocal要领!");
threadLocal.set(name);
Thread.currentThread().sleep(100);
System.out.println(threadLocal.get() + "脱离了threadLocal要领!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2. synchronized
不论synchronized是用来润饰要领,照样润饰代码块,其实质都是锁定某一个对象。润饰要领时,锁上的是挪用这个要领的对象,即this;润饰代码块时,锁上的是括号里的谁人对象。每一个Java对象都有一个内置锁,接见synchronized代码块或synchronized要领的时刻,线程都须要起首猎取到对象关联的内置锁,关于static要领,线程猎取的是类对象的内置锁。
- 特性
- 锁的对象越小越好
public class StudyThread {
public static void main(String[] args) throws ExecutionException, InterruptedException {
SyncTest syncTest = new SyncTest();
ConcurrentHashMap<String, String> testConMap = new ConcurrentHashMap<>();
for (int i = 0; i < 10; i++) {
ThreadTest2 threadTest2 = new ThreadTest2();
threadTest2.setSyncTest(syncTest);
threadTest2.setTestConMap(testConMap);
Thread threadTest = new Thread(threadTest2);
threadTest.start();
}
}
}
//完成Runnable
class ThreadTest2 implements Runnable {
private ConcurrentHashMap<String, String> testConMap;
private SyncTest syncTest;
public void setTestConMap(ConcurrentHashMap<String, String> testConMap) {
this.testConMap = testConMap;
}
public void setSyncTest(SyncTest syncTest) {
this.syncTest = syncTest;
}
@Override
public void run() {
//三个要领须要零丁测试,由于testConMap会相互影响
//测试同步要领,锁住的对象是syncTest
//syncTest.testSyncMethod(testConMap,Thread.currentThread().getName());
//测试同步代码块,锁住的对象是testConMap
//syncTest.testSyncObject(testConMap, Thread.currentThread().getName());
//测试没有锁时实行请求是何等的杂沓!!!
//syncTest.testNoneSyncObject(testConMap, Thread.currentThread().getName());
}
}
//同步测试要领类
class SyncTest {
public synchronized void testSyncMethod(ConcurrentHashMap<String, String> testConMap, String name) {
try {
System.out.println(name + "进入了同步要领!");
testConMap.put("name", name);
Thread.currentThread().sleep(10);
System.out.println(testConMap.get("name") + "脱离了同步要领!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void testSyncObject(ConcurrentHashMap<String, String> testConMap, String name) {
synchronized (testConMap) {
try {
System.out.println(name + "进入了同步代码块!");
testConMap.put("name", name);
Thread.currentThread().sleep(10);
System.out.println(testConMap.get("name") + "脱离了同步代码块!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void testNoneSyncObject(ConcurrentHashMap<String, String> testConMap, String name) {
try {
System.out.println(name + "进入了无人统领地区!");
testConMap.put("name", name);
Thread.currentThread().sleep(10);
System.out.println(testConMap.get("name") + "脱离了无人统领地区!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3. volatile
- 特性
- 保证可见性,有序性,不保证原子性
- 它会强迫将对缓存的修正操纵马上写入主存
- volatile不适合复合操纵(对变量的写操纵不依赖于当前值),不然须要保证只要单一线程可以修正变量的值
- 运用volatile关键字,可以制止指令重排序(单例两重搜检锁)
public class StudyThread {
static int v = 1;//volatile可以保证变量的可见性
public static void main(String[] args) {
//修改线程
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
v++;//确保只要一个线程修正变量值
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
//检测线程
new Thread(new Runnable() {
@Override
public void run() {
int old = 0;
while (old < 11) {
if (old != v) {
old = v;
System.out.println("检测线程:v的值更改为" + old);
}
}
}
}).start();
}
}
4. ReentrantLock
- 申明
- 现在ReentrantLock和synchronized机能上没有什么差异
- ReentrantLock须要手动加锁和解锁,且解锁的操纵只管要放在finally代码块中,保证线程准确开释锁
- ReentrantLock可以完成平正锁,在锁上守候时刻最长的线程将取得锁的运用权,机能没有非平正锁机能好
- ReentrantLock供应了一个可以相应中断的猎取锁的要领
lockInterruptibly()
,可以用来处理死锁问题 - ReentrantLock还供应了猎取锁限时守候的要领
tryLock()
,运用该要领合营失利重试机制来更好的处理死锁问题 - ReentrantLock连系Condition接口可以完成守候关照机制
public class StudyThread {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//三段代码一一测试
//测试tryLock
Thread threadTest00 = new Thread(new Runnable() {
@Override
public void run() {
try {
while(!lock.tryLock()){
System.out.println(Thread.currentThread().getName() + "没有拿到锁,继承守候!");
Thread.sleep(50);
}
System.out.println(Thread.currentThread().getName() + "进入了lock代码块!");
Thread.currentThread().sleep(300);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + "预备开释锁,并脱离lock代码块!");
lock.unlock();
}
}
});
threadTest00.start();
Thread threadTest01 = new Thread(new Runnable() {
@Override
public void run() {
try {
while(!lock.tryLock()){
System.out.println(Thread.currentThread().getName() + "没有拿到锁,继承守候!");
Thread.sleep(50);
}
System.out.println(Thread.currentThread().getName() + "进入了lock代码块!");
Thread.currentThread().sleep(300);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + "预备开释锁,并脱离lock代码块!");
lock.unlock();
}
}
});
threadTest01.start();
//测试中断锁
Thread threadTest02 = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + "进入了lock代码块!");
Thread.currentThread().sleep(2000);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + "预备开释锁,并脱离lock代码块!");
lock.unlock();
}
}
});
threadTest02.start();
Thread threadTest03 = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + "进入了lock代码块!");
Thread.currentThread().sleep(2000);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + "预备开释锁,并脱离lock代码块!");
lock.unlock();
}
}
});
threadTest03.start();
Thread.currentThread().sleep(20);
threadTest02.interrupt();
//测试condition
Thread threadTest04 = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "进入了lock代码块,守候关照!");
condition.await();
System.out.println(Thread.currentThread().getName() + "收到关照,继承实行!");
Thread.currentThread().sleep(1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + "预备开释锁,并脱离lock代码块!");
lock.unlock();
}
}
});
threadTest04.start();
Thread threadTest05 = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "进入了lock代码块!");
Thread.currentThread().sleep(1000);
condition.signal();
System.out.println(Thread.currentThread().getName() + "发出关照!");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + "预备开释锁,并脱离lock代码块!");
lock.unlock();
}
}
});
threadTest05.start();
}
}
5. 线程平安的类
Java中许多类说的线程平安指的是,它的每一个要领零丁挪用(即原子操纵)都是线程平安的,然则代码整体的互斥性并不受掌握
- 线程平安的类有以下几类
- Concurrentxxx
- ThreadPoolExecutor
- BlockingQueue和BlockingDeque
- 原子类Atomicxxx—包装类的线程平安类
- CopyOnWriteArrayList和CopyOnWriteArraySet
- 经由过程synchronized 关键字给要领加上内置锁来完成线程平安:Timer,TimerTask,Vector,Stack,HashTable,StringBuffer
- Collections中的synchronizedCollection(Collection c)要领可将一个鸠合变成线程平安:
Map m=Collections.synchronizedMap(new HashMap());
线程池
线程池只能放入完成Runable或callable类线程,不能直接放入继承Thread的类
1. Executors线程池的完成
- 要点
- 大概致使资本耗尽,OOM问题涌现
- 线程池不允许运用Executors去竖立,而是经由过程ThreadPoolExecutor的体式格局(阿里巴巴java开发)
public class StudyThread {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//竖立一个线程池,该线程池重用牢固数目的从同享无界行列中运转的线程
//ExecutorService threadPool = Executors.newFixedThreadPool(20);
//竖立一个保护充足的线程以支撑给定的并行级别的线程池,线程的现实数目可以动态增进和压缩,事情盗取池不保证实行提交的使命的次序
//ExecutorService threadPool = Executors.newWorkStealingPool(8);
//竖立一个运用从无界行列运转的单个事情线程的实行程序。
//ExecutorService threadPool = Executors.newSingleThreadExecutor();
//竖立一个依据须要竖立新线程的线程池,但在可用时将从新运用之前组织的线程。假如没有可用的线程,将竖立一个新的线程并将其添加到该池中。未运用六十秒的线程将被停止并从缓存中删除
ExecutorService threadPool = Executors.newCachedThreadPool();
//放入Runnable类线程
for (int i = 0; i < 10; i++) {
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程名:" + Thread.currentThread().getName());
}
});
}
//Thread.currentThread().sleep(1000);
//放入Callable类线程
List<Future<String>> futures = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Future<String> future = threadPool.submit(new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("线程名:" + Thread.currentThread().getName());
return Thread.currentThread().getName();
}
});
futures.add(future);
}
threadPool.shutdown();
for (Future future : futures) {
System.out.println(future.get());
}
}
}
2. ThreadPoolExecutor竖立线程池
- 要点
- 线程池余暇大小和最大线程数依据现实情况肯定
- keepAliveTime平常设置为0
- unit平常设置为TimeUnit.SECONDS(其他的也行,反恰是0)
- 使命行列须要指定大小,不要运用无界行列,轻易形成OOM-> new ArrayBlockingQueue<>(512)
- ThreadFactory threadFactory运用体系默许的
- 谢绝战略:
- AbortPolicy:抛出RejectedExecutionException(该非常黑白受检非常,要记得捕捉)
- DiscardPolicy:什么也不做,直接疏忽
- DiscardOldestPolicy:抛弃实行行列中最老的使命,尝试为当前提交的使命腾出位置
- CallerRunsPolicy:直接由提交使命者实行这个使命
public class StudyThread {
public static void main(String[] args) throws ExecutionException, InterruptedException {
int poolSize = Runtime.getRuntime().availableProcessors() * 2;
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(512);
RejectedExecutionHandler policy = new ThreadPoolExecutor.DiscardPolicy();
ExecutorService executorService = new ThreadPoolExecutor(poolSize, poolSize,
0, TimeUnit.SECONDS,
queue,
policy);
//放入Runnable类线程
for (int i = 0; i < 10; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程名:" + Thread.currentThread().getName());
}
});
}
//放入Callable类线程
List<Future<String>> futures = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Future<String> future = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("线程名:" + Thread.currentThread().getName());
return Thread.currentThread().getName();
}
});
futures.add(future);
}
for (Future future:futures) {
System.out.println(future.get());
}
//放入Callable类线程
//运用CompletionService简化猎取效果的操纵,实行完一个使命,猎取一个效果,效果次序和实行次序雷同
CompletionService<String> ecs = new ExecutorCompletionService<String>(executorService);
for (int i = 0; i < 10; i++) {
Future<String> future = ecs.submit(new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("线程名:" + Thread.currentThread().getName());
return Thread.currentThread().getName();
}
});
}
for (int i = 0; i < 10; i++) {
System.out.println(ecs.take().get());
}
}
}
一文带你了解 C# DLR 的世界