创建线程的两个方法
public class ThreadTest1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
//方法一,创建一个Thread的子类,覆盖run方法
Thread thread = new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
};
thread.start();
//方法二,用带参数的构造函数传进去一个Runnable接口
//所谓runnable接口就是实现了run方法的一个接口,自己写
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
});
thread2.start();
}
}
Thread中run()的源码
private Runnable target;
public void run() {
if (target != null) {
target.run();
}
}
Thread的应用:计时器
public class ThreadTimer {
public static void main(String[] args) {
// TODO Auto-generated method stub
//10s后触发incident
new Timer().schedule(new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("incident");
}
}, 10000);
//10s后触发incident,之后每隔2s触发一次。
new Timer().schedule(new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("incident");
}
}, 10000, 2000);
}
}
//其中 TimeTask( )是实现了Runnable接口的一个类
锁:线程互斥
开辟两个线程调用同一个对象的同一个方法。
public class SynchronizedTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Outer out = new Outer();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
out.output();
}
});
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
out.output();
}
});
}
}
方法一:
class Outer {
public synchronized void output() {
System.out.println();
}
}
方法二:
class Outer {
public void output() {
synchronized (this) {
System.out.println();
}
}
}
两个线程争抢的必须是同一把锁,若方法是静态方法
不能用this,可以用类名.class
操作线程,Example
//子线程循环10遍,主线程循环100遍,然后如此循环50遍。
public class InterviewQuestion {
public static void main(String[] args) {
// TODO Auto-generated method stub
Business business = new Business();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
for(int i = 0;i<=50;i++) {
business.sub();
}
}
}).start();
for(int i =1;i<=50;i++) {
business.main();
}
}
}
class Business{
boolean flag = true;
public synchronized void sub() {
if(!flag)
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for(int i =1;i<=10;i++) {
System.out.println("sub: "+i);
}
flag = false;
this.notify();
}
public synchronized void main() {
if(flag)
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for(int i=1;i<=100;i++) {
System.out.println("main "+i);
}
flag = true;
this.notify();
}
}
注意:把上述if改为while之后,线程被唤醒之后会再次检查flag以确定是否真的轮到了自己工作。
ThreadLocal实现线程范围的共享变量
即实现同一个线程访问的是对象中相同的数据。
线程1和线程2同时访问和修改类中的数据,要保证线程之间的数据互不影响
其底层就是一个Map容器,即Map<Thread,data>,同一个线程绑定同样的数据
public class ThreadLocalTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
for(int i =1;i<=2;i++) {
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
int data = new Random().nextInt(100);
ThreadData.getThreadDataInstance().setName("suli");
ThreadData.getThreadDataInstance().setAge(data);
new A().sop();
new B().sop();
}
}).start();
}
}
static class A{
public void sop() {
// TODO Auto-generated method stub
ThreadData my = ThreadData.getThreadDataInstance();
System.out.println(Thread.currentThread().getName()+" "+"A"+my.getName()+" " +my.getAge());
}
}
static class B{
public void sop() {
// TODO Auto-generated method stub
ThreadData my = ThreadData.getThreadDataInstance();
System.out.println(Thread.currentThread().getName()+" "+"B"+my.getName()+" "+my.getAge());
}
}
}
class ThreadData{
private ThreadData() {
}
private static ThreadLocal<ThreadData> map = new ThreadLocal<ThreadData>();
public static ThreadData getThreadDataInstance() {
ThreadData instance = map.get();
if(instance == null) {
instance = new ThreadData();
map.set(instance);
}
return instance;
}
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
多个线程实现数据共享
public class Interview2 {
final static Ticket ti = new Ticket();
public static void main(String[] args) {
// TODO Auto-generated method stub
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
ti.inc();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
ti.dec();
}
}).start();
}
}
class Ticket{
int data = 100;
public synchronized void inc() {
data++;
}
public synchronized void dec() {
data--;
}
}
线程池
线程池依靠调用Executors类的静态方法实现
1、创建固定大小的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(int num);
2、创建缓存线程池//线程数量随需求变化
ExecutorService threadPool = Executors.newCachedThreadPool();
3、创建单一线程池//保证始终有一个线程
ExecutorService threadPool = Executors.newSingleThreadExecutor();
线程关闭方法:
1、threadPool.shutdown();
等待线程任务结束后,关闭该线程。
2、threadPool.shutdownNow();
立刻关闭所有线程,不管任务完成没。
利用线程池创建多个定时器
1、固定时间后触发定时器
Executors.newScheduledThreadPool(3).schedule(Runnable command,long delay,TimeUnit unit)
2、固定时间后触发定时器,之后每隔特定时间触发一次
Executors.newScheduledThreadPool(3).scheduleAtFixedRate(Runnable command,
long delay,TimeUnit unit)
//Ecanple,创建线程池,之后为线程池分配任务,之后关闭闲置的线程
public class ThreadPoolTest {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(3);
for(int i = 0;i<10;i++) {
final int num = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
//dosomething();
System.out.println(Thread.currentThread().getName()+" "+"task"+num);
}
});
}
// TODO Auto-generated method stub
Executors.newScheduledThreadPool(3).schedule(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("incident");
}
}, 1000,TimeUnit.MILLISECONDS);
Executors.newScheduledThreadPool(3).scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("incident");
}
}, 1000,2000,TimeUnit.MILLISECONDS);
}
}
需要线程返回结果时:Callable和Future
1、Future取得的结果类型和Callable返回的结果类型必须一致,这是通过泛型来实现的。
2、Callable要采用ExecutorSevice的submit方法提交,返回的future对象可以取消任务。
3、CompletionService用于提交一组Callable任务,其take方法返回已完成的一个Callable任务对应的Future对象。
public static void main(String[] args) {
// TODO Auto-generated method stub
ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(threadPool2);
Future<Integer> future = completionService.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
// TODO Auto-generated method stub
return 666;
}
});
Future<Integer> fu = completionService.take();
System.out.println(fu.get());
}
Lock和Condition以及读写锁
1、Lock接口,可以替代同步代码块或者同步函数,将同步的隐式锁变为显式。
2、Conditon接口,可以替代Object中的wait(),notify(),notifyall()方法
将这些监视器方法单独进行了封装。
组织关系
Lock接口,其实现子类为 ReentrantLock,包含方法newCondition();
ReadWriteLock接口,其实现子类为ReentrantReadWriteLock;
获取读写锁时候,可以调用newCondition()方法。
await()-->wait();
signal()-->notify();
signalall()-->notifyall();
wait和sleep的区别?
1、wait可以指定时间也可以不指定时间,是绑定在对象上的方法。
sleep必须指定时间,是绑定在线程上的方法。
2、wait:释放执行权,释放锁。
sleep:释放执行权,不释放锁。
Example,线程一拿到锁,con1.await(),释放执行权,释放锁。
线程二拿到锁,执行输出,con1.notify(),激活线程一,释放锁。
public class LockTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Myoutput output = new Myoutput();
Lock lock = output.lock;
Condition con1 = output.con1;
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
output.output("aaaaaa");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
output.output2("bbbbbb");
}
}).start();
}
}
class Myoutput{
public Lock lock = new ReentrantLock();
public Condition con1 = lock.newCondition();
public void output(String str) {
lock.lock();
try {
con1.await();
System.out.println(str);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lock.unlock();
}
public void output2(String str) {
lock.lock();
System.out.println(str);
con1.signal();
lock.unlock();
}
}
例如,假设我们有一个有限的缓冲区,它支持put和take方法。 如果在一个空的缓冲区尝试
一个take ,则线程将阻塞直到一个项目可用; 如果put试图在一个完整的缓冲区,那么线程
将阻塞,直到空间变得可用。 我们希望在单独的等待集中等待put线程和take线程,以便我
们可以在缓冲区中的项目或空间可用的时候使用仅通知单个线程的优化。 这可以使用两个
Condition实例来实现。
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock(); try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally { lock.unlock(); }
}
public Object take() throws InterruptedException {
lock.lock(); try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally { lock.unlock(); }
}
}
1、读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥.
2、如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;
如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。
总之,读的时候上读锁,写的时候上写锁!
例子
public class ReadWriteLockTest {
public static void main(String[] args) {
final Queue3 q3 = new Queue3();
for(int i=0;i<3;i++)
{
new Thread(){
public void run(){
while(true){
q3.get();
}
}
}.start();
new Thread(){
public void run(){
while(true){
q3.put(new Random().nextInt(10000));
}
}
}.start();
}
}
}
class Queue3{
private Object data = null;//共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。
ReadWriteLock rwl = new ReentrantReadWriteLock();
public void get(){
rwl.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " be ready to read data!");
Thread.sleep((long)(Math.random()*1000));
System.out.println(Thread.currentThread().getName() + "have read data :" + data);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
rwl.readLock().unlock();
}
}
public void put(Object data){
rwl.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " be ready to write data!");
Thread.sleep((long)(Math.random()*1000));
this.data = data;
System.out.println(Thread.currentThread().getName() + " have write data: " + data);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
rwl.writeLock().unlock();
}
}
}
//ReentrantReadWriteLock类中一个关于缓存的例子
class CachedData {
Object data;
boolean cacheValid;
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// Must release read lock before acquiring write lock
rwl.readLock().unlock();
rwl.writeLock().lock();
try {
// Recheck state because another thread might have
// acquired write lock and changed state before we did.
if (!cacheValid) {
data = ...
cacheValid = true;
}
// Downgrade by acquiring read lock before releasing write lock
rwl.readLock().lock();
} finally {
rwl.writeLock().unlock(); // Unlock write, still hold read
}
}
try {
use(data);
} finally {
rwl.readLock().unlock();
}
}
}}</pre>
Semaphore实现信号灯
1、Semaphore可以维护当前访问自身的线程个数,并提供了同步机制。
使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。
1)、Semaphore实现的功能就类似厕所有5个坑,假如有十个人要上厕所,那么同时能
有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中的任何一个人让开后,其中
在等待的另外5个人中又有一个可以占用了。
2)、另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得
机会,这取决于构造Semaphore对象时传入的参数选项。
2、单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”
,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。
public class SemaphoreTest {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final Semaphore sp = new Semaphore(3);
for(int i=0;i<10;i++){
Runnable runnable = new Runnable(){
public void run(){
try {
sp.acquire();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() +
"进入,当前已有" + (3-sp.availablePermits()) + "个并发");
try {
Thread.sleep((long)(Math.random()*10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() +
"即将离开");
sp.release();
//下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元
System.out.println("线程" + Thread.currentThread().getName() +
"已离开,当前已有" + (3-sp.availablePermits()) + "个并发");
}
};
service.execute(runnable);
}
}
}
线程的一些常见工具类
1、Exchange
用于实现两个人之间的数据交换,每个人在完成一定的事务后想与对方交换数据,
第一个先拿出数据的人将一直等待第二个人拿着数据到来时,才能彼此交换数据。
Exanple
public class ExchangerTest {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final Exchanger exchanger = new Exchanger();
service.execute(new Runnable(){
public void run() {
try {
String data1 = "zxx";
System.out.println("线程" + Thread.currentThread().getName() +
"正在把数据" + data1 +"换出去");
Thread.sleep((long)(Math.random()*10000));
String data2 = (String)exchanger.exchange(data1);
System.out.println("线程" + Thread.currentThread().getName() +
"换回的数据为" + data2);
}catch(Exception e){
}
}
});
service.execute(new Runnable(){
public void run() {
try {
String data1 = "lhm";
System.out.println("线程" + Thread.currentThread().getName() +
"正在把数据" + data1 +"换出去");
Thread.sleep((long)(Math.random()*10000));
String data2 = (String)exchanger.exchange(data1);
System.out.println("线程" + Thread.currentThread().getName() +
"换回的数据为" + data2);
}catch(Exception e){
}
}
});
}
}
2、CyclicBarrier
表示大家彼此等待,大家集合好后才开始出发
Example
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final CyclicBarrier cb = new CyclicBarrier(3);
for(int i=0;i<3;i++){
Runnable runnable = new Runnable(){
public void run(){
try {
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() +
"即将到达集合地点1,当前已有" + (cb.getNumberWaiting()+1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));
cb.await();
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() +
"即将到达集合地点2,当前已有" + (cb.getNumberWaiting()+1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));
cb.await();
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() +
"即将到达集合地点3,当前已有" + (cb.getNumberWaiting() + 1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));
cb.await();
} catch (Exception e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
service.shutdown();
}
3、CountDownLatch
犹如倒计时计数器,调用CountDownLatch对象的countDown方法就将计数器减1,当计数到达0时,则所有等待者或单个等待者开始执行。
Example
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final CountDownLatch cdOrder = new CountDownLatch(1);
final CountDownLatch cdAnswer = new CountDownLatch(3);
for(int i=0;i<3;i++){
Runnable runnable = new Runnable(){
public void run(){
try {
System.out.println("线程" + Thread.currentThread().getName() +
"正准备接受命令");
cdOrder.await();
System.out.println("线程" + Thread.currentThread().getName() +
"已接受命令");
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() +
"回应命令处理结果");
cdAnswer.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
try {
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() +
"即将发布命令");
cdOrder.countDown();
System.out.println("线程" + Thread.currentThread().getName() +
"已发送命令,正在等待结果");
cdAnswer.await();
System.out.println("线程" + Thread.currentThread().getName() +
"已收到所有响应结果");
} catch (Exception e) {
e.printStackTrace();
}
service.shutdown();
}
阻塞队列
1、接口 BlockingDeque<E> ,在java.util.concurrent包中
其父类接口包括:Collection<E>, Iterable<E>, Queue<E>
2、已知实现的子类:ArrayBlockingQueue, DelayQueue, LinkedBlockingDeque,
LinkedBlockingQueue, PriorityBlockingQueue, SynchronousQueue
public static void main(String[] args) {
final BlockingQueue queue = new ArrayBlockingQueue(3);
for(int i=0;i<2;i++){
new Thread(){
public void run(){
while(true){
try {
Thread.sleep((long)(Math.random()*1000));
System.out.println(Thread.currentThread().getName()
+ "准备放数据!");
queue.put(1);
System.out.println(Thread.currentThread().getName()
+ "已经放了数据," +
"队列目前有" + queue.size()
+ "个数据");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
new Thread(){
public void run(){
while(true){
try {
//将此处的睡眠时间分别改为100和1000,观察运行结果
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()
+ "准备取数据!");
queue.take();
System.out.println(Thread.currentThread().getName()
+ "已经取走数据," +
"队列目前有" + queue.size()
+ "个数据");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
并发集合工具类
1、传统集合类在并发访问时,容易出现问题,线程不安全。
2、传统方式下用Collections工具类提供的synchronizedCollection方法来获得同步集合。
Collection c = Collections.synchronizedCollection(coll);
源代码是new了一个实现了collection的类,每次调用集合方法时,前面加上同步语句。
static class SynchronizedCollection<E> implements Collection<E>, Serializable {
private static final long serialVersionUID = 3053995032091335093L;
final Collection<E> c; // Backing Collection
final Object mutex; // Object on which to synchronize
SynchronizedCollection(Collection<E> c) {
this.c = Objects.requireNonNull(c);
mutex = this;
}
SynchronizedCollection(Collection<E> c, Object mutex) {
this.c = Objects.requireNonNull(c);
this.mutex = Objects.requireNonNull(mutex);
}
public int size() {
synchronized (mutex) {return c.size();}
}
public boolean isEmpty() {
synchronized (mutex) {return c.isEmpty();}
}
3、传统方式下的Collection在迭代集合时,不允许对集合进行修改。
即在next()方法时,不允许改变集合中的元素,否则抛出异常。
public static void main(String[] args) {
Collection users = new CopyOnWriteArrayList();
//new ArrayList();
users.add(new User("张三",28));
users.add(new User("李四",25));
users.add(new User("王五",31));
Iterator itrUsers = users.iterator();
while(itrUsers.hasNext()){
System.out.println("aaaa");
User user = (User)itrUsers.next();
if("李四".equals(user.getName())){
users.remove(user);
//itrUsers.remove();
} else {
System.out.println(user);
}
}
}
4、可以使用jdk1.5提供的线程安全的集合工具类:
ConcurrentHashMap
ConcurrentSkipListMap
ConcurrentLinkedQueue
CopyOnWriteArrayList
CopyOnWriteArraySet
ConcurrentSkipListSet