1.线程同步机制:
多个线程同时访问同一个资源:为了保证数据再方法被访问时的正确性,在访问时加入锁的机制:synchronized。当一个线程获得对象的排他锁,独占资源,其他线程必须等待。
三大不安全问题:
买票:
银行账户取钱:
集合不安全:
Synchronized 方法
同步方法:给方法申明为synchronized ,相当于去拿对象资源的那把锁。保证线程的安全性。默认是锁自己 this。
public synchronized void test(){
xxxxx
}
同步代码块:Synchronized 代码块可以锁任何的对象。
Synchronized (xxx) {
}
死锁:
双方都持有自己的资源,不进行释放,同时还想要对方的资源。
解决:保证一个资源只能有一个人所持有。
Lock锁:
ReentrantLock (可重入锁) 类实现了Lock锁,与synchronized 相同的语义。
对比synchronized : Lock锁是显示的锁,需要手动开启锁和关闭锁。synchronized 是隐私锁。
package com;
import java.util.Locale;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author Lenovo
* @date 2024/3/11
* @time 21:02
* @project Chapter01_Java_多线程
**/
public class TestLock {
public static void main(String[] args) {
//实例化一个Runnable的对象:
Lock lock1 = new Lock();
new Thread(lock1).start();
new Thread(lock1).start();
}
}
class Lock implements Runnable{
private int ticketNubmer=10;
//加入ReentrantLock锁:
ReentrantLock lock= new ReentrantLock();
@Override
public void run() {
while (true){
if (ticketNubmer>0){
try {
//枷锁
lock.lock();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前票为"+ticketNubmer--);
}finally {
//释放锁
lock.unlock();
}
}else {
break;
}
}
}
}
生产者和消费者问题:
解决的办法:
管程法:使用缓冲区: (重点)
(消费者等待wait; 通知生产者生产notifyAll)
重点:都需要给方法或者代码块加上锁。
package com.chapter01_多线程;
/**
* @author Lenovo
* @date 2024/3/12
* @time 9:15
* @project Chapter01_Java_多线程
**/
public class ProductorAndConsumer {
public static void main(String[] args) {
Buffer buffer = new Buffer();
Productor productor = new Productor(buffer);
Consumer consumer = new Consumer(buffer);//传入缓存;
//多线程:
new Thread(productor).start();
new Thread(consumer).start();
}
}
class Productor implements Runnable{
Buffer buffer;
//构造器
public Productor(Buffer buffer) {
this.buffer = buffer;
}
//生产:
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
buffer.push(new Product(i)); //放入产品
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生产者生产了多少产品" + i);
}
}
}
class Consumer implements Runnable{
//缓冲区属性:
Buffer buffer;
//构造器
public Consumer(Buffer buffer) {
this.buffer = buffer;
}
//消费产品
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费者消费了"+ buffer.pop().id+"产品");
}
}
}
//产品id
class Product {
public int id;
public Product(int id) {
this.id = id;
}
}
//缓存区
class Buffer{
//缓冲区大小:
Product[] productsBuffer=new Product[10];
//计数器计数:
int count=0;
//生产者生产产品
public synchronized void push(Product product) throws InterruptedException {
if (count == productsBuffer.length){
//通知消费者消费;生产者等待:
this.wait();
}
//生产产品:
productsBuffer[count]= product;//放入产品
count++;
//通知消费者消费:
this.notifyAll();
}
//消费者消费产品: 要加上锁:
public synchronized Product pop(){
if (count == 0){
//通知生产者生产;消费者等待;
try {
this.wait();
}catch (Exception e){
}
}
//消费:
count-- ; //数组从0开始;
//拿出产品:
Product product = productsBuffer[count];
//吃完了通知生产者生产:
this.notifyAll();
return product;
}
}
信号灯:(重点)通过一个标志位flag 进行标志进区分。
package com.chapter01_多线程;
/**
* @author Lenovo
* @date 2024/3/12
* @time 12:47
* @project Chapter01_Java_多线程
**/
public class ProductorAndConsumer02 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();//继承了Thread类
new Watcher(tv).start(); //继承了Thread类
}
}
class Player extends Thread{
TV tv;
public Player(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i % 2== 0){
tv.play("三锅演绎");
}else {
tv.play("西游记");
}
}
}
}
class Watcher extends Thread{
TV tv;
public Watcher(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
//观众只管观看:
tv.watch();
}
}
}
class TV{
String name;
boolean flag=true; //为真表演者表演,为假是观看者观看:
public synchronized void play(String name){
//演员等待:
if (!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了:"+name);
//通知观众观看:
this.notifyAll();
this.name=name;
this.flag=!this.flag; //取反
}
//要加上锁:synchronized
public synchronized void watch(){
//观众等待:
if (flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观众观看了:"+name);
//通知表现者表演:
this.notifyAll();
this.flag=!this.flag;
}
}
线程池:保证资源不会被浪费
实现Runnable 接口可以直接使用 excute。
思考
有什么想法或心得体会,都可以拿出来分享下。