虚拟机中线程的六种状态
创建一个池子,池子中是空的-----------------创建Executors中的静态方法
有任务需要执行时创建线程对象;任务执行完毕,线程对象归还给池子----------------submit方法
(池子会自动帮我们创建对象,任务执行完毕,也会自动把线程对象归还给池子)
所有的任务执行完毕,关闭连接池-----------------shutdown方法
创建默认线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyThreadPoolTest {
public static void main(String[] args) throws InterruptedException {
//创建一个池子,池子中是空的,默认可以容纳int类型的最大值
ExecutorService executorService = Executors.newCachedThreadPool();
//Executors----可以帮我们创建线程池对象
//ExecutorService----可以帮助我们控制线程池
executorService.submit(()->{
System.out.println(Thread.currentThread().getName()+"在执行了");
});
//Thread.sleep(2000);
executorService.submit(()->{
System.out.println(Thread.currentThread().getName()+"在执行了");
});
executorService.shutdown();
}
}
创建指定上限的线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyThreadPoolTest2 {
public static void main(String[] args) {
//参数不是初始值而是最大值
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.submit(()->{
System.out.println(Thread.currentThread().getName()+"在执行了");
});
executorService.submit(()->{
System.out.println(Thread.currentThread().getName()+"在执行了");
});
executorService.shutdown();
}
}
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class MyThreadPoolTest3 {
public static void main(String[] args) {
//参数一:核心线程数量----不能小于0
//参数二:最大线程数----不能小于等于0,最大数量>=核心线程数量
//参数三:空闲线程最大存活时间----不能小于0
//参数四:时间单位----TimeUnit
//参数五:任务队列----不能为null,让任务在队列中等着,等有线程空闲了,在从队列中获取任务并执行
//参数六:创建线程工厂----不能为null,按照默认的方式创建线程对象
//参数七:任务的拒绝策略----不能为null,当提交的任务>池子中最大的线程数量+队列容量时拒绝任务;
ThreadPoolExecutor pool = new ThreadPoolExecutor(2,5,2, TimeUnit.SECONDS,new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
pool.submit(()->{
System.out.println(Thread.currentThread().getName()+"在执行了");
});
pool.submit(()->{
System.out.println(Thread.currentThread().getName()+"在执行了");
});
pool.shutdown();
}
}
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常;默认策略
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常,这是不推荐的做法;
ThreadPoolExecutor.DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入到队列中;
ThreadPoolExecutor.CallerRunsPolicy:调用任务的run()方法绕过线程池直接执行
问题:
如果A线程修改了堆中共享的变量,那么其他线程不一定能及时使用最新的值。
Volatile关键字,强制线程每次在使用的时候,都会看一下共享区域最新的值。
示例:
public class LittleFish {
public static volatile int fish = 5;
}
public class WhiteCat extends Thread {
@Override
public void run() {
while (LittleFish.fish == 5){
}
System.out.println("小鱼干不够5斤了");
}
}
public class BlackCat extends Thread {
@Override
public void run() {
try {
Thread.sleep(1000);
LittleFish.fish = 4;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args) {
WhiteCat wc = new WhiteCat();
wc.setName("小白");
wc.start();
BlackCat bc = new BlackCat();
bc.setName("小黑");
bc.start();
}
}
所谓的原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断;要么所有的操作都不执行,多个操作是一个不可分割的整体;
volatile关键字:只能保证线程每次在使用共享数据的时候是最新值,但是不能保证原子性。
示例:
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicTest {
public static void main(String[] args) {
AtomicInteger ai = new AtomicInteger();
System.out.println(ai); //0
AtomicInteger ai2 = new AtomicInteger(10);
System.out.println(ai2); //10
System.out.println(ai2.get()); //10
int andIncrement = ai2.getAndIncrement();
System.out.println(andIncrement); //10
System.out.println(ai2.get()); //11
int i = ai2.incrementAndGet();
System.out.println(i); //12
System.out.println(ai2.get()); //12
int i1 = ai2.addAndGet(10);
System.out.println(i1); //22
System.out.println(ai2.get()); //22
int andSet = ai2.getAndSet(10);
System.out.println(andSet); //22
System.out.println(ai2.get()); //10
}
}
相同点:在多线程情况下,都可以保证共享数据的安全性。
不同点:synchronized总是从最坏的角度出发,认为每次获取数据的时候,别人都有可能修改。所以在每次操作共享数据之前,都会上锁。(悲观锁)
CAS是从乐观的角度出发,假设每次获取数据别人都不会修改,所以不会上锁。只不过在修改共享数据的时候,会检查以下,别人有没有修改过这个数据。如果别人修改过,那么再次获取现在的最新值。如果别人没有修改过,那么直接修改共享数据的值。(乐观锁)
HashMap是线程不安全的(多线程环境下可能会存在问题);为了保证数据的安全性我们可以使用Hashtable,但是Hashtable效率低下;
1.7原理
1.8原理
使用场景:让某一条线程等待其他线程执行完毕后再执行。
方法 | 说明 |
---|---|
public CountDownLatch(int count) | 参数传递线程数,表示等待线程数量 |
public void await() | 让线程等待 |
public void countDown() | 当前线程执行完毕 |
示例:
import java.util.concurrent.CountDownLatch;
public class ChildThread1 extends Thread {
CountDownLatch countDownLatch;
public ChildThread1(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
//吃饺子
for (int i = 0; i < 10; i++) {
System.out.println(getName() + "在吃第" + i + "个饺子");
}
//吃完说一声
//每一次调用countDown方法的时候,就让计数器-1
countDownLatch.countDown();
}
}
public class ChildThread2 extends Thread {
CountDownLatch countDownLatch;
public ChildThread2(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
//吃饺子
for (int i = 0; i < 15; i++) {
System.out.println(getName() + "在吃第" + i + "个饺子");
}
//吃完说一声
//每一次调用countDown方法的时候,就让计数器-1
countDownLatch.countDown();
}
}
import java.util.concurrent.CountDownLatch;
public class ChildThread3 extends Thread {
CountDownLatch countDownLatch;
public ChildThread3(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
//吃饺子
for (int i = 0; i < 12; i++) {
System.out.println(getName() + "在吃第" + i + "个饺子");
}
//吃完说一声
//每一次调用countDown方法的时候,就让计数器-1
countDownLatch.countDown();
}
}
import java.util.concurrent.CountDownLatch;
public class MotherThread extends Thread {
CountDownLatch countDownLatch;
public MotherThread(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
//等待
try {
//当计数器变成0的时候,会自动唤醒这里等待的线程
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//收拾碗筷
System.out.println("妈妈在收拾碗筷");
}
}
import java.util.concurrent.CountDownLatch;
public class MyCountDownLatchTest {
public static void main(String[] args) {
//创建CountDownLatch的对象,需要传递给四个线程
//在底层定义了一个计数器,此时计数器的值就是3
CountDownLatch countDownLatch = new CountDownLatch(3);
//创建4个线程对象并开启他们
MotherThread mt = new MotherThread(countDownLatch);
mt.start();
ChildThread1 ct1 = new ChildThread1(countDownLatch);
ChildThread2 ct2 = new ChildThread2(countDownLatch);
ChildThread3 ct3 = new ChildThread3(countDownLatch);
ct1.setName("小红");
ct2.setName("小明");
ct3.setName("小兰");
ct1.start();
ct2.start();
ct3.start();
}
}
使用场景:可以控制访问特定资源的线程数量。
需要有人管理这个通道----------------创建Semaphore对象
当有车进来了,发通行许可证-----------------acquire()发通行证
当车出去了,收回通行许可证------------------release()收回通行证
如果通行许可证发完了,那么其他车辆只能等着
代码实现:
import java.util.concurrent.Semaphore;
public class MyRunnable implements Runnable {
//获得管理员对象
private Semaphore semaphore = new Semaphore(2);
@Override
public void run() {
//获得通行证
try {
semaphore.acquire();
//开始行使
System.out.println("获得了通行证开始行驶");
Thread.sleep(2000);
System.out.println("归还通行证");
//归还通行证
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class MySemaphoreTest {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
for (int i = 0; i < 50; i++) {
new Thread(mr).start();
}
}
}