Java多线程

创建任务和线程

  1,任务类实现implements接口,重写run方法;

public class TaskClass implements Runnable{
  public TaskClass(){
       // ...
    }
     public void run(){
    //...
    } 
}

  2, 使用Thread创建线程,Thread类包括创建线程的构造方法以及控制线程的方法;

TaskClass task new TaskClass();//创建任务
Thread thread = new Thread(task);//为任务创建线程

Thread类的方法  

Thread()                             //创建一个空线程 
Thread(Runnable task)     //为指定任务创建一个线程
void start()                         //启动线程,使得run()方法被调用
boolean isAlive(int p)        //j检测线程当前是否在运行
void setPriority()               //设置线程的优先级p (范围从1到10)
void join()                          //等到线程结束     例如:A线程调用B.join(),则A线程进入阻塞队列,等B线程运行完后,唤醒A线程
void sleep(long millis)      //使线程睡眠指定毫秒
void yield()                      //当前线程暂停,进入就绪队列,运行其他线程

线程池

通过Thread来创建线程对单一任务的执行很方便,但需要为每个任务创建一个线程,对应大量任务来说是不够搞效的。因此java提供Executor接口来执行线程池中的任务,提供ExecutorService接口来管理和控制任务。

1,使用Executors类中的静态方法创建Executor对象

newFixedThreadPool(int)//创建固定数目的线程,当线程完成了任务的执行,它可以被重新使用以执行另外一个任务。当所有线程都不是空闲状态,而且有任务在等待,那么在关机前,如果因为错误终止了一个线程,就会创建一个新线程代替它。

newCachedThreadPool()//当有线程在等待时就创建新的线程,缓冲池中的线程的线程60秒没有使用就终止它。

Executor类的方法

execute(Runnable object)//接受任务
 
ExecutorService接口的方法
void shutdown()  //关闭执行器,不接受新的任务,但要运行完已有的任务
list<Runnable> shutdownNow()   //关闭执行器,即使还有任务没完成任务,返回没完成的任务清单
boolean isShutdown()    //检测执行器是够已关闭
boolean isTerminated()    //如果线程池所有任务都被终止,则返回true
 1 import java.util.concurrent.*;
 2 
 3 class PrintChar implements Runnable{
 4     char c;
 5     int len;
 6     public PrintChar(char a,int length) {
 7         c=a;
 8         len=length;
 9     }
10     @Override
11     public void run() {
12         // TODO 自动生成的方法存根
13         for(int i=0;i<len;i++) {
14             System.out.print(c);
15         }
16     }
17     
18 }
19 
20 public class ExecutorDemo {
21     public static void main(String args[]) {
22 //        ExecutorService executor = Executors.newFixedThreadPool(1);
23         ExecutorService executor = Executors.newCachedThreadPool();
24         executor.execute(new PrintChar('a',100));
25         executor.execute(new PrintChar('b',100));
26         executor.execute(new PrintChar('c',100));
27         
28         executor.shutdown();
29     }
30 }
View Code

线程同步

 synchronized 同步的方法在执行前会隐式的加上一个锁:方法锁,对象锁 和 类锁

1,给方法加synchronized关键字

  1)给普通方法加:当运行到这个方法时,实际是给调用这个方法的对象加锁,其他线程调用这个对象的这个方法时就会阻塞;

  2)给静态方法加:当运行到这个方法时,实际是给调用这个方法的类加锁,其他线程调用这个类的这个方法时就会阻塞;

public synchronized void deposit(double amount)//当运行到这里调用这个方法的时候,实际上是给调用这个方法的对象加锁了
public synchronized static void deposit(double amount)//当运行到这里调用这个方法的时候,实际上是给这个方法的类加锁了

2, 给同步块加锁:可以给任何对象加锁,只会同步部分代码,而不必同步整个方法,大大增强了程序的并发能力;

  1)对调用这个方法的对象加锁

public void acount(){
  synchronized (this){//还可写其他对象的名字,表示对其他对象加锁
       ...... 
    }      
}

  2)给这个方法的类加锁

public void acount(){
  synchronized (acount.class){//方法.class  可以给这个方法的所属类加锁
       ...... 
    }      
}

java显式的加锁:显式加锁对线程的控制更加直观灵活,重要的是可以进行线程间的协作

 通过ReentrantLock类可以实现接口Lock来表示一个锁,ReentrantLock是创建相互排斥的锁的Lock的具体表现。

创建Lock锁,获取锁,释放锁

Lock lock =new ReentrantLock();//创建锁
lock.lock();//获取锁
lock.unlock();//释放锁

线程间协作

调用Lock对象的newCondition()方法创建的对象可以使用await(),signal(),signalAll()方法来实现线程之间的互相通信。

Lock lock = new ReentrantLock();
Condition newDeposit = lock.newCondition();
newDeposit.await();//当前线程等待,直到被signal()或signalAll()唤醒
newDeposit.signal();//唤醒一个等待线程
newDeposit.signalAll();//唤醒所以等待线程

示例学习:生产者/消费者

package test;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;

public class ConsumerProducer {
    public static Buffer buffer=new Buffer();
    public static void main(String args[]) {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        executor.execute(new ProducerTack());
        executor.execute(new ConsumerTask());
        executor.shutdown();
    }
    private static class ProducerTack implements Runnable{
        public ProducerTack() {
            // TODO 自动生成的构造函数存根
        }

        @Override
        public void run() {
            try {
                int i=1;
                while(true) {
                    System.out.println("Producer writes" + i);
                    buffer.write(i++);
                    Thread.sleep((int)(Math.random()*10000));
                }
            }catch(InterruptedException ex) {
                ex.printStackTrace();
            }
        }
        
    }

    private static class ConsumerTask implements Runnable{
        public ConsumerTask() {
            // TODO 自动生成的构造函数存根
        }

        public void run() {
            try {
                while(true) {
                    System.out.println("			Consumer reads " + buffer.read());
                    Thread.sleep((int)(Math.random()*10000));
                }
            }catch(InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }

    
    private static class Buffer{
        private static final int CAPACITY=1;
        private java.util.LinkedList<Integer> queue=new java.util.LinkedList<Integer>();
        
        private static Lock lock=new ReentrantLock();
        private static Condition notEmpty=lock.newCondition();
        private static Condition notFull = lock.newCondition();
        
        public Buffer() {
            // TODO 自动生成的构造函数存根
        }

        public void write(int value) {
            lock.lock();
            try {
                while(queue.size()==CAPACITY) {
                    System.out.println("Wait for notFull condition");
                    notFull.await();
                }
                queue.offer(value);
                notEmpty.signal();
            }
            catch(InterruptedException ex){
                ex.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
        
        public int read() {
            int value=0;
            lock.lock();
            try {
                while(queue.isEmpty()) {
                    System.out.println("			Wait for notEmpty condition");
                    notEmpty.await();
                }
                value=queue.remove();
                notFull.signal();
            }catch(InterruptedException ex) {
                ex.printStackTrace();
            }finally {
                lock.unlock();
            }
            return value;
            
        }
    }

}
ConsumerProducer.java

阻塞队列

java.util.concurrent包中提三个具体的阻塞队列ArrayBlockingQueue,LinkedBlockingQueue和PriorityBlockingQueue;

阻塞队列的方法:

put(element e)  //在队尾插入一个元素,如果队列已满则线程进入阻塞队列等待。

take()        //返回并删除这个队列的头,如果队列为空则线程进入阻塞队列等待。

LinkedBlockingQueue是可以创建不受限的或受限的链队,对于不受限队列而言,put方法将永远不会阻塞。

例子:用阻塞队列改写上面的生产者/消费者

package test;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;

public class ConsumerProducer {
//    public static Buffer buffer=new Buffer();
    private static ArrayBlockingQueue<Integer> buffer =new ArrayBlockingQueue<Integer>(1);
    public static void main(String args[]) {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        executor.execute(new ProducerTack());
        executor.execute(new ConsumerTask());
        executor.shutdown();
    }
    private static class ProducerTack implements Runnable{
        public ProducerTack() {
            // TODO 自动生成的构造函数存根
        }

        @Override
        public void run() {
            try {
                int i=1;
                while(true) {
                    buffer.put(i);
                    System.out.println("Producer writes" + i);
                    i++;
                    Thread.sleep((int)(Math.random()*1000));
                }
            }catch(InterruptedException ex) {
                ex.printStackTrace();
            }
        }
        
    }

    private static class ConsumerTask implements Runnable{
        public ConsumerTask() {
            // TODO 自动生成的构造函数存根
        }

        public void run() {
            try {
                while(true) {
                    System.out.println("			Consumer reads " + buffer.take());
                    Thread.sleep((int)(Math.random()*1000));
                }
            }catch(InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }
}
阻塞队列改写生产者/消费者

信号量Semaphore

信号量用来限制访问共享资源的线程数,在访问资源之前,线程必须从信号量获取许可,在访问完资源之后,这个线程必须将许可返回给信号量。

构造方法:Semaphore(int numberOfPermits int)  //创建一个带指定数目许可的信号量,公平策略为false

方法: void acquire()   //获取这个信号量的许可。如果无许可可用,线程就被锁住,直到有可用许可为止

    void release()    //释放一个许可给该信号量

避免死锁

给资源安排一个加锁顺序,就不会发生死锁的现象了

同步集合

Java集合框架中的类不是线程安全的,可以通过锁定集合和同步集合保护集合中数据。

Collections类提供六个静态方法来将集合转成同步版本,这些方法创建的集合成为同步包装类

方法:

Collection synchronizedCollection(Collection c) //返回同步集合

List synchronizedList(List list) //返回同步线性表

Map synchronizedMap(Map m) //返回同步图

Set synchronizedSet(Set s) //返回同步规则集

SortedMap synchronizedSortedMap(SortedMap s) //返回同步有序图

SortedSet synchronizedSortedSet(SortedSet c) //返回同步有序规则集

同步集合可以很安全地被多个线程并发的访问和修改

注意:如果用迭代器对集合进行遍历时,同时对集合进行修改,迭代器就会抛出异常java.util.ConcurrentModificationExceptione而结束;

为了避免这个错误,需要创建一个同步集合对象,并且在遍历它时获取对象上的锁。

Set hashSet = Collections.synchronizedSet(new HashSet());//同步集合
synchronized (hashSet){//加锁
    Iterator iterator = hashset.iterator();
    while(iterator.hasnext()){
         System.out.println(iterator.next());
    }  
}
原文地址:https://www.cnblogs.com/zdl2234/p/11210929.html