一、并发--线程同步synchronized
在一个多线程的情况下,保证数据的安全性和准确性,同时还要提高性能
线程不安全指的是:有负数或相同的数
并发的条件:存在共享资源、多人、同时操作
线程同步的实现机制:等待池队列+锁机制。
java锁机制:synchronized
synchronized存在以下问题:
1、一个线程持有锁会导致其他其他所有需要此锁的线程挂起
2、在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题
3、如果优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题
synchronized使用方式:
synchronized方法:
在操作共享资源的方法上面加synchronized关键字。(注意,syn锁的是当前对象的资源。也就是调用该方法的对象内部的属性。this的属性。如果方法内存在其他对象的属性,即:this.其他对象.属性,无法锁住)
synchronized同步块:
synchronized(obj){},obj称为同步监视器
在代码执行的的某一部分加syn块,同时监视相应的对象。
同步监视器执行过程:
第一个线程访问,锁定同步监视器,执行代码
第二个线程访问,发现同步监视器被锁定,无法访问
第一个线程执行完毕,解锁同步监视器
第二个线程访问,未发现同步锁,锁定并访问
具体用法:
package com.zzu.threadmethod;
import java.util.ArrayList;
import java.util.List;
public class SynBlockTest {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for (int i = 0; i <1000; i++) {
new Thread(()->{
synchronized (list) {//syn块
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
举个线程例子:电影票
package com.zzu.threadmethod;
import java.util.ArrayList;
import java.util.List;
public class CinemaTest{
public static void main(String[] args) {
List<Integer> seates = new ArrayList<Integer>();
seates.add(1);
seates.add(2);
seates.add(3);
seates.add(4);
seates.add(5);
seates.add(6);
Cinema cinema = new Cinema(seates, "lcx");
List<Integer> seate = new ArrayList<Integer>();
seate.add(1);
seate.add(3);
seate.add(5);
List<Integer> seat = new ArrayList<Integer>();
seat.add(1);
seat.add(3);
new Thread(new customer(cinema,seate),"李晨曦").start();
new Thread(new customer(cinema,seat),"略略略").start();
}
}
class Cinema{
List<Integer> seates;
String cinemaName;
public Cinema(List<Integer> seates, String cinemaName) {
this.seates = seates;
this.cinemaName = cinemaName;
}
public boolean buyTickets(List<Integer> seat) {
List<Integer> sea = new ArrayList<Integer>();
synchronized (this) {//线程锁
sea.addAll(seates);
System.out.println("当前座位有:"+seates.toString());
sea.removeAll(seat);
if(sea.size()!=seates.size()-seat.size()) {
return false;
}
Thread.yield();//为了更好地看到效果
seates = sea;
}
return true;
}
}
class customer implements Runnable{
Cinema cinema;
List<Integer> cusSeats;
public customer(Cinema cinema, List<Integer> cusSeats) {
this.cinema = cinema;
this.cusSeats = cusSeats;
}
@Override
public void run() {
//synchronized (cinema) {在这里加锁也可以不过粒度比较大
boolean flag = cinema.buyTickets(cusSeats);
if(flag) {
System.out.println("购票成功!!!\n"+Thread.currentThread().getName()+"----->购买座位:"+cusSeats.toString());
}else {
System.out.println(Thread.currentThread().getName()+"购票失败,座位不够!!");
}
//}
}
}
二、并发容器
java.util.concurrent.CopyOnWriteList 加有synchronized的容器。里面的锁是ReentrantLock可重复锁
三、并发--线程死锁
双方互相占有地方需要的资源。解决方式:不要在同一代码块中持有两个锁
四、并发--线程协作
先了解一下wait()、notify()和notifyAll()方法:
wait方法:
1.wait()是Object里面的方法,而不是Thread里面的,这一点很容易搞错。它的作用是将当前线程置于预执行队列,并在wait()所在的代码处停止,等待唤醒通知。
2.wait()只能在同步代码块或者同步方法中执行,如果调用wait()方法,而没有持有适当的锁,就会抛出异常。
wait()方法调用后会释放出锁,线程与其他线程竞争重新获取锁。
方法示例:
public class TestWait implements Runnable {
private final Object object=new Object();
@Override
public void run() {
synchronized (object){
System.out.println("线程执行开始。。。");
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程执行结束。。。");
}
}
public static void main(String[] args) {
TestWait testWait=new TestWait();
Thread thread=new Thread(testWait);
thread.start();
}
}
执行结果:
notify方法:
1.notify()方法也是要在同步代码块或者同步方法中调用的,它的作用是使停止的线程继续执行,调用notify()方法后,会通知那些等待当前线程对象锁的线程,并使它们重新获取该线程的对象锁,如果等待线程比较多的时候,则有线程规划器随机挑选出一个呈wait状态的线程。
2.notify()调用之后不会立即释放锁,而是当执行notify()的线程执行完成,即退出同步代码块或同步方法时,才会释放对象锁。
方法示例:
package com.zzu.threadmethod.cooperation;
public class TestWait implements Runnable{
T obj ;//共享资源
public boolean flag=true;//标志位
TestWait(T obj){
this.obj = obj;
}
public static void main(String[] args) {
T obj = new T();//创建共享资源
obj.a=0;
obj.name="主线程";
new Thread(new TestWait(obj),"lcx").start();//启动第一个线程,扔入共享资源
new Thread(new TestWait(obj),"lhp").start();//启动第二个线程,扔入共享资源
TestWait tt = new TestWait(obj);
tt.flag=false;
new Thread(tt,"lyb").start();//启动唤醒线程
}
@Override
public void run() {
try {//为了模拟多个线程竞争的延时
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//线程操作
if(flag) {//根据标志位确定执行那种操作
synchronized (obj) {
System.out.println(Thread.currentThread().getName()+"---线程开始执行");
System.out.println("共享资源现状:"+obj.toString());
try {
obj.wait();
obj.a=3;
obj.name=Thread.currentThread().getName();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"----线程结束执行");
System.out.println("共享资源现状:"+obj.toString());
}
}else {
synchronized (obj) {
obj.notify();
obj.a=2;
obj.name="唤醒线程";
System.out.println(Thread.currentThread().getName()+"唤醒其他线程");
System.out.println("共享资源现状:"+obj.toString());
}
}
}
}
class T{
int a;
String name;
@Override
public String toString() {
return "T [a=" + a + ", name=" + name + "]";
}
}
执行结果如下:
notifyAll方法:
唤醒由当前资源调用wait方法阻塞的所有线程。不同类的线程都会唤醒
例子:
package com.zzu.threadmethod.cooperation;
public class TestWait implements Runnable{
T obj ;//共享资源
public boolean flag=true;//标志位
TestWait(T obj){
this.obj = obj;
}
public static void main(String[] args) {
T obj = new T();//创建共享资源
obj.a=0;
obj.name="主线程";
new Thread(new TestWait(obj),"lcx").start();//启动第一个线程,扔入共享资源
new Thread(new TestWait(obj),"lhp").start();//启动第二个线程,扔入共享资源
new Thread(new TestWait(obj),"lz").start();//启动第二个线程,扔入共享资源
new Thread(new TT(obj),"李晨曦").start();//启动第二个线程,扔入共享资源
new Thread(new TestWait(obj),"lzh").start();//启动第二个线程,扔入共享资源
TestWait tt = new TestWait(obj);
tt.flag=false;
new Thread(tt,"lyb").start();//启动唤醒线程
}
@Override
public void run() {
try {//为了模拟多个线程竞争的延时
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//线程操作
if(flag) {//根据标志位确定执行那种操作
synchronized (obj) {
System.out.println(Thread.currentThread().getName()+"---线程开始执行");
System.out.println("共享资源现状:"+obj.toString());
try {
obj.wait();
obj.a=3;
obj.name=Thread.currentThread().getName();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"----线程结束执行");
System.out.println("共享资源现状:"+obj.toString());
}
}else {
try {//为了模拟多个线程竞争的延时
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj) {
obj.notifyAll();
obj.a=2;
obj.name="唤醒线程";
System.out.println(Thread.currentThread().getName()+"唤醒其他线程");
System.out.println("共享资源现状:"+obj.toString());
}
}
}
}
class T{
int a;
String name;
@Override
public String toString() {
return "T [a=" + a + ", name=" + name + "]";
}
}
class TT implements Runnable{//另一个线程类
T obj ;//共享资源
TT(T obj){
this.obj = obj;
}
@Override
public void run() {
//线程操作
synchronized (obj) {
System.out.println(Thread.currentThread().getName()+"---线程开始执行");
System.out.println("共享资源现状:"+obj.toString());
try {
obj.wait();
obj.a=3;
obj.name=Thread.currentThread().getName();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"----线程结束执行");
System.out.println("共享资源现状:"+obj.toString());
}
}
}
生产者-消费者模式实现方案:共享内存或者信号灯法
1、管程法:
public class TestPC {
public static void main(String[] args) {
Queue<Goods> queue = new ArrayBlockingQueue<>(5);//共享资源
Producer producer = new Producer(queue);
Consumer consumer = new Consumer(queue);
for (int i = 1; i < 5; i++) {
Thread threadA = new Thread(producer, "生产者" + i);//创建多个生产者
threadA.start();
}
for (int j = 1; j <= 5; j++) {
Thread threadB = new Thread(consumer, "消费者" + j);//创建多个消费者
threadB.start();
}
}
}
class Goods {//产品
String name;
public Goods(String name) {
this.name = name;
}
}
class Consumer implements Runnable {
Queue<Goods> queue;
public Consumer(Queue<Goods> queue) {
this.queue = queue;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(500);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
synchronized (queue) {
if (!queue.isEmpty()) {//有商品可以消费
queue.poll();
System.out.println(Thread.currentThread().getName() + "消费商品---剩余商品:" + queue.size());
if(queue.size()<5) {//释放生产者
queue.notifyAll();
}
} else {//没有商品,消费者等待
try {
System.out.println("消费者--"+Thread.currentThread().getName()+"--阻塞");
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者--"+Thread.currentThread().getName()+"--被唤醒");
}
}
}
}
}
class Producer implements Runnable {
private Goods goods;
Queue<Goods> queue;
public Producer(Queue<Goods> queue) {
this.queue = queue;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(500);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
synchronized (queue) {
goods = new Goods( "商品");
if (queue.size() <5) {//生产商品
queue.add(goods);
System.out.println(Thread.currentThread().getName() + "生产商品---剩余商品:" + queue.size());
if(queue.size()>0) {//释放消费者
queue.notifyAll();
}
} else {//共享资源已满,阻塞生产者
try {
System.out.println("生产者--"+Thread.currentThread().getName()+"--阻塞");
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生产者--"+Thread.currentThread().getName()+"--被唤醒");
}
}
}
}
}
2、信号灯法:将上面的queue.size() <5改成信号灯flag即可;