1.volatile
可以强制去掉缓存区 每次都去从内存读值
static byte value=0;
static boolean finish=false;
public static void testVolatile() throws InterruptedException {
value=0;
finish=false;
new Thread(new Runnable() {
@Override
public void run() {
while(value==0&&!finish);
console.info
("value is "+value+" finish is "+finish);
}
}).start();
Thread.sleep(1000);
new Thread(new Runnable() {
@Override
public void run() {
value=10;
finish=true;
console.info
("has set the finish");
}
}).start();
}
结果可以看到运行结果如图:第一个线程并没有如期结束,在线程循环里, 如果没有与其他线程产生交互 ,他不会读取内存的值 会始终使用缓存的值,所以第一个线程的死循环。
线程运行这是因为cpu和线程有个缓存区,当线程运行的时候 读入需要的变量进入缓冲区
volatile字段添加位置所以我们可以通过volatile关键字 保证每次读取都会从实际内存里读取
2.concurrent的BlockingQueue
java 1.6之后有的这个接口,这个是一个线程安全的对象,用于线程之间的通信。
BlockingQueue是一个线程安全的队列接口 ,提供了 put take poll等方法
实现类有 ArrayBlockingQueue LinkedBlockingQueue PriorityBlockingQueue。
- ArrayBlockingQueue初始化的时候要指定大小,且容量就是初始化指定的
- LinkedBlockingQueue 是链表实现 不需要指定大小 会自动扩容
- PriorityBlockingQueue 是一个有优先级的队列,可以让优先级高的先处理,优先级低的后处理
生产者 消费者 用这个就很好解决,如图:
生产者和消费者 BlockingQueue中线程运行2.1LinkedBlockingQueue
public static void blockingQueueTest(){
BlockingQueue<String> blockingDeque=new LinkedBlockingQueue<String>();
new Thread(new Runnable() {
@Override
public void run() {
try {
console.info
(blockingDeque.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"消费者").start();
new Thread(new Runnable() {
@Override
public void run() {
try {
blockingDeque.put("new Msg");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"生产者").start();
}
2.2PriorityBlockingQueue (优先级)
public static class Message implements Comparable<Message>{
public String name;
public int value=0;
public Message(int value,String name) {
this.value = value;
this.name=name;
}
@Override
public int compareTo(Message o) {
return value-o.value;
}
}
public static void blockingQueueTests(){
PriorityBlockingQueue<Message> blockingDeque=new PriorityBlockingQueue <Message>();
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<100;i++) {
try {
console.info
(blockingDeque.take().name);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"消费者").start();
new Thread(new Runnable() {
@Override
public void run() {
blockingDeque.put(new Message(100,"zhangsan"));
blockingDeque.put(new Message(0,"lisi"));
blockingDeque.put(new Message(0,"wangwu"));
blockingDeque.put(new Message(100,"zhaosi"));
}
},"生产者").start();
}
可以看put方法是阻塞的 直到里面有足够的空间存储,take也是阻塞的直到有内容的时候返回,所以上面的线程 即是是消费者直接运行 生产者后运行 也能正确的指向。
3.concurren包的线程池
Executors.newScheduledThreadPool 创建固定大小的线程池
Executors.newFixedThreadPool 创建固定大小的线程池
Executors.newCachedThreadPool 创建缓存线程池 从0 开始 每个线程存货一分钟
Executors.newSingleThreadExecutor 创建只有一个线程的线程池
实际上都是创建了一个ThreadPoolExecutor 控制了不同参数
/**
*
* corePoolSize 线程池最小保持线程数 即是他们处于空闲状态
* maximumPoolSize 线程池最大线程数
* keepAliveTime 如果线程数超出最小保持 那么线程最大空闲存活实际
* unit 存活时间单位单位
* 初始工作的缓冲区
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
}
eg:
public static void CountDownLatchTest() throws InterruptedException {
long startTime=System.currentTimeMillis();
CountDownLatch countDownLatch=new CountDownLatch(65535);
ScheduledExecutorService service = Executors.newScheduledThreadPool(9000);
for(int i=0;i<65535;i++){
service.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(200);
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
try {
countDownLatch.await();
service.shutdown();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("所有线程完成了!"+(System.currentTimeMillis()-startTime)+"ms");
}
4.ReentrantReadWriteLock
synchronized 获取锁的方式 是不区分读写的,只要获取锁就排斥其他所有线程
ReentrantReadWriteLock 把锁分成读锁和写锁:读锁不排斥读锁 也就是多个线程都可以进入读锁。
写锁 排斥读锁和其它写锁,换成写锁的话只有一个线程能获取到锁。
锁用完需要进行释放unlock()
public static void ReadWriteLock(){
ReadWriteLock reentrantLock=new ReentrantReadWriteLock();
Lock readLock=reentrantLock.readLock();
Lock writeLock=reentrantLock.writeLock();
for(int i=0;i<2;i++)
new Thread(new Runnable() {
@Override
public void run() {
readLock.lock();
console.info
("获取到了读锁1");
try {
Thread.sleep(20);
readLock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
for(int i=0;i<2;i++)
new Thread(new Runnable() {
@Override
public void run() {
writeLock.lock();
console.info
("获取到了写锁2");
try {
Thread.sleep(2000);
writeLock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
运行结果会发现不是立马两个线程都运行了,是先运行了第一个,等待线程运行完成,在运行第二个。
网友评论