线程

线程的6种状态

1.New(可创建)
2.Runnable(可运行)
3.Blocked(被阻塞)
4.Waiting(等待)
5.Timed waiting(计时等待)
6.Terminated(被终止)

1.新创建线程:当用new 操作符创建一个新线程时,如New Thread(r),该线程还没有开始运行。这意味着它的状态是new。当一个线程处于新创建状态时,程序还没有开始运行线程中的代码。

2.可运行线程:一旦调用start方法,线程处于runnable状态。一个可运行的线程可能正在运行也可能没有运行,这取决于操作系统给线程提供运行的时间。
一旦一个线程开始运行。它不必始终保持运行。事实上,运行中的线程被中断,目的是为了让其他线程获得运行机会。

3.被阻塞线程和等待线程:当线程处于阻塞或等待状态时,它暂时不活动。它不运行任何代码且消耗最少的资源。直到线程调度器重新激活它。细节决定于它是怎么达到非活动状态的:

  • 当一个线程尝试获取一个对象的内部锁,而该锁被其他线程持有,则该线程进入阻塞状态。当所有其他线程释放该锁,并且线程调度器允许本线程持有它的时候,该线程变成非阻塞状态。

  • 当线程等待另一个线程通知调度器一个条件时,它自己进入等待状态。

  • 有几个方法有一个超时参数。调用它们导致线程进入计时等待状态。这一状态将一直保持到超时期满或者接受适当的通知。带有超时参数的方法有Thread.sleep 和Object.wait、Thread.join、Lock.tryLock以及Condition.await的计时版。

4.被终止的线程
线程因一下两个原因之一而被终止:

  • 因为run方法没有正常退出而自然死亡。
  • 因为一个没有捕获的异常终止了run方法而意外死亡。

中断线程

当对一个线程调用interrupt方法时,线程的中断状态将被置位。这是每一个线程都具有的boolean标志。每个线程都应该不时地检查这个标志,以判断线程是否被中断。
要想弄清中断状态是否被置位,首先调用静态的Thread.currentThread方法获得当前线程,然后调用isInterrupted方法。

while(!Thread.currentThread().isInterrupted()&& more work to do){
    do more work
}

但是,如果线程被阻塞,就无法检测中断状态。这是产生InerruptedException异常的地方。

线程优先级

每个线程都有一个优先级。默认情况下,一个线程继承它的父线程的优先级。可以用setPriority方法提高或降低任何一个线程的优先级。可以将优先级设置为在MIN_PRIORITY(在Thread类中定义为1)与MAX_PRIORITY(定义为10)之间的任何值。NORM_PRIORITY定义为5.

每当线程调度器有机会选择新线程时,它首先选择具有较高优先级的线程。

守护线程

可以通过调用
Thread.setDaemon(true);
将线程转换为守护线程。守护线程的用途是为其他线程提供服务。

Java多线程实现的三种方式

1、继承Thread类;
2、实现Runnable接口;
3、使用ExecutorService、Callable、Future实现有返回结果的多线程。

1.通过扩展Thread类来创建多线程

demo:

public class MyThread extends Thread {  

    private static int a=1;

    public void run() {  
        a++;
        System.out.println(a);
    }  


    public static void main(String[] args) {
        MyThread thread1=new MyThread();
        thread1.start();    

        MyThread thread2=new MyThread();
        thread2.start();    
    }
}  

2.通过实现Runnable接口来创建多线程

public class MyRunnable implements Runnable{

    private static int a=1;

    public void run() {
        a++;
        System.out.println(a);
    }

    public static void main(String[] args) {
        MyRunnable runnable1=new MyRunnable();
        //实例化一个Thread,并传入自己的Runnable实例
        Thread thread1=new Thread(runnable1);

        MyRunnable runnable2=new MyRunnable();
        Thread thread2=new Thread(runnable2);

        thread1.start();
        thread2.start();
    }
}

3.使用Callable、Future实现有返回结果的多线程

public class MyCallable implements Callable<Integer>{

    private static int a=1;

    public Integer call() {
        a++;
        System.out.println(a);
        return 1;
    }

    public static void main(String[] args) {
        MyCallable counter=new  MyCallable ();
        FutureTask<Integer> task=new FutureTask<Integer>(counter);
        //实例化一个Thread,并传入自己的FutureTask实例
        Thread thread=new Thread(task);
        thread.start();
    }
}

线程锁

用ReentrantLock保护代码块的demo:

class C {
        //锁对象
        private final ReentrantLock lock = new ReentrantLock();
        ......
        //保证线程安全方法
        public void method() {
            //上锁
            lock.lock();
            try {
                //保证线程安全操作代码
            } catch() {

            } finally {
                lock.unlock();//释放锁
            }
        }
    }
public sychronized  void method(){
    method body 
}

等价于加锁

demo:

public class Bank {
    private  static int[] amount={1000,1000};

    private final ReentrantLock lock = new ReentrantLock();

    public  void transfer(){
         //上锁
        lock.lock();
        try {
            amount[0]-=1000;
            amount[1]+=1000;

            System.out.println("totla="+(amount[0]+amount[1]));
        } finally {
            lock.unlock();//释放锁
        }
    } 
}

public class MyThread extends Thread {  

    private static int a=0;
    private static int b=0;

    private Bank bank;

    public MyThread(Bank bank){
        this.bank=bank;
    }

    public void run() {  
        this.bank.transfer();
    }  


    public static void main(String[] args) {
        Bank b=new Bank();
        for(int i=1;i<=10000;i++){
        MyThread thread=new MyThread(b);
        thread.start(); 
        }
    }
}  
线程条件对象:

一个锁对象可以有一个或多个相关的条件对象。可以用newCondition方法获得一个条件对象。

demo(设置一个条件对象来表达“余额充足”条件):

public class Bank {
    private Condition sufficentFunds;
    ....
    public Bank(){
        sufficentFunds=lock .newCondition();
   }

    public void transfer(){
        lock .lock();
        try{
            while(account(amount[0]-1000<0){
                //wait
                ...
            }
            //transfer fund
            ....
        }finally{
            lock.unlock();
        }
    }
}

如果transfer方法发现余额不足,它调用
sufficentFunds.await();
当前线程被阻塞,并且放弃了锁。
等待获得锁的线程和调用await()方法的线程存在本质的不同。一旦一个线程调用await方法,它进入该条件的等待集。当锁可用时,该线程不能马上解除阻塞。必须另一个线程调用signallAll()方法时为止。

这里写图片描述

线程等待与激活demo:
public class Bank {
    private  static int[] amount={1000,1000};

    private final ReentrantLock lock = new ReentrantLock();

    public  synchronized void transfer() throws InterruptedException{
            Random random=new Random();
            int i=random.nextInt(2);
            int j=random.nextInt(2);

            while(amount[i]<1000){
                wait(); //当余额不足时,该线程进入等待状态,必须另一线程调用notify方法激活才能解除阻塞
            }
            amount[i]-=1000;
            amount[j]+=1000;

            notifyAll();//解除所有等待线程的阻塞,以便这些线程通过竞争实现对对象的访问
            System.out.println("totla="+(amount[0]+amount[1]));
    } 
}

public class MyThread extends Thread {  

    private static int a=0;
    private static int b=0;

    private Bank bank;

    public MyThread(Bank bank){
        this.bank=bank;
    }

    public void run() {  
        try {
            this.bank.transfer();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }  


    public static void main(String[] args) {
        Bank b=new Bank();
        for(int i=1;i<=10000;i++){
        MyThread thread=new MyThread(b);
        thread.start();
        }
    }
}  
同步阻塞:

每个java对象有一个锁。线程获得锁有两种方法:
1.通过调用同步方法获得锁
2.通过进入一个同步阻塞获得锁,demo:

synchronized(obj){
    cirtical section
}
获得obj对象的锁

public class Bank{
    private double[] accounts;
    private Object lock=new Object();
    ...
    public void transfer(){
        synchronized(lock){
            ...
        }
    }
}
Volatile关键字:

有时,仅仅为了读写一个或俩个实例就使用同步,显得开销过大了。volatile关键字为实例域的同步访问提供了一种免锁机制。如果声明一个域为volatile,那么编译器和虚拟机就知道该域是可能被另一个线程并发更新的。

死锁:

所有线程都被阻塞。

读写锁:

ReentrantLock类和ReentrantReadWriteLock类

下面是使用读/写锁的必要步骤:
1. 构造一个ReentrantReadWriteLock 对象

private ReentrantReadWriteLock rwl=new ReentrantReadWriteLock ();

2.抽取读锁和写锁

 private Lock readLock=rwl.readLock();
 private Lock writeLock=rwl.writeLock();

3.对所有的获取方法加读锁:

public void get(){
    readLock.lock();
    try{
    ....
    }finally{
    readLock.unlock();
     }
}

4.对所有的修改方法加读锁:

public void modify(){
    writeLock.lock();
    try{
    ....
    }finally{
    readLock.unlock();
     }
}
阻塞队列:

在协调多个线程之间的合作时,阻塞队列是一个有用的工具。工作者线程可以周期性将中间结果存储在阻塞队列中。其他的工作者线程移出中间结果并进一步加以修改。

这里写图片描述

java.until.concurrent提供了阻塞队列的几个变种:
1.LinkedBlockingQueue (双端,容量没有上限,但可以指定容量)
2.ArrayBlockingQueue (在构造时需要指定容量,并且有一个可选参数来指定是否需要公平性)
3.PriorityBlockingQueue(带优先级的队列)

demo:

public class BlockingQueueTest {
    public static void main(String[] args) {
        Scanner in=new Scanner(System.in);
        System.out.print("Enter 文件路径:");
        String directory=in.nextLine();
        System.out.print("Enter 关键字:");
        String keyword=in.nextLine();

        final int FILE_QUEUE_SIZE=10;
        final int SEARCH_THREADS=100;

        BlockingQueue<File> queue=new ArrayBlockingQueue<File>(FILE_QUEUE_SIZE);

        FileEnumerationTask enumeration=new FileEnumerationTask(queue, new File(directory));
        new Thread(enumeration).start();
        for(int i=1;i<SEARCH_THREADS;i++){
            new Thread(new SearchTask(queue, keyword)).start();
        }


    }
}

public class FileEnumerationTask implements Runnable {

    public static File DUMMY=new File("");
    private BlockingQueue<File> queue;
    private File startingDirectory;



    public FileEnumerationTask(BlockingQueue<File> queue, File startingDirectory) {
        super();
        this.queue = queue;
        this.startingDirectory = startingDirectory;
    }



    @Override
    public void run() {
        try {
            enumerate(startingDirectory);
            queue.put(DUMMY);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    public void enumerate(File directory) throws InterruptedException{
        File[] files= directory.listFiles();
        for(File f:files){
            if(f.isDirectory()) enumerate(f);
            else queue.put(f);
        }
    }
}

public class SearchTask implements Runnable{

    private BlockingQueue<File> queue;
    private String keyword;


    public SearchTask(BlockingQueue<File> queue, String keyword) {
        super();
        this.queue = queue;
        this.keyword = keyword;
    }


    @Override
    public void run() {
        try {   
            boolean done=false; 
            while(!done){
                File file = queue.take();
                if(file==FileEnumerationTask.DUMMY){
                    queue.put(file);
                    done=true;
                }
                else
                    search(file);
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void search(File file){
        try {
            Scanner in=new Scanner(file);
            int lineNumber=0;
            while(in.hasNextLine()){
                lineNumber++;
                String line=in.nextLine();
                if(line.contains(keyword)){
                    System.out.printf("%s:%d:%s%n",file.getPath(),lineNumber,line);
                }
            }
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
线程安全的集合

java.until.concurrent包提供了映射表、有序集和队列的高效实现:
ConcurreentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet和ConcurrentLinkedQueue

从Java的初始版本开始,V和HashTable类就提供了线程安全的动态数组和散列表的实现。现在这些类被弃用,取而代之的是ArrayList和HashMap类。这些类不是线程安全的,而集合库中提供了不同的机制,任何集合类通过使用同步包装器变成线程安全的:

List<E> synchAttayList=Collections.synchronizedList(new ArrayList<E>());

Map<K,V> synchHashMap=Collections.synchronizedMap(new HashMa<K,V>());

线程池

执行器(Executor)类有许多静态工厂方法用来构建线程池,

这里写图片描述

推荐:http://blog.csdn.net/hsuxu/article/details/8985931

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值