线程(二)
让线程按顺序执行
例:
static Object obj = new Object();
static int flag = 1;
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
synchronized (obj) {
while (flag != 1) {
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("a");
flag = 2;
obj.notifyAll();
}
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
synchronized (obj) {
while (flag != 2) {
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("b");
flag = 3;
obj.notifyAll();
}
}
});
Thread t3 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
synchronized (obj) {
while (flag != 3) {
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("c");
flag = 1;
obj.notifyAll();
}
}
});
t1.start();
t2.start();
t3.start();
}
创建线程的第三种方式
public interface Callable<V>{}
FutureTask task = new FutureTask(()->{
System.out.println(Thread.currentThread().getName()+"开始执行");
Thread.sleep(2000);
return "ok";
}
});
new Thread(task).start();//启动
System.out.println(task.get());//获取返回结果
与Runnable的不同处:
1)有返回结果
2)可以抛出异常
线程池
创建有限的线程资源(体现一种设计模式:享元模式)
常见线程池类型
创建固定大小的线程池:
Executors.newFixedThreadPool(2);
核心线程数=最大线程数(没有救急线程被创建)
阻塞队列 无界,可以放任意数量的任务,
使用场景 : 适合执行数量有限,长时间运行的任务
例 :
ExecutorService threadPool = Executors.newFixedThreadPool(2);//创建固定大小的线程池
// 执行带有返回结果的任务
Future future = threadPool.submit(() -> {
System.out.println(Thread.currentThread().getName()+"执行计算...");
Thread.sleep(1000);
return 10;
});
System.out.println(future.get());
threadPool.shutdown(); // 不接收新任务,当所有任务运行结束,整个线程池关闭
创建缓冲线程池:
Executors.newCachedThreadPool()
核心线程数是0, 最大线程数是Integer的最大值(救急线程可以无限创建)
生存时间是60s
使用场景 : 适合任务数比较密集,但每个任务执行时间较短的情况
创建单线程线程池:
Executors.newSingleThreadExecutor()
使用场景:希望多个任务排队执行
带有日程安排功能的线程池 :
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
// 让任务推迟一段时间执行
// 参数1.任务对象, 参数2,3 推迟的时间
/*service.schedule(()->{
System.out.println("执行任务...");
}, 10L, TimeUnit.SECONDS);*/
// 以一定的频率反复执行任务(任务不会重叠)
// 参数1,任务对象, 参数2,初始推迟时间, 参数3,4 时间间隔和单位
/*service.scheduleAtFixedRate(()->{
try {
Thread.sleep(1200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello");
}, 0, 1, TimeUnit.SECONDS);*/
//还有scheduleWithFixedDalay(), 用于一个任务结束,然后过固定时间后开始下一任务
ExecutorService的主要实现类:ThreadPoolExecutor
ThreadPoolExecutor(int corePoolSize,//核心线程数目
int maximumPoolSize,//最大线程数
long keepAliveTime,//生存时间- 针对救急线程
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue)//超过核心线程数的任务进入阻塞队列
//阻塞队列是 first in first out)
//超过阻塞队列则创建新的线程救急
//corePoolSize+救急的线程 <= maximumPoolSize(最大线程数)
原子操作类
AtomicInteger
AtomicBoolean
常见方法 : getAndIncrement(); 获取并自增
incrementAndGet();自增并获取
getAndDecrement();获取并自减
decrementAndGet();自减并获取
// 创建原子整数类
private static AtomicInteger i = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int j = 0; j < 5000; j++) {
i.getAndIncrement(); // 获取并且自增 i++
// i.incrementAndGet(); // 自增并且获取 ++i
}
});
Thread t2 = new Thread(() -> {
for (int j = 0; j < 5000; j++) {
i.getAndDecrement(); // 获取并且自减 i--
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
线程安全集合类
曾学过线程安全类:
StringBuffer
Random
vector
Hashable
新增线程安全集合类:
ConcurrentHashMap
ConcurrentSkipLinkedMap //可排序
CopyOnwriteArrayList
生产者消费者
private static BlockingQueue<Product> queue = new ArrayBlockingQueue<>(5);
public static void main(String[] args) {
// 生产者线程
new Thread(()->{
for (int i = 0; i < 10; i++) {
Product p = new Product(i);
System.out.println(Thread.currentThread().getName()+"生产了:"+p);
try {
queue.put(p);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
// 消费者线程
for (int j = 0; j < 5; j++) {
new Thread(()->{
for (int i = 0; i < 2; i++) {
try {
Product p = queue.take();
System.out.println(Thread.currentThread().getName()+"消费了:"+p);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
CAS机制
juc中大部分类通过无锁并发实现的(无synchronized) :
ReentrantLock,CountDownLatch,CyclicBarrier,Semaphore
CAS : Compare And Swap
常称synchronized为悲观锁
称 cas 为乐观锁
首先不会给共享资源加锁,而是先尝试比较旧值是否与共享
区域的值相同,如果不等,那么说明别的线程改动了共享区域
的值,我的修改失败,如果相等,那么就让我的修改成功,修改
失败再尝试
代码显示 :
int var5;
// 修改失败,没关系,重新尝试 自旋
do {
// 获取共享区域的最新值
var5 = this.getIntVolatile(var1, var2); // 10
// 比较并交换 最新值 最新值+1
} while(! this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
ReentrantLock
性能较synchronized高,但内存占用多
static int i = 0;
public static void main(String[] args) throws InterruptedException {
ReentrantLock rl = new ReentrantLock();
Thread t1 = new Thread(() -> {
for (int j = 0; j < 5000; j++) {
try {
rl.lock(); // 加锁
i++;
} finally {
rl.unlock(); // 保证解锁一定被执行
}
}
});
Thread t2 = new Thread(() -> {
for (int j = 0; j < 5000; j++) {
try {
rl.lock(); // 加锁
i--;
} finally {
rl.unlock(); // 保证解锁一定被执行
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
CountDownLatch
固定线程数,多个线程结束后才进行下一步
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(10);
String[] all = new String[10];
for (int j = 0; j < 10; j++) {
int x = j;
new Thread(()->{
Random r = new Random();
for (int i = 0; i <= 100; i++) {
try {
Thread.sleep(r.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (all){
all[x]=(i+"%");
System.out.print("\r"+Arrays.toString(all));
}
}
latch.countDown();
}).start();
}
latch.await();//多个线程完成之后,再继续下一步
System.out.println("\nend...");
}
CyclicBarrier
可循环,且需要等待,每达到指定个数线程才执行
// CyclicBarrier 可循环的 屏障(栅栏)
// 当满足CyclicBarrier设置的线程个数时,继续执行,没有满足则等待
CyclicBarrier cb = new CyclicBarrier(2); // 个数为2时才会继续执行
new Thread(()->{
System.out.println("线程1开始.."+new Date());
try {
cb.await(); // 当个数不足时,等待
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("线程1继续向下运行..."+new Date());
}).start();
new Thread(()->{
System.out.println("线程2开始.."+new Date());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
cb.await(); // 2 秒后,够两个线程,继续运行
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("线程2继续向下运行..."+new Date());
}).start();
Semaphore
限制了能同时运行线程的上限
Semaphore s = new Semaphore(3); // 最多能同时运行3个线程
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
s.acquire(); // 获得此信号量
System.out.println("我是线程" + Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
s.release(); // 必须释放信号量
}
}).start();
}