并发:同一个对象多个线程同时操作
线程同步:由于同一进程的多个线程共享同一个存储空间,在带来方便的同时,也带来了访问冲突的问题。为了保证数据在方法中被访问时加入锁机制(synchronized),当一个线程活得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。
同步方法
public synchronized void method(int args) {}
synchronized方法控制对“成员变量|类变量”对象的访问,每个对象对应一把锁,每个synchronized方法都必须活得调用该方法的对象的锁才能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得锁,重新进入可执行状态。
同步块
同步容器
// 同步容器在内部已经实现同步块,只需要像正常容器创建与使用即可
import java.util.concurrent.CopyOnWriteArrayList;
public class SynContainer{
public static void main(String[] args) throws InterruptedException{
// 创建同步容器
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for(int i=0; i<1000; i++){
new Thread(()->{
list.add(Thread.currentThread().getName();
}).start();
}
Thread.sleep(100);
System.out.println(list.size());
}
}
死锁
过多的同步可能造成相互不释放资源,从而互相等待,一般发生于同步中持有多个对象的锁(锁套锁),因此在编写程序的时候,尽量不要锁套锁,不要再同一个代码块中同时持有多个对象锁。
// 容易发生死锁
public class DeadLock {
public static void main(String[] args) {
new UseResource().start();
new UseResource().start();
}
}
class Resource_1{}
class Resource_2{}
//该对象会使用资源
class UseResource extend Thread{
private static final Resource_1 res1 = new Resource_1(); //创建资源对象
private static final Resource_2 res2 = new Resource_2();
@Override
public void run(){
UseWithDeadLock();
}
private void UseWithDeadLock(){ // 容易发生死锁
// 锁套锁结构,容易发生死锁。
synchronized(res1){
// 做其他事情
synchronized(res2){
// 做其他事情
}
}
}
private void UseWithoutDeadLock(){ // 解决死锁方法
synchronized(res1){//做其他事情}
synchronized(res2){//做其他事情}
}
}
并发线程协作(Cooperation)-- 生产者消费者模式
生产者消费者模式需要解决的是线程之间的通信问题,其中有两种方法(管程法、红绿灯法)
管程法:
生产者:负责生产数据的模块(模块可能是方法、对象、线程、进程)
消费者:负责处理数据的模块
缓冲区:通过缓冲区消费者不能直接使用生产者的数据,只能是生产者将生产好的数据放入该区,消费者从缓冲区取数据。
package cooperation;
/**
* 协作模型:生产者消费者实现方式:管程法
*
* @author Wuhua
*/
public class CoTest01 {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Producer(container).start();
new Consumer(container).start();
}
}
// 生产者
class Producer extends Thread {
private SynContainer container;
Producer(SynContainer container) {
this.container = container;
}
@Override
public void run() {
// 生产
for (int i = 0; i < 100; i++) {
System.out.println("生产-->" + i + "个数据");
container.push(new ProductData(i));
}
}
}
// 消费者
class Consumer extends Thread {
private SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
@Override
public void run() {
// 消费
for (int i = 0; i < 100; i++) {
System.out.println("消费-->" + container.pop().id + "个数据");
}
}
}
// 缓冲区
class SynContainer {
// 存储容器
ProductData[] datas = new ProductData[10];
private int count = 0;
// 存储 生产
synchronized void push(ProductData data) {
// 何时能够生产?
// 容器存在空间,即可生产,否则进入等待状态
if (count == datas.length) {
try {
this.wait(); // 等待消费者通知阻塞解除
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.datas[count] = data;
this.count++;
this.notifyAll(); // 生产了数据,唤醒消费者消费数据
}
// 消费 获取
synchronized ProductData pop() {
// 何时消费?
// 容器中是否存在数据
// 没有数据,只能等待
if (count == 0) {
try {
this.wait(); // 线程阻塞,等待生产者通知
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 存在数据则消费
this.count--;
ProductData data = this.datas[count];
this.notifyAll(); // 存在空间,唤醒对方生产
return data;
}
}
// 数据
class ProductData {
int id;
ProductData(int id) {
this.id = id;
}
}
红绿灯法:
package cooperation;
/**
* 协作模型:生产者消费者实现方式:信号灯法
*
* @author Wuhua
*/
public class CoTest02 {
public static void main(String[] args) {
TV tv = new TV();
new Actor(tv).start();
new Audience(tv).start();
}
}
// 消费者 观众
class Audience extends Thread {
private TV tv;
public Audience(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
// 生产者 演员
class Actor extends Thread {
private TV tv;
public Actor(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i % 2 == 0) {
this.tv.play("奇葩说");
} else {
this.tv.play("广告");
}
}
}
}
// 资源 电视
class TV {
private String voice;
// 信号灯
// T 表示演员表演,观众等待
// F 表示观众观看,演员等待
private boolean flag = true;
// 表演的时候观众等待
synchronized void play(String voice) {
// 演员等待
if (!this.flag) {
try {
this.watch();
} catch (Exception e) {
e.printStackTrace();
}
}
// 表演
System.out.println("表演了:" + voice);
this.voice = voice;
// 唤醒
this.notifyAll();
this.flag = !this.flag;
}
// 观看的时候演员等待
synchronized void watch() {
// 观众等待
if (this.flag) {
try {
this.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
// 观看
System.out.println("听到了:" + this.voice);
// 唤醒
this.notifyAll();
// 切换标志
this.flag = !this.flag;
}
}