javase-thread-210313-01
- 线程同步
- 死锁
- Lock
- 线程通信
- 生产者/消费者模式01(管城法)
- 生产者/消费者模式02(信号灯法)
- 线程池
线程同步
关键字 synchronized
并发:同一个对象被多个线程同时操作
Demo01
public class Demo08_ThreadUnsafe01 {
public static void main(String[] args) {
Station station = new Station();
new Thread(station,"路人A").start();
new Thread(station,"路人B").start();
new Thread(station,"路人C").start();
}
}
class Station implements Runnable{
private int ticket = 100;
boolean flag = true;
@Override
public void run() {
while (flag){
buyTicket();
}
}
// 同步方法
// 加上synchronized变成同步方法 现在锁的this 默认对象是this
private synchronized void buyTicket(){
if(ticket <= 0){
System.out.println("票已卖完");
flag = false;
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticket-- +"张票");
}
}
Demo02
public class Demo08_ThreadUnsafe02 {
public static void main(String[] args) {
Account account = new Account(150,"bgy");
new Thread(new Bank(0,50,account),"A").start();
new Thread(new Bank(0,150,account),"B").start();
new Thread(new Bank(0,150,account),"C").start();
new Thread(new Bank(0,150,account),"D").start();
}
}
class Account{
int sumMoney;
String name;
public Account(int sumMoney, String name) {
this.sumMoney = sumMoney;
this.name = name;
}
}
class Bank implements Runnable{
Account account;
int nowMoney;
int drawMoney;
public Bank(int nowMoney,int drawMoney,Account account){
this.nowMoney = nowMoney;
this.drawMoney = drawMoney;
this.account = account;
}
@Override
public void run() {
// 同步块,可以监视任何对象
// 锁的对象就是变化的量,,,所以这个例子要锁住account,而不是Bank
synchronized (account){
if(account.sumMoney <= drawMoney){
System.out.println("余额不足");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 取钱
account.sumMoney = account.sumMoney - drawMoney;
System.out.println("还剩余额"+account.sumMoney);
// 目前我有多少钱
nowMoney = nowMoney + drawMoney;
System.out.println(Thread.currentThread().getName() + "我现在手里有" + nowMoney);
}
}
Demo03
import java.util.ArrayList;
import java.util.List;
public class Demo08_ThreadUnsafe03 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
synchronized (list){
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
CopyOnWriteArrayList
CopyOnWriteArrayList 是一个线程安全的集合
import java.util.concurrent.CopyOnWriteArrayList;
public class Demo01 {
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(3000);
System.out.println(list.size());
}
}
死锁
产生死锁的四个条件:
1. 互斥条件:一个资源每次只能被一个进程使用
2. 请求与保持条件:一个进程因请求资源而阻塞是,对已获得的资源保持不放
3. 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
只要破坏以上任意一个或多个条件就能避免死锁的发生
Demo01(造成死锁)
public class Demo01_DeadLock01 {
public static void main(String[] args) {
new GoOut(1,"路人甲").start();
new GoOut(0,"路人乙").start();
}
}
// 需要穿的衣服
class Cloth{ }
// 需要穿的鞋子
class Shoes{ }
// 选择搭配
class GoOut extends Thread{
// static修饰,,确保只有一件衣服,一双鞋子
static Cloth cloth = new Cloth();
static Shoes shoes = new Shoes();
int choose;
String userName;
public GoOut(int choose, String userName) {
this.choose = choose;
this.userName = userName;
}
@Override
public void run() {
try {
makeUp();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 穿搭
public void makeUp() throws InterruptedException {
/*
目前情况会造成死锁,,双方都拿着锁,都不释放锁
*/
if (choose == 1){
synchronized (cloth){ // 得到衣服的锁
System.out.println(this.userName+"获得衣服的锁");
Thread.sleep(1000);
synchronized (shoes){ // 得到鞋子的锁
System.out.println(this.userName+"获得鞋子的锁");
}
}
}else{
synchronized (shoes){ // 得到鞋子的锁
System.out.println(this.userName+"获得鞋子的锁");
Thread.sleep(1000);
synchronized (cloth){ // 得到衣服的锁
System.out.println(this.userName+"获得衣服的锁");
}
}
}
}
}
Demo02(解决)
public class Demo01_DeadLock02 {
public static void main(String[] args) {
new GoOut02(1,"路人甲").start();
new GoOut02(0,"路人乙").start();
}
}
// 需要穿的衣服
class Cloth02{ }
// 需要穿的鞋子
class Shoes02{ }
// 选择搭配
class GoOut02 extends Thread{
// static修饰,,确保只有一件衣服,一双鞋子
static Cloth cloth = new Cloth();
static Shoes shoes = new Shoes();
int choose;
String userName;
public GoOut02(int choose, String userName) {
this.choose = choose;
this.userName = userName;
}
@Override
public void run() {
try {
makeUp();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 穿搭
public void makeUp() throws InterruptedException {
/*
解决死锁
*/
if (choose == 1){
synchronized (cloth){ // 得到衣服的锁 sleep方法不会释放锁,,同步代码块结束后释放锁
System.out.println(this.userName+"获得衣服的锁");
Thread.sleep(1000);
}
// 放到外面
synchronized (shoes){ // 得到鞋子的锁
System.out.println(this.userName+"获得鞋子的锁");
}
}else{
synchronized (shoes){ // 得到鞋子的锁
System.out.println(this.userName+"获得鞋子的锁");
Thread.sleep(1000);
}
// 放到外面
synchronized (cloth){ // 得到衣服的锁
System.out.println(this.userName+"获得衣服的锁");
}
}
}
}
Lock
Lock是显示锁(手动开启关闭锁),,,synchronized是隐式锁,出了作用域自动释放
Lock只有代码块锁,,synchronized有代码块锁和方法锁
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(拥有更多的子类)
优先使用:
Lock > 同步代码块 > 同步方法
同步代码块:已经进入到了方法体,分配了相应的资源
同步方法:在方法体之外
Demo
import java.util.concurrent.locks.ReentrantLock;
public class Demo02_Lock implements Runnable{
private int ticket = 100;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
getTicket();
}
public void getTicket() {
while (true){
try{
// 加锁
lock.lock();
if(ticket <= 0){
System.out.println("票已售空");
break;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "获得第" + ticket-- +"张票");
}finally {
// 解锁
lock.unlock();
}
}
}
public static void main(String[] args) {
Demo02_Lock demo = new Demo02_Lock();
new Thread(demo,"线程A").start();
new Thread(demo,"线程B").start();
}
}
线程通信
wait()
表示线程一直等待,直到其他线程通知,与slee相反,会释放锁
wait(long time)
指定等待的毫秒数
notify()
唤醒一个处于等待状态的线程
notifyAll()
唤醒同一个对象上所有调用wait方法的程,优先级别高的线程优先调用
以上均为Object类的方法,都只能用在同步方法或同步代码块中,
否则抛出lllegaMonitorStateException
01生产者/消费者模式(管城法)
生产者/消费者模式 ,,也叫,,管城法
生产者:负责生产数据的模块(可以是方法,对象,线程,进程)
消费者:负责处理数据的模块(可以是方法,对象,线程,进程)
缓冲区:消费者不能直接使用生产者的数据,他们之间有一个“缓冲区”
生产者将生产好的数据放入缓冲区,,,消费者从缓冲区拿取数据
public class Demo03_PC01 {
public static void main(String[] args) {
Container container = new Container();
new Consumer(container).start();
new Product(container).start();
}
}
// 定义产品
class Chicken{
int id;
public Chicken(int i){
this.id = i;
}
}
// 1.定义容器
class Container{
// 定义容器大小
Chicken[] chickens = new Chicken[10];
// 定义容器计数器
int count = 0;
// 生产者放入产品
public synchronized void push(Chicken chicken){
// 判断容器是否满了,,满的话就等待消费者消费
if (count == chickens.length){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 没满的话,放入产品
chickens[count]=chicken;
count++;
// 这个用来通知消费者消费
this.notifyAll();
}
// 消费者消费产品
public synchronized Chicken pop(){
// 判断容器是否是空,,空的话就等待生产者生产
if (count == 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 满的话,消费产品
count--;
Chicken chicken = chickens[count];
// 买完了,通知生产者生产
this.notifyAll();
return chicken;
}
}
// 消费者
class Consumer extends Thread{
Container containe;
public Consumer(Container containe){
this.containe = containe;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了" + containe.pop().id + "只鸡");
}
}
}
// 生产者
class Product extends Thread{
Container containe;
public Product(Container containe){
this.containe = containe;
}
@Override
public void run() {
for (int i = 1; i < 101; i++) {
System.out.println("生产了" + i + "只鸡");
containe.push(new Chicken(i));
}
}
}
02生产者/消费者模式(信号灯法)
public class Demo03_PC02 {
public static void main(String[] args) {
TV tv = new TV();
new PeoplePlay(tv).start();
new PeopleWatch(tv).start();
}
}
// 创建一个动漫
class TV{
/*
规则如下:
演员表演,观众等待 T
观众观看,演员等待 F
不是直播,是录播
*/
// 节目
String program;
// 标志
boolean flag = true;
// 表演
public synchronized void play(String program){
// 如果为false,,观众观看,演员等待
if (!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("现在的节目是:" + program);
// 通知观众观看
this.notifyAll();
this.program = program;
this.flag = !this.flag;
}
// true,,观众等待,演员表演
public synchronized void watch(){
if (flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观众正在看:" + program);
this.notifyAll();
this.flag = !this.flag;
}
}
// 表演的人
class PeoplePlay extends Thread{
TV tv;
public PeoplePlay(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 PeopleWatch extends Thread{
TV tv;
public PeopleWatch(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
this.tv.watch();
}
}
}
线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo04_ThreadPool {
public static void main(String[] args) {
// 创建线程池
// newFixedThreadPool 池子大小
ExecutorService service = Executors.newFixedThreadPool(10);
TestThread tt = new TestThread();
service.execute(tt);
service.execute(tt);
service.execute(tt);
service.execute(tt);
// 关闭连接
service.shutdown();
}
}
class TestThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}