Java Thread笔记
1,开启线程的两种方式:
一:
Thread thread = new Thread(){
@Override
public void run() {
while(true){
try {
Thread.sleep(500);
} catch (InterruptedExceptione) {
e.printStackTrace();
}
System.out.println("1:"+ Thread.currentThread().getName());
System.out.println("2:"+ this.getName());
}
}
};
thread.start();
二:
Thread thread2 = new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {
Thread.sleep(500);
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("1:"+ Thread.currentThread().getName());
}
}
});
thread2.start();
2,定时器
一,
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.out.println("bombing!");
}
}, 10000,3000);
10秒后启动,然后没3秒启动一次
二,
线程池中提供多线程的定时器,在线程池中开启3个线程,固定6秒执行,然后执行的频率是每个2秒在执行一次
Executors.newScheduledThreadPool(3).scheduleAtFixedRate(
new Runnable(){
@Override
public void run() {
System.out.println("bombing!");
}},
6,
2,
TimeUnit.SECONDS);
}
三,
CountDownLatch:一个倒计时的计数器,用countDown发布开始命令,当归零时执行某段代码
package cn.itcast.heima2;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CountdownLatchTest {
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 = newRunnable(){
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 (Exceptione) {
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();
}
}
3.互斥synchronized
应当多线程访问同一资源的时候在不断读取数据后悔造成数据的混乱,所以引入了synchronized,可以对相应的资源用:
Synchronized(xx.class){
……..
}
其中xx.class是唯一的一个资源锁
当然也可以对相应的功能类使用:
public synchronizedvoid xx{
。。。。
}
class xxx{
private boolean bShouldSub = true;
public synchronized void a(int i){
while(!bShouldSub){
try {
this.wait();
} catch(InterruptedException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
。。。。。。
bShouldSub = false;
this.notify();
}
public synchronized void B(int i){
while(bShouldSub){
try{
this.wait();
} catch(InterruptedException e) {
// TODOAuto-generated catch block
e.printStackTrace();
}
}
。。。。。。。。。。。。
bShouldSub = true;
this.notify();
}
}
这样就实现了多线程中这两个方法不会同时执行,保证了在修改同一资源的时候不会造成资源的混乱
4,线程间的数据共享ThreadLocal
public classThreadLocalTest {
private static ThreadLocal<Integer> x =new ThreadLocal<Integer>();
public static void main(String[] args) {
for(int i=0;i<2;i++){
new Thread(new Runnable(){
@Override
public void run() {
int data =new Random().nextInt();
System.out.println(Thread.currentThread().getName()
+" has put data :" + data);
x.set(data);
MyThreadScopeData.getThreadInstance().setName("name" + data);
MyThreadScopeData.getThreadInstance().setAge(data);
new A().get();
new B().get();
}
}).start();
}
}
static class A{
public void get(){
intdata = x.get();
MyThreadScopeDatamyData = MyThreadScopeData.getThreadInstance();
System.out.println("A from " +Thread.currentThread().getName()
+" getMyData: " + myData.getName() +"," +
myData.getAge());
}
}
static class B{
public void get(){
intdata = x.get();
MyThreadScopeDatamyData = MyThreadScopeData.getThreadInstance();
System.out.println("B from " +Thread.currentThread().getName()
+" getMyData: " + myData.getName() +"," +
myData.getAge());
}
}
}
class MyThreadScopeData{
// private MyThreadScopeData(){}
private static ThreadLocal<MyThreadScopeData> map = newThreadLocal<MyThreadScopeData>();
public static MyThreadScopeData getThreadInstance(){
MyThreadScopeDatainstance = map.get();
if(instance ==null){
instance= newMyThreadScopeData();
map.set(instance);
}
return instance;
}
private Stringname;
private int age;
public String getName() {
returnname;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
returnage;
}
public void setAge(int age) {
this.age = age;
}
}
这样就是现了ab各自内的数据的共享
加入申明一个全局的AtomicInteger age变量,那么在接下来在一线程中addAnGet(age)时,不管在其他线程中对age做什么操作,那么必须先执行addAnGet(age)方法;在api java.util.concurrent中有对更多对线程的方法
5,线程池 Executors
多线程中使用线程池好处,每次都要newThread这样就会浪费内存,而使用线程池不一样这个线程执行后如果池中没有要执行的了就休眠,如果有东西执行就会醒过来
创建线程池的3中方法:
//1,
ExecutorService threadPool = Executors.newFixedThreadPool(3);//创建固定的线程池
//2,
//ExecutorService threadPool = Executors.newCachedThreadPool();//缓存线程池,中的线程不确定,当任务有的时候自动增加线程
//3,
ExecutorService threadPool = Executors.newSingleThreadExecutor();//只有一个线程池,相当单线程,但是好处是当线程死后马上又会启动一个替补,保证池中有一个线程
threadPool.execute(new Runnable(){
@Override
public void run() {
………..
}
}
threadPool.shutdown()//等到所以线程空闲后杀掉线程
//threadPool.shutdownNow();//结束当前任务,不过线程是否执行完毕
6,Callable,Future
返回线程一直执行完的结果,然后用Future接受:
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Future<String> future=threadPool.submit(
newCallable<String>() {
public Stringcall() throws Exception {
Thread.sleep(2000);
return"hello";
};
}
);
System.out.println("等待结果");
try {
System.out.println("拿到结果:" +future.get());
}catch (InterruptedException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
也可传去一组服务,根据任务完成的情况,完成的就返回当前任务:
ExecutorService threadPool2 = Executors.newFixedThreadPool(10);
CompletionService<Integer>completionService = new ExecutorCompletionService<Integer>(threadPool2);
for(int i=1;i<=10;i++){
final int seq = i;
completionService.submit(newCallable<Integer>() {
@Override
public Integer call()throws Exception {
Thread.sleep(newRandom().nextInt(5000));
return seq;
}
});
}
for(int i=0;i<10;i++){
try {
System.out.println(
completionService.take().get());
} catch(InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch(ExecutionException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
}
7,Lock 实现互斥
public classlocktest {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
Thread.sleep(1000);
new locktest().a("aaaaaa");
}catch(InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
Thread.sleep(1000);
new locktest().a("bbbbbb");
}catch(InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
}
public void a(String name){
// synchronized (A.class) {
// for(int i=0;i<name.length();i++){
// System.out.print(name.charAt(i));
// }
// System.out.println();
// }
Locklock1=new ReentrantLock();
lock1.lock();
try{
for(int i=0;i<name.length();i++){
System.out.print(name.charAt(i));
}
}finally{
lock1.unlock();
}
System.out.println();
}
和synchronized都能实现互斥;但他比synchronized更面向对象,当然lock也提供读写锁:
读写锁分为读锁和写锁,多个读锁不互斥有并发读的效率高,读锁与写锁互斥,写锁也写锁互斥
public class CacheDemo {
//缓存
private Map<String,Object> cache = new HashMap<String, Object>();
//读写锁
private ReadWriteLockrwl = new ReentrantReadWriteLock();
//去缓存中取数据
public Object getData(String key){
//取数据的时候上读锁,应许并发,多条线程同时去读
rwl.readLock().lock();
Object value = null;
try{
//去缓存中取数据
value =cache.get(key);
//如果缓存中没有
if(value == null){
//就关闭读锁,
rwl.readLock().unlock();
//开启写锁
rwl.writeLock().lock();
try{
//如果多条线程同时关闭读锁,只有一个开启写锁,之后赋值完成后关闭读锁,那么第二线程可以进来了,他同样还要去赋值,在这里就判断避免这种情况
if(value==null){
value ="aaaa";//实际失去queryDB();
}
}finally{
//关闭写锁
rwl.writeLock().unlock();
}
//赋值完成,开启读锁
rwl.readLock().lock();
}
}finally{
//关闭读锁
rwl.readLock().unlock();
}
return value;
}
}
8,线程中的通信(condition,是Lock中的属性)
Condition是Lock的特有属性可以通过:
Locklock=new ReentrantLock();
Condition condition=lock.newCondition();
condition.await();//让当前线程等待
……
……
Condtion.signal();//发送信号
这个和this.wait();和this.notify();的作用差不多,但他们必须在synchronized代码块中
1. class BoundedBuffer {
2. final Lock lock = new ReentrantLock();
3. final Condition notFull = lock.newCondition();
4. final Condition notEmpty = lock.newCondition();
5. //需要用两个condition,如果用一个看上去能解决问题,但是对一些特殊情况有问题,比如现在有五个线程阻塞,突然又线程去取缓冲中的东西,取完后发出通知,五个线程中有一个不用阻塞了,进去放完后使用signal()发信号,此时发信号会导致阻塞的四个线程往已经满的缓存后放东西,导致溢出,所以要用两个condition来区分开读和写
6.
7. final Object[] items = new Object[100];
8. int putptr, takeptr, count;
9.
10. public void put(Object x) throws InterruptedException {
11. lock.lock();
12. try {
13. while (count == items.length)
14. notFull.await();
15. items[putptr] = x;
16. if (++putptr == items.length) putptr = 0;
17. ++count;
18. notEmpty.signal();
19. } finally {
20. lock.unlock();
21. }
22. }
23.
24. public Object take() throws InterruptedException {
25. lock.lock();
26. try {
27. while (count == 0)
28. notEmpty.await();
29. Object x = items[takeptr];
30. if (++takeptr == items.length) takeptr = 0;
31. --count;
32. notFull.signal();
33. return x;
34. } finally {
35. lock.unlock();
36. }
37. }
38. }
9,信号灯(semaphore)
信号灯:限定灯的个数,多线程进入后只有相应灯的数目的线程中的任务可以执行,当灯中的任务执行完后释放,在放其他等待线程进入,他和synchronized的不同是,synchronized中只能是一条线程进入,和semaphore中可以根据灯的个数,决定并发的个数
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.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 = newRunnable(){
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);
}
}
}