java.util.concurrent包及其子包提供了一系列工具,更好,更方便得使用线程
下面介绍几个实用的类 单变量,集合,Timer,线程池
原子变量
线程在执行任务的时候,需要些同步控制,但自己的可能有问题,在java.util.concurrent.atomic包中,提供了一AtomicInteger类,原子类,这个类在线程访问的时候是安全的,不会在执行到一般 别的线程参合进来执行。
下面这个例子中,我们先创建了1000个线程
每个线程对i++ 同时对比原子整数的++
结果表明 原子变量是线程安全的
结果:
999 false
1000 true
import java.util.concurrent.atomic.AtomicInteger;
class AtomicIntegerDemo
{
static int n=0;
static AtomicInteger cnt=new AtomicInteger(0);
public static void main(String[] args)
{
final int NUM=1000;
Thread[] threads=new Thread[NUM];
for(int i=0;i<NUM;i++) {
threads[i]=new Thread() {
public void run() {
n++;
cnt.getAndIncrement();//Add 1 在线程中+1 线程安全
}
};
}
for(int i=0;i<NUM;i++)threads[i].start();
try {Thread.sleep(3000);}
catch(InterruptedException ex) {}
System.out.printf("%d %b\n",n,n==NUM);
System.out.printf("%d %b\n",cnt.get(),cnt.get()==NUM);
}
}
1.5以前 ArrayList HashMap不是线程安全的
vector 以及 Hashtable是线程安全的
下面这些都是线程安全的
Collections.synchronizedArrayList(list)
java.util.concurrent中 CopyOnWriteArrayList CopyOnWriteArraySet 是线程安全的类
ConcurrentHashMap pubIfAbsent() remove() replace()
ArrayBlockingQueue 可以实现生产者消费者模型 是应用put() take()
下面看一些线程安全的方法写的例子 结果 从结果上来看 彼此的叠加是互不干扰的
Prodece 0.
Consume 0.
Prodece 1.
Consume 1.
Prodece 2.
Prodece 3.
Consume 2.
Prodece 4.
Prodece 5.
Consume 3.
Consume 4.
Consume 5.
import java.util.concurrent.*;
class Producer implements Runnable{
private BlockingQueue<Integer>queue;
public Producer(BlockingQueue<Integer>queue) {
this.queue=queue;
}
public void run() {
for(int i=0;i<=5;i++) {
try {
Thread.sleep((int)(Math.random()*10));
queue.put(i);//用put take 的时候就不用底层去写 wati notify等方法
System.out.println("Prodece "+i+".");
}catch(InterruptedException ex) {}
}
}
}
class Consumer implements Runnable{
private BlockingQueue<Integer>queue;
public Consumer(BlockingQueue<Integer>queue) {
this.queue=queue;
}
public void run() {
for(int i=0;i<=5;i++) {
try {
Thread.sleep((int)(Math.random()*20));
Integer product =queue.take();
System.out.println("Consume "+product+".");
}catch(InterruptedException ex) {}
}
}
}
class BlockingQueueDemo{
public static void main(String[] args) {
BlockingQueue<Integer>queue=new ArrayBlockingQueue<>(3);
new Thread(new Producer(queue)).start();
new Thread(new Consumer(queue)).start();
}
}
线程池
线程池可以简单理解为 同时开好多个线程 下次再有别的任务的时候,这个线程没有销毁掉,新的任务还是用这个线程去执行
所以 开一定数量的线程放到那里 称为线程池。
线程池的类
ExecutorService接口 ThreadPlloExecutor类 Executors工具类
常见的用法
ExecutorService pool=Executors.newCachedThreadPool();创建线程池
使用其execute(Runnabler)方法
结果 :0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 21 22 25 26 27 28 29 30 31 32 23 24 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 end
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 end
64 65 66 67 68 69 end
代码
import java.util.concurrent.*;
class ThreadPoolDemo{
public static void main(String[]args) {
ExecutorService pool=Executors.newCachedThreadPool();//创建线程池
MyTask t1=new MyTask(50);//这些runnable对象就是执行run
MyTask t2=new MyTask(70);
MyTask t3=new MyTask(80);//多个线程公用一定数量的线程
//假设有几百个任务 并不需要开几百个线程
pool.execute(t1);//执行第一个任务
pool.execute(t2);//执行第二个任务
pool.execute(t3);//执行第三个任务
pool.shutdown();
}
}
class MyTask implements Runnable{
int n=10;
public MyTask(int n) {this.n=n;}
public void run() {
for(int i=0;i<n;i++)System.out.print(i+" ");
System.out.println("end");
}
}
上面这个例子说明了线程池的好处 没有建立线程 创建了一个线程池 然后把任务定义 用线程池来执行任务 输出中是乱序的 也体现了“并发"的意味
Timer
有两个timer java.util.Timer类 javax.swing.Timer类
它底层是线程,用线程循环的执行任务
import java.util.*;
class TimerTest{
public static void main(String[] args) {
Timer timer=new Timer("display");
TimerTask task=new MyTask();
timer.schedule(task,1000,1000);//
//每隔一定时间执行任务 这个任务是 task
}
}
class MyTask extends TimerTask{
int n=0;
public void run() {
n++;
System.out.println(new Date());
System.out.println("---"+n);
}
}