【Java基础复习】concurrency

创建多线程的方法

  • new一个Thread时传入一个实现了Runnable接口的类的实例对象
  • 继承Thread类,重写Thread类的run方法

线程休眠的方法

Thread.sleep

Interrupts

每一个线程都有一个interrupt status的flag,如果该线程被interrupt的话,这个标志位就会为true。
使用Thread的static方法Thread.interruped()可以返回该标志位的值,同时清除这个标志位。
使用Thread的non-static方法threadObject.isInterrupted()可以查看该线程的标志位,但是不清除这个标志位。

threadObject可以使用interrupt方法来interrupr这个thread。

怎样使线程supporting inerruption?

  • If the thread is frequently invoking methods that throw InterruptedException, it simply returns from the run method after it catches that exception. (比如Thread.sleep()方法就会抛出InterruptedException)
  • What if a thread goes a long time without invoking a method that throws InterruptedException? Then it must periodically invoke Thread.interrupted, which returns true if an interrupt has been received.

Joins

join(非static)方法使得当前线程等待另一线程的完成。
如果t是一个Thread实例,那么t.join()会使得当前线程等待t线程的完成。

线程之间交互可能存在的问题:

Threads communicate primarily by sharing access to fields and the objects reference fields refer to. This form of communication is extremely efficient, but makes two kinds of errors possible: thread interference and memory consistency errors. The tool needed to prevent these errors is synchronization.


However, synchronization can introduce thread contention, which occurs when two or more threads try to access the same resource simultaneously and cause the Java runtime to execute one or more threads more slowly, or even suspend their execution. Starvation and livelock are forms of thread contention.

首先是thread interference:
Interference happens when two operations, running in different threads, but acting on the same data, interleave. This means that the two operations consist of multiple steps, and the sequences of steps overlap. 这种bug很难被发现和修正

其次是memory consistency errors:

Memory consistency errors occur when different threads have inconsistent views of what should be the same data. The causes of memory consistency errors are complex and beyond the scope of this tutorial.


The key to avoiding memory consistency errors is understanding the happens-before relationship.

We’ve already seen two actions that create happens-before relationships.

  • When a statement invokes Thread.start, every statement that has a happens-before relationship with that statement also has a happens-before relationship with every statement executed by the new thread. The effects of the code that led up to the creation of the new thread are visible to the new thread.
  • When a thread terminates and causes a Thread.join in another thread to return, then all the statements executed by the terminated thread have a happens-before relationship with all the statements following the successful join. The effects of the code in the thread are now visible to the thread that performed the join.

Synchronized methods & Synchronized statements

首先是synchronized methods

class Counter {
    private int c = 0;

    public synchronized void increment() {
        c++;
    }

    public synchronized void decrement() {
        c--;
    }

    public synchronized int value() {
        return c;
    }
}

给方法加上synchronized关键字由如下两个效果:

  • First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.
  • Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads.

虽然synchronized methods可以防止thread interference和memory consistency, 但是可能会带来liveness的问题.

Intrinsic Locks & Synchronization

什么是Intrinsic lock?

intrinsic lock有什么功能?

  • enforcing exlusive accesss to an object’s state.
  • When a thread releases an intrinsic lock, a happens-before relationship is established between that action and any subsequent acquisition of the same lock.

静态的synchronized method:
You might wonder what happens when a static synchronized method is invoked, since a static method is associated with a class, not an object. In this case, the thread acquires the intrinsic lock for the Class object associated with the class. Thus access to class’s static fields is controlled by a lock that’s distinct from the lock for any instance of the class.

synchronized statements:

public class MsLunch {
    private long c1 = 0;
    private long c2 = 0;
    private Object lock1 = new Object();
    private Object lock2 = new Object();

    public void inc1() {
        synchronized(lock1) {
            c1++;
        }
    }

    public void inc2() {
        synchronized(lock2) {
            c2++;
        }
    }
}

Reentrant lock:
防止线程自己阻塞自己

Atomic Access

atomic 操作可以防止thread interference,但是不可以防止memory consistence errors.

常见的atomic action:

  • Reads and writes are atomic for reference variables and for most primitive variables (all types except long and double).
  • Reads and writes are atomic for all variables declared volatile (including long and double variables).

Atomic actions cannot be interleaved, so they can be used without fear of thread interference. However, this does not eliminate all need to synchronize atomic actions, because memory consistency errors are still possible. Using volatile variables reduces the risk of memory consistency errors, because any write to a volatile variable establishes a happens-before relationship with subsequent reads of that same variable. This means that changes to a volatile variable are always visible to other threads. What’s more, it also means that when a thread reads a volatile variable, it sees not just the latest change to the volatile, but also the side effects of the code that led up the change.

java.util.concurrency中的一些类提供了原子操作

Liveness

Deadlock

public class Deadlock {
    static class Friend {
        private final String name;
        public Friend(String name) {
            this.name = name;
        }
        public String getName() {
            return this.name;
        }

        public synchronized void bow (Friend bower) {
            System.out.printf("%s: %s" + " has bowed to me!%n", this.name, bower.getName());
            bower.bowBack(this);
        }

        public synchronized void bowBack(Friend bower) {
            System.out.printf("%s: %s " + "has bowed back to me%n", this.name, bower.getName());
        }
    }

    public static void main(String[] args) {
        final Friend alphonse =
                new Friend("Alphonse");
        final Friend gaston =
                new Friend("Gaston");
        new Thread(new Runnable() {
            public void run() { alphonse.bow(gaston); }
        }).start();
        new Thread(new Runnable() {
            public void run() { gaston.bow(alphonse); }
        }).start();
    }
}

什么是Starvation,什么是Livelock
走廊里相互礼让的例子解释deadlock和livelock

Guarded block

线程检验某条件成立时才继续往下执行

使用wait, notify的方法

线程调用某实例的wait方法时必须拥有此实例的intrinsic lock

Lock Objects

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

Executors

java.util.concurrent中定义的三种executor接口:

  • Executor
  • ExecutorServiceExexutor的子接口
  • ScheduledExecutorServiceExecutorService的子接口

Typically, variables that refer to executor objects are declared as one of these three interface types, not with an executor class type.

Executor接口
可以直接使用Executor接口中提供的exrcute方法来代替原始的创建线程的方法.如果r是一个Runnableobject, e是一个Executorobject的话,呢么下面两个语句相等:
(new Thread(r)).start();e.execute(r);
有不同的是,第一种方式直接new一个新的线程并且立即启动这个新的线程, 第二种方式很有可能利用一个已经存在的worker thread去runr, 或者将r放入一个队列中,等待一个worker thread空闲的时候再runr.

ExecutorService接口

ScheduledExecutorService接口

Thread Pools

java.util.concurrent大多数对的executor实现都使用了***thread pools***, thread pools由***worker thread***组成.worker thread和它执行的Runnable或者Callable任务是分离的, 经常被用于执行多任务.

thread pool的一种常见类型是***fixed thread pool***

fork/join

是一种框架, 对ExecutorService的实现,为了尽可能地利用系统地多核处理能力,应用场景为可以层层细分地任务.

work-steal 算法

核心是java.util.ForkJoinPool类, 父类是AbstractExecutorService类.

为了使用fork/join框架,首先要实现下面一段代码:

if (my portion of the work is small enough)
  do the work directly
else
  split my work into two pieces
  invoke the two pieces and wait for the results

然后:Wrap this code in a ForkJoinTask subclass, typically using one of its more specialized types, either RecursiveTask (which can return a result) or RecursiveAction.

最后After your ForkJoinTask subclass is ready, create the object that represents all the work to be done and pass it to the invoke() method of a ForkJoinPool instance.

atomic variables

可以同时解决交织和内存不连续地问题
java.util.cuncurrent.atomic

concurrent random numbers

java.util.concurrent.THreadLocalRandom
int r = ThreadLocalRandom.current().nextInt(4, 77);

猜你喜欢

转载自blog.csdn.net/qq_27637285/article/details/107558624
今日推荐