java多线程(一)

来源:互联网 发布:淘宝卖家后台功能介绍 编辑:程序博客网 时间:2024/06/17 23:41

一直想写博客,但总是想做到完美,结果就一直拖着。先试着写一篇吧,记录下看过的一些东西,对以后应该会有些帮助吧!

学过java的人应该都会知道,实现java多线程一般是两种方式:

1.继承Thread类,重写父类run方法;

2.实现Runnable接口。

下面是Thread类中的run方法(其中target是一个Runnable对象)

public void run() {if (target != null) {    target.run();}    }
可以看出,如果Thread的run方法就是判断是否传入了Runnable对象,如果传入,则直接调用Runnable接口的run方法(当然这个抽象方法肯定是要我们重写的),如果没有传入,则不进行任何操作。

守护线程:

守护线程的优先级很低,一般是没有其他线程运行的时候,守护线程才会运行。守护线程一般是无限循环的,以等待服务请求或者执行线程的任务,最典型的例子就是java的gc。

import java.util.Date;import java.util.Deque;public class CleanerTask extends Thread{private Deque<Event> deque;public CleanerTask(Deque<Event> deque) {this.deque = deque;setDaemon(true);}@Overridepublic void run() {while(true){Date date = new Date();clean(date);}}private void clean(Date date) {long diff;boolean delete;if(deque.size() == 0){return;}delete = false;do{Event e = deque.getLast();diff = date.getTime() - e.getDate().getTime();if(diff > 10000){System.out.println("delete "+ e.getEvent());deque.removeLast();delete = true;break;}}while(diff > 10000);if(delete){System.out.println("deque size : "+deque.size());}}}
守护线程的小例子,超过循环清除超过10s的事件。


synchronized

对于了解过java并发的人来说,synchronized真是再熟悉不过了,synchronized关键字用来控制一个方法或一段代码的并发访问,如果一段代码使用了synchronized声明,那么只能有一个线程访问它。每一个用synchronized关键字声明的方法都是临界区,在java中,同一个对象的临界区,在同一时间内只有一个允许被访问。此外需要注意的是,两个线程可以同时访问一个对象不同的synchronized方法,其中一个是静态方法,另一个是非静态方法,如果两个方法修改了相同的变量, 那么就有可能出现数据不一致的情况。

public class Account {private double balance;public synchronized void addAmount(double amount){double tmp = balance;try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}tmp += amount;balance = tmp;}public synchronized void subAmount(double amount){double tmp = balance;try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}tmp -= amount;balance = tmp;}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}}
上段代码模拟银行存款和取款的过程,其中存取的方法上都加上了synchronized关键字,保证存取款的结果正确。


在synchronized中,可以调用wait()、notify()、notifyAll()方法,wait方法被一个线程调用时,jvm将这个线程置入休眠,并且释放控制这个同步代码块的对象,同事允许其他线程执行这个对象控制的其他同步代码块。而为了唤醒这个线程,必须在这个对象控制的某个同步代码块中调用notify()或notifyAll()方法。

import java.util.Date;import java.util.LinkedList;import java.util.List;public class EventStorage {private int maxSize;private List<Date> storage;public EventStorage() {maxSize = 10;storage = new LinkedList<Date>();}public synchronized void set(){while(storage.size() == maxSize){try {wait();} catch (InterruptedException e) {e.printStackTrace();}}storage.add(new Date());System.out.println("Set : "+storage.size());notifyAll();}public synchronized void get(){while(storage.size() == 0){try {wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("Get : "+storage.size()+" : "+((LinkedList<Date>)storage).poll());notifyAll();}}
这段代码模拟了生产者-消费者中的缓冲区,当缓冲区容量已满时,线程将会休眠,当检测到缓冲区有空间时,又会唤醒所有在休眠中的线程,同样的,当缓冲区为空时,调用get()方法的线程也会休眠,缓冲区不为空时,重新被唤醒执行。

Lock

作为同步代码的另一种机制,Lock比synchronized更强大、更灵活。Lock接口允许分离读写操作,允许多个读线程和一个写线程,而且具有更好的性能。

读写锁是Lock机制最大的特点,使用读操作锁时,可以允许多个线程同时访问,但是使用写操作锁时,只允许一个线程运行。

import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantReadWriteLock;public class PricesInfo {private double price1;private double price2;private ReadWriteLock lock;public PricesInfo() {price1 = 1.0;price2 = 2.0;lock = new ReentrantReadWriteLock();}public double getPrice1(){lock.readLock().lock();double value = price1;lock.readLock().unlock();return value;}public double getPrice2(){lock.readLock().lock();double value = price2;lock.readLock().unlock();return value;}public void setPrices(double price1,double price2){lock.writeLock().lock();this.price1 = price1;this.price2 = price2;lock.writeLock().unlock();}}
这是一个获取和改变price的类,获取price时使用了读锁,改变price时使用了写锁。


ReentrantLock和ReentrantReadWriteLock类的构造器都含有一个布尔参数fair,默认值为false,即非公平模式,锁在选择一个等待的线程时,没有任何约束。当设置为true时,锁将选择等待时间最长的线程。

import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class PrintQueue {private Lock queueLock = new ReentrantLock(true);public void printJob(Object document){queueLock.lock();try{Long duration = (long)(Math.random()*10000);System.out.println(Thread.currentThread().getName()+":PrintQueue: Printing a Job during "+(duration/1000)+" seconds");Thread.sleep(duration);}catch(Exception e){e.printStackTrace();}finally{queueLock.unlock();}queueLock.lock();try{Long duration = (long)(Math.random()*10000);System.out.println(Thread.currentThread().getName()+":PrintQueue: Printing a Job during "+(duration/1000)+" seconds");Thread.sleep(duration);}catch(Exception e){e.printStackTrace();}finally{queueLock.unlock();}}}
public class Job implements Runnable{private PrintQueue printQueue;public Job(PrintQueue printQueue) {super();this.printQueue = printQueue;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+": Going to print a document");printQueue.printJob(new Object());System.out.println(Thread.currentThread().getName()+": The document has been printed");}}
public class Main {public static void main(String[] args) {PrintQueue printQueue = new PrintQueue();Thread thread[] = new Thread[10];for(int i=0; i<10; i++){thread[i] = new Thread(new Job(printQueue),"Thread "+ i);}for(int i=0; i<10; i++){thread[i].start();try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}}
console输出:
Thread 0: Going to print a documentThread 0:PrintQueue: Printing a Job during 3 secondsThread 1: Going to print a documentThread 2: Going to print a documentThread 3: Going to print a documentThread 4: Going to print a documentThread 5: Going to print a documentThread 6: Going to print a documentThread 7: Going to print a documentThread 8: Going to print a documentThread 9: Going to print a documentThread 1:PrintQueue: Printing a Job during 9 secondsThread 2:PrintQueue: Printing a Job during 8 secondsThread 3:PrintQueue: Printing a Job during 0 secondsThread 4:PrintQueue: Printing a Job during 9 secondsThread 5:PrintQueue: Printing a Job during 9 secondsThread 6:PrintQueue: Printing a Job during 2 secondsThread 7:PrintQueue: Printing a Job during 9 secondsThread 8:PrintQueue: Printing a Job during 1 secondsThread 9:PrintQueue: Printing a Job during 5 secondsThread 0:PrintQueue: Printing a Job during 6 secondsThread 0: The document has been printedThread 1:PrintQueue: Printing a Job during 9 secondsThread 1: The document has been printedThread 2:PrintQueue: Printing a Job during 8 secondsThread 2: The document has been printedThread 3:PrintQueue: Printing a Job during 3 secondsThread 3: The document has been printedThread 4:PrintQueue: Printing a Job during 8 seconds
可以看出,锁获取线程是按创建的顺序来的,即公平模式。

Condition这个类很强大,提供了挂起线程和唤醒线程的机制,可以为多个线程建立不同的Condition

import java.util.LinkedList;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;public class Buffer {private LinkedList<String> buffer;private int maxSize;private ReentrantLock lock;private Condition lines;private Condition space;private boolean pendingLines;public Buffer(int maxSize){this.maxSize = maxSize;buffer = new LinkedList<String>();lock = new ReentrantLock();lines = lock.newCondition();space = lock.newCondition();pendingLines = true;}public void insert(String line){lock.lock();try {while(buffer.size() == maxSize){space.await();}buffer.offer(line);System.out.println(Thread.currentThread().getName()+":Inserted Line:"+buffer.size());lines.signalAll();} catch (InterruptedException e) {e.printStackTrace();}finally{lock.unlock();}}public String get(){String line = null;lock.lock();try {while((buffer.size() == 0) && (hasPendingLines())){lines.await();}if(hasPendingLines()){line = buffer.poll();System.out.println(Thread.currentThread().getName()+":Line Readed:"+buffer.size());space.signalAll();}} catch (InterruptedException e) {e.printStackTrace();}finally{lock.unlock();}return line;}public void setPendingLines(boolean pendingLines){this.pendingLines = pendingLines;}public boolean hasPendingLines() {return pendingLines || buffer.size() > 0;}}
这个缓冲区同样有存取的方法,初始化时创建了读写Condition,insert方法里面,如果缓冲区已满,写线程将会休眠,而此时将会唤醒读线程,同样的如果缓冲区为空,读线程将会休眠,写线程将会被唤醒。





0 0
原创粉丝点击