去年已经看过这本书了,今年回看这本书发现这本书内容跟没看过一样,便重新进行阅览并且对其中的代码进行编写以及课后练习的习题做一个记录。 (自我认识:对Java的并发了解并不透彻,并且还是一知半解的状态,大体情况只停留在理论阶段)
关键字:Thread(sleep,join,yield,Object.wait),Runnable,Callable,JUC,volatile,线程池(Executors),优先级(几乎没什么用不同操作系统有所不同),共享资源,原子类,临界区。
个人理解:先编写完一个业务流程的顺序执行过程后转线程,因为一个线程就是在进程中的一个单一的顺序控制流。
线程驱动任务,那么定义任务实现Runnable接口后重写Run方法(ListOFF发射之前的倒计时方法)
public class LiftOff implements Runnable{
protected int countDown = 10; //default
private static int taskCount = 0;
private final int id = taskCount++;
public LiftOff() {
}
public LiftOff(int countDown) {
this.countDown = countDown;
}
public String status(){
return "#" + id + "(" + (countDown > 0 ? countDown : "LiftOff") + "), ";
}
@Override
public void run() {
while (countDown-- > 0){
System.out.print(status());
Thread.yield();
}
}
}
ListOff方法中有一个技巧taskCount是静态变量伴随着Class类,并且id是final修饰经过初始化后不可再进行更改,用来标识不同的任务。
public class MainThread {
public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
Thread t = new Thread(new LiftOff());
t.start();
}
System.out.println("Waiting For ListOff");
}
}
会出现不同线程交叉运行的结果,这是由于线程只要获得CPU执行权就可以运行,并且方法没有进行同步操作。
线程池Executors
有返回值使用exec.submit(),无返回值直接执行exec.execute()
在讲线程池之前要对线程池的操作原理以及线程池的几个参数进行讲解一下
①一个任务提交到了线程池,首先判断是否少于核心线程数如果少则创建一个线程执行任务否则进入到②判断阻塞队列是否已满,如果没满则进入阻塞队列否则进入③
③如果当前线程数没大于最大线程数那么就创建一个线程,如果大于的话进行删除策略
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
RejectedExecutionHandler handler)
参数的类型可以查看Java全栈知识点
练习:实现一个Runnable,在run()内部打印一个消息,然后调用yield()。重复操作三次,然后从run()中返回。在构造器中放置一条启动消息,并且防止一条在任务终止时的关闭消息。使用线程池创建大量任务并驱动它们
public class task implements Runnable{
private static int taskCount = 0;
private final int id = taskCount++;
public task() {
System.out.println(id + "task is construct");
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(id + "task is running");
Thread.yield();
}
System.out.println(id + " task compelte");
}
}
public class CacheThreadPool {
public static void main(String[] args) {
ExecutorService threadpool = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
threadpool.execute(new task());
}
threadpool.shutdown();
}
}
解决共享资源的竞争
场景:你坐在桌边手拿叉子,正要去叉盘子中的最后一片食物,当你的叉子就要够着它时,这片食物突然消失了,因为你的线程被挂起来,而另一个餐者进入并吃掉了它。这正是你在编写并发程序时需要处理的问题,对于并发工作,你需要同步方式来防止两个任务访问相同的资源。
public class data implements Runnable{
private int x = 10;
private int y = 0;
public synchronized void plusXminusY(){
++x;
Thread.yield();
--y;
System.out.println(x +" " +y);
}
@Override
public String toString() {
return "data{" +
"x=" + x +
", y=" + y +
'}';
}
@Override
public void run() {
plusXminusY();
}
}
public class Main {
public static void main(String[] args) {
data d = new data();
for (int i = 0; i < 6; i++) {
new Thread(d).start();
}
}
}
使用synchronized方法进行并发同步的注意点:
① 使用并发时域设置为private,否则synchronized无法防止其他任务直接访问域
② 多个并发任务要共享同一把锁,(针对每一个类都有一个类锁所以synchronized static方法可以在类的方位防止对static数据的并发访问)
public class AtomicityTest implements Runnable{
private int i = 0;
public int getValue(){return i;}
private synchronized void evenIncrement(){i++;i++;}
@Override
public void run() {
while (true){
evenIncrement();
}
}
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
AtomicityTest at = new AtomicityTest();
exec.execute(at);
while (true){
int val = at.getValue();
if(val % 2 != 0){
System.out.println(val);
System.exit(0);
}
}
}
}
上述程序几点问题:
①即使evenIncrement上了同步,return i当中也是原子性操作但是只要涉及到对同步资源域操作就上必须synchronized,导致程序在加到1时候,main主程序获取到i=1以后那么就退出程序了。
② 并且i也不是volatile存在内存可见性问题
注:对volatile修饰的变量是直接针对内存进行修改,而没有被缓存
原子类(Atomic)
使用Atomic原子类对上述例子进行改写,底层原子类通过CAS乐观锁机制对值进行修改,那么就无需显示上锁,并且设置一个Timer,让程序5s后结束。
public class AtomicIntegerTest implements Runnable{
private AtomicInteger i = new AtomicInteger(0);
public int getValue(){
return i.get();
}
private void evenIncrement(){
i.getAndAdd(2);
}
@Override
public void run() {
while (true){
evenIncrement();
}
}
public static void main(String[] args) {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.out.println("Aborting");
System.exit(0);
}
},5000);
ExecutorService exec = Executors.newCachedThreadPool();
AtomicIntegerTest at = new AtomicIntegerTest();
exec.execute(at);
while (true){
int val = at.getValue();
if(val % 2 != 0){
System.out.println(val);
System.exit(0);
}
}
}
}
临界区
只是希望防止多个线程同时访问方法内部的部分代码而不是防止访问整个方法,使用同步代码块进行抽离出来的代码块称为临界区
public class PairManager1 extends PairManager{
@Override
public synchronized void increment() {
p.incrementX();
p.incrementY();
store(getPair());
}
}
//临界区使用同步代码块,多个任务对访问对象的时间性能提升
public class PairManager2 extends PairManager{
@Override
public void increment() {
Pair temp;
synchronized (this){
p.incrementX();
p.incrementY();
temp = getPair();
}
store(temp);
}
}
//显示lock
public class PairManager3 extends PairManager{
private ReentrantLock lock = new ReentrantLock();
@Override
public void increment() {
lock.lock();
try {
p.incrementX();
p.incrementY();
store(getPair());
} finally {
lock.unlock();
}
}
}



