一、Lock(锁)
从JDK5.0开始提供,java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能由一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存意义,在实现线程安全的控制中,常用的是ReentranLock,可以显式加锁、释放锁。
import java.util.concurrent.locks.ReentrantLock;
public class StartLock{
public static void main(String[] args) {
SLock sLock = new SLock();
new Thread(sLock,"A").start();
new Thread(sLock, "B").start();
new Thread(sLock,"C").start();
}
}
class SLock implements Runnable{
private int ticketNums = 10;
// 定义Lock锁
private final ReentrantLock lock = new ReentrantLock();
// 常量不可变
@Override
public void run() {
while(true){
try{
lock.lock();
if(ticketNums > 0){
try{
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ticketNums--);
}else{
break;
}
}finally {
lock.unlock();
}
}
}
}
Lock是显式的锁,synchronized是隐式的锁。
使用Lock,JVM将花费较少的时间来调度线程,性能更好。
二、线程协作(生产者消费者)
synchronized只能实现线程同步,不能线程间通信。
wait():线程等待
wait(long timeout):
notify(): 唤醒一个属于等待状态的线程
notifyAll():唤醒同一个对象上所有调用wait()方法的线程,有限级别高的线程优先调度。
生产者:复杂生产数据的模块(可能是方法,对象,线程,进程);
消费者:负责处理数据的模块(可能是方法,对象,线程,进程);
管程法:
缓冲区:消费者不能直接使用生产者的数据,他们之间有个“缓冲区”,生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据。
// 管程法:利用缓冲区解决
public class StartPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
Productor p = new Productor(container);
Consumer c = new Consumer(container);
p.start();
c.start();
}
}
class Productor extends Thread{
SynContainer container;
public Productor(SynContainer container){
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Products(i));
System.out.println("生产者生产:"+i);
}
}
}
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container){
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.pop();
System.out.println("消费者消费:"+i);
}
}
}
class Products{
int id;
Products(int id){
this.id = id;
}
}
//缓冲区
class SynContainer{
Products [] products = new Products[10];
int count = 0;
public synchronized void push(Products product){
if(count == products.length){
//生产者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 管道没有填满,生产者丢入吃奶瓶
products[count] = product;
count++;
// 通知消费者
this.notifyAll();
}
public synchronized Products pop(){
if(count==0){
// 消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//可以消费
count --;
Products product = products[count];
// 通知消费者可以生产了
this.notifyAll();
return product;
}
}
信号灯法:用一个标识位来实现
public class StartPC2 {
public static void main(String[] args) {
TV tv = new TV();
Player player = new Player(tv);
Audience audience = new Audience(tv);
player.start();
audience.start();
}
}
class Player extends Thread{
TV tv;
Player(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
tv.play(""+i);
}
}
}
class Audience extends Thread{
TV tv;
Audience(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
tv.watch();
}
}
}
class TV{
String voice;//表演节目
boolean flag = true;
//表演
public synchronized void play(String voice){
if(!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("表演:"+voice);
// 通知观众
this.voice = voice;
this.flag = false;
this.notifyAll();
}
public synchronized void watch(){
if(flag){
try{
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观看:"+voice);
this.voice = null;
this.flag = true;
this.notifyAll();
}
}
三、线程池
提出原因:线程经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
解决思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。实现重复利用。
优点:
提高响应速度、降低资源消耗、便于线程管理。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class StartPool {
public static void main(String[] args) {
// 输入参数10是线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new TestThread());
service.execute(new TestThread());
service.execute(new TestThread());
service.execute(new TestThread());
// 关闭线程池
service.shutdown();
}
}
class TestThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}