Java 多线程
Runnable
当从Runnable中导出一个类时,必须具有run方法。但是这个方法并不存在线程能力。需要将Runnable任务附着到线程上。
将Runnable对象转变为工作任务的传统方式是把它提交给一个Thread构造器。
Interface : java.lang.Runnable
The Runnable interface should be implemented by any class whose instances are
intended to be executed by a thread. The class must define a method of no arguments
called run. This interface is designed to provide a common protocol for objects that wish
to execute code while they are active.
Class : java.lang.Thread.Thread(Runnable target)
Allocates a new Thread object. This constructor has the same effect as
Thread (null, target, gname), where gname is a newly generated name.
Automatically generated names are of the form "Thread-"+n,
where n is an integer.
eg:
public class LiftOff implements Runnable{
protected int countDown = 10;
private static int taskCount =0 ;
private final int id = taskCount ++;
public LiftOff() {}
public String status() {
return "#"+Thread.currentThread().getName() +"#"+
id + "("+ (countDown>0 ? countDown : "LiftOff") + ")";
}
@Override
public void run() {
while(countDown -- >0){
System.out.println(status());
}
}
}
执行
//直接执行run 并不会启动新线程,在main线程中执行
LiftOff lf1 = new LiftOff();
lf1.run();
//启动子线程Thread-0
LiftOff lf = new LiftOff();
Thread td = new Thread(lf);
td.start();
Executor
java.util.concurrent包中的执行器(Executor)将为你管理Thread对象,从而简化了并发编程。Executor在客户端和任务执行之间提供了一个间接层。Executor允许管理异步任务的执行,而无需显式的管理线程的生命周期。 java.util.concurrent.Executors
Factory and utility methods for Executor, ExecutorService, ScheduledExecutorService,
ThreadFactory, and Callable classes defined in this package.
其中,newCachedThreadPool为每个任务都创建一个线程。newFixedThreadPool使用了有限的线程集来执行所提交的任务。newSingleThreadExecutor在线程中排队执行多个任务。shutdown()防止其他任务提交到当前的Executor
ExecutorService exec = Executors.newCachedThreadPool();
// ExecutorService exec = Executors.newFixedThreadPool(5);
// ExecutorService exec = Executors.newSingleThreadExecutor();
for(int i=0; i<2; i++){
exec.execute(new LiftOff());
}
exec.shutdown();
同步
加锁
如果你正在写一个变量,它可能接下来将被另一个线程读取,或者正在读取一个上一次已经被另一个线程写过的变量,那么必须使用同步。并且读写线程都必须使用相同的监视器锁同步。
//在方法上加锁
public synchronized void f() {
Pairs q = new Pairs();
p.storeTo(q);
Thread.yield();
}
//在共用变量上加锁
public void g() {
Pairs q = new Pairs();
synchronized(p) {
p.storeTo(q);
}
}
//在调用对象上加锁
public void m() {
Pairs q = new Pairs();
synchronized(this) {
p.storeTo(q);
}
}
//使用显式加锁
private Lock lock = new ReentrantLock();
public void n() {
Pairs q = new Pairs();
try {
lock.lock();
p.storeTo(q);
} finally{
lock.unlock();
}
}
线程本地存储
线程本地存储是一种自动化机制,可以为使用相同变量的每个不同的线程都创建不同的存储。
创建和管理线程本地存储可以使用java.lang.ThreadLocal类来实现。
ThreadLocal不是用来解决共享,竞争等多线程问题,而是提供了保持对象的方法和避免参数传递的方便的对象访问方式。
1,每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。
2,将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。
协作
wait
wait()会在等待外部产生变化的时候将任务挂起,并且只有在notify()或者notifyAll()发生时,这个任务才被唤醒。
1,在wait期间对象是释放的,可以通过notify()、notifyAll(),或者时间到期恢复。
2,sleep()和yield()期间对象仍然是锁定的。
//资源
public class Resource {
private final int ordernum;
public Resource(int ordernum) {this.ordernum = ordernum;}
public String toString() {return "R#" + ordernum;}
}
//生产者
public class Producer implements Runnable{
private Scene rest;
private int count = 0;
public Producer(Scene rest) {this.rest = rest;}
public void run() {
try{
while(!Thread.interrupted()) {
synchronized(this) {
while( rest.res != null) {
wait();
}
if (++count == 10) {
System.out.println("Out of food");
rest.exec.shutdownNow();
}
System.out.println("Order up");
synchronized(rest.customer) {
rest.res = new Resource(count);
rest.customer.notifyAll();
}
TimeUnit.MILLISECONDS.sleep(100);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费者
public class Consumer implements Runnable
{
private Scene rest;
public Consumer(Scene rest){this.rest = rest;}
public void run() {
try {
while(!Thread.interrupted()) {
synchronized(this) {
while(rest.res == null ){
wait();
}
}
System.out.println("Waitperson get" + rest.res);
synchronized(rest.producer) {
rest.res = null;
rest.producer.notifyAll();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//执行场景
public class Scene {
public Resource res;
public Producer producer = new Producer(this);
public Consumer customer = new Consumer(this);
ExecutorService exec = Executors.newCachedThreadPool();
public Scene() {
exec.execute(producer);
exec.execute(customer);
}
public static void main(String[] argv) {
new Scene();
}
}
队列
同步队列在任何时刻都只允许一个任务插入或者移除元素。在java.util.concurrent.BlockingQueue接口中提供了这个队列。通常可以使用LinkedBlockingQueue无界队列,或者ArrayBlockingQueue有固定尺寸的队列。
如果消费者试图从队列中获取对象,而对象为空,那么这些队列还可以挂起消费者任务,并且当有更多元素可用时恢复。阻塞队列可以解决非常大量的问题,简单并且可靠。
如下例子使用LinkedBlockQueue,一个制作Toast,一个涂抹黄油,一个涂果酱,最后吃掉。
public class Toast {
public enum Status {DRY,BUTTERED,JAMMED};
private final int id;
private Status status = Status.DRY;
public Toast(int id) {this.id = id;System.out.println("Dry made");}
public void butter() { status = Status.BUTTERED;System.out.println("buttered");}
public void jam() {status = Status.JAMMED;System.out.println("jammed");}
public int getId() { return id;}
public Status getStatus() { return status;}
public String toString() { return "T#" + id + ":" + status;}
}
public class Toaster implements Runnable {
private ToastQueue queue;
private int count;
private Random rand = new Random(45);
public Toaster(ToastQueue queue) {this.queue = queue;}
@Override
public void run() {
try {
while(!Thread.interrupted()) {
TimeUnit.MILLISECONDS.sleep(100+rand.nextInt(500));
Toast t = new Toast(count++);
queue.put(t);
}
} catch(Exception e) {
System.out.println("Toasted ");
}
}
}
public class Jammer implements Runnable{
private ToastQueue ends;
private ToastQueue buttered;
public Jammer(ToastQueue buttered,ToastQueue ends) {
this.ends = ends;
this.buttered = buttered;
}
@Override
public void run() {
try {
while(!Thread.interrupted()) {
TimeUnit.MILLISECONDS.sleep(500);
Toast t = buttered.take();
t.jam();
ends.put(t);
}
} catch(Exception e) {
}
}
}
public class Eater implements Runnable{
private ToastQueue ends;
private int counter = 0;
public Eater(ToastQueue ends) {
this.ends = ends;
}
@Override
public void run() {
try {
while(!Thread.interrupted()) {
TimeUnit.MILLISECONDS.sleep(100);
Toast t = ends.take();
if (t.getId() != counter++ || t.getStatus()!= Toast.Status.JAMMED){
System.out.println("Error");
}
}
} catch(Exception e) {
}
}
}
public class SceneTest {
public static void main(String[] str) throws Exception{
ToastQueue dry = new ToastQueue();
ToastQueue buf = new ToastQueue();
ToastQueue ends = new ToastQueue();
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new Toaster(dry));
exec.execute(new Butter(dry,buf));
exec.execute(new Jammer(buf,ends));
exec.execute(new Eater(ends));
TimeUnit.SECONDS.sleep(5);
exec.shutdownNow();
}
}