Java之多线程(同步、死锁、经典问题)


同步

举个栗子

以买火车票为例
如果有四个线程一起卖票
不存在同步的话

public class TestTicked implements Runnable{
//有5张票
int ticked = 5;
@Override
//重写run()方法
public void run() {
for(int i=ticked;i>0;i--) {
System.out.println(Thread.currentThread().getName() + " sells " + i + " ticket");
}
}
//main方法测试
public static void main(String[] args) {
//实例化Thread对象
TestTicked tT = new TestTicked();
Thread td1 = new Thread(tT,"火车站");
Thread td2 = new Thread(tT,"老黄牛");
Thread td3 = new Thread(tT,"代理商");
Thread td4 = new Thread(tT,"12306");
//启动线程
td1.start();
td2.start();
td3.start();
td4.start();
}
}

执行结果
Java之多线程(同步、死锁、经典问题)

看结果,我们可以知道,每一个线程都卖了5张票,一共应该是卖了20张票
但是我们只有5张,这样显然是不合理的
同步代码块

synchronized(临界资源对象){//为临界资源对象加锁
//原子操作
}

某个子线程进入后,先上锁,将同步代码块中的代码以“独占”的方式执行完毕后,释放锁,以便下一个线程对象进入执行
锁要求:任意的非空对象都可以充当锁。

如果使用同步代码块卖票的话

public class TestTicked implements Runnable{

int ticket = 50;
//担任"锁",锁是什么不重要,他只是一把锁
//要求,非空对象
private String mutex = "";
@Override
public void run() {
//while循环
while (true) {
synchronized (mutex) {
if(ticket > 0) {
System.out.println(Thread.currentThread().getName()+ " sells " + ticket-- + " ticket.");
}else {
break;
}
}
}
}
//测试
public static void main(String[] args) {
TestTicked tt = new TestTicked();

Thread t1 = new Thread(tt, "火车站");
Thread t2 = new Thread(tt, "12306");
Thread t3 = new Thread(tt, "黄牛");
Thread t4 = new Thread(tt, "代理商");

t1.start();
t2.start();
t3.start();
t4.start();
}
}

执行结果
Java之多线程(同步、死锁、经典问题)
这样就实现了,这四个共同卖票,而不是每个卖每个的

同步方法

synchronized 返回值类型 方法名称(形参列表){//对当前对象(this)加锁
//代码(原子操作)
}

也使用同步方法

public class TestTicked implements Runnable{

int ticket = 500;
@Override
public void run() {

sellTicket();
}
//创建同步方法
/**
* 自定义一个同步方法,锁将是this对象
* 可能是t1, t2 , t3和t4中的任意一个对象
*/
public synchronized void sellTicket() {
while (true) {
if(ticket > 0) {
System.out.println(Thread.currentThread().getName() + " sells " + ticket--);
}else {
break;
}
}
}
public static void main(String[] args) {
TestTicked tt = new TestTicked();
Thread t1 = new Thread(tt, "火车站");
Thread t2 = new Thread(tt, "12306");
Thread t3 = new Thread(tt, "黄牛");
Thread t4 = new Thread(tt, "代理商");

t1.start();
t2.start();
t3.start();
t4.start();
}

}

执行结果
Java之多线程(同步、死锁、经典问题)
这般使用同步方法的话,会是的t1,t2,t3,t4中的一个抢占了此次的资源,把该方法全部执行完(把票卖完)

死锁

死锁的概念

同步可以保证资源共享操作的正确性,但是过多同步也会产生问题。
例如,现在张三想要李四的画,李四想要张三的书,张三对李四说“把你的画给我,我就给你书”,李四也对张三说“把你的书给我,我就给你画”两个人互相等对方先行动,就这么干等没有结果
所谓死锁,就是两个线程都在等待对方先完成,造成程序的停滞,一般程序的死锁都是在程序运行时出现的。

//示例
class Zhangsan{ // 定义张三类
public void say(){
System.out.println("张三对李四说:“你给我画,我就把书给你。”") ;
}
public void get(){
System.out.println("张三得到画了。") ;
}
};
class Lisi{ // 定义李四类
public void say(){
System.out.println("李四对张三说:“你给我书,我就把画给你”") ;
}
public void get(){
System.out.println("李四得到书了。") ;
}
}
public class ThreadDeadLock implements Runnable{
private static Zhangsan zs = new Zhangsan() ; // 实例化static型对象
private static Lisi ls = new Lisi() ; // 实例化static型对象
private boolean flag = false ; // 声明标志位,判断那个先说话
public void run(){ // 覆写run()方法
if(flag){
synchronized(zs){ // 同步张三
zs.say() ;
try{
Thread.sleep(500) ;
}catch(InterruptedException e){
e.printStackTrace() ;
}
synchronized(ls){
zs.get() ;
}
}
}else{
synchronized(ls){
ls.say() ;
try{
Thread.sleep(500) ;
}catch(InterruptedException e){
e.printStackTrace() ;
}
synchronized(zs){
ls.get() ;
}
}
}
}
public static void main(String args[]){
ThreadDeadLock t1 = new ThreadDeadLock() ; // 控制张三
ThreadDeadLock t2 = new ThreadDeadLock() ; // 控制李四
t1.flag = true ;
t2.flag = false ;
Thread thA = new Thread(t1) ;
Thread thB = new Thread(t2) ;
thA.start() ;
thB.start() ;
}
}

Java之多线程(同步、死锁、经典问题)

经典问题

生产者消费者问题
需求

若干个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个能存储多个产品的缓冲区,生产者将生产的产品放入缓冲区中.消费者从缓冲区中取走产品进行消费,显然生产者和消费者之间必须保持同步,即不允许消费者到一个空的缓冲区取商品,也不允许生产者向满的缓冲区放入产品
分析
需要四个类:生产者类,消费者类.,缓冲区类(工厂类)和测试类
生产者类和消费者类分别是一个线程
且工厂类里应有两个同步方法

生产者类

public class Produer extends Thread {

private BreadContainer bc = null;

private int num;
//构造器
public Produer(BreadContainer bc, int num) {
this.bc = bc;
this.num = num;
}

@Override
public void run() {

// 调用面包工厂的生产方法来生产面包
bc.produce(num);
}
}

消费者类

public class Consumer extends Thread {

private BreadContainer bc = null;

private int num;
//构造器
public Consumer(BreadContainer bc, int num) {
this.bc = bc;
this.num = num;
}
@Override
public void run() {

// 调用面包工厂的生产方法来生产面包
bc.consume(num);
}
}

工厂类

public class BreadContainer {

// 面包工厂的最大容量
private static final int MAX_NUM = 500;

// 面包工厂的当前面包数
private int currNum;

// 设置当前面包工厂的面包数量
public void setCurrNum(int currNum) {
if(currNum > MAX_NUM) {
currNum = MAX_NUM;
}
this.currNum = currNum;
}

/**
* 生产方法,用来生产num箱面包
* @param num
*/
public synchronized void produce(int num) {
System.out.printf("当前有面包数量为%d,尝试生产%d箱面包\n", currNum, num);

while(currNum + num > MAX_NUM) {
try {
System.out.println("仓库容量不足,暂停生产....");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

currNum += num;
System.out.printf("生产了%d箱面包,当前的面包数量为%d\n", num, currNum);

// 唤醒,随机唤醒线程队列中的某一个线程对象来进行执行
notify();

// 唤醒线程队列中的所有线程对象,只有获得了cpu资源的线程才可以直接进入运行状态
//notifyAll();
}

public synchronized void consume(int num) {
System.out.printf("当前有面包数量为%d,尝试消费%d箱面包\n", currNum, num);

while(currNum < num) {
try {
System.out.println("仓库库存不足,暂停消费....");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

currNum -= num;
System.out.printf("消费了%d箱面包,当前的面包数量为%d\n", num, currNum);

// 唤醒,随机唤醒线程队列中的某一个线程对象来进行执行
notify();

// 唤醒线程队列中的所有线程对象
// notifyAll();
}
}

测试类

public class Test {

public static void main(String[] args) {
BreadContainer bc = new BreadContainer();

bc.setCurrNum(30);

Produer p1 = new Produer(bc, 60);
Produer p2 = new Produer(bc, 80);
Produer p3 = new Produer(bc, 40);
Produer p4 = new Produer(bc, 20);

Consumer c1 = new Consumer(bc, 30);
Consumer c2 = new Consumer(bc, 40);
Consumer c3 = new Consumer(bc, 70);
Consumer c4 = new Consumer(bc, 50);

p1.start();
p2.start();
p3.start();
p4.start();

c1.start();
c2.start();
c3.start();
c4.start();
}
}

执行结果
Java之多线程(同步、死锁、经典问题)
sleep()和wait()对比
共同点:

都会抛出InterruptedException中断异常
不同点
sleep
休眠指定时间后自动进入可运行状态
可以有一参、两参的重载方法
Thread类的静态方法,只能通过Thread.sleep()来调用
sleep不释放线程锁

wait

等待,需要被唤醒才能进入可运行状态,一般与notify或者notifyAll搭配使用
可以有无参,一参和两参的重载方法
Object类的非静态方法,所以在任何地方都可以随时调用
wait会释放线程锁

原创:https://www.panoramacn.com
源码网提供WordPress源码,帝国CMS源码discuz源码,微信小程序,小说源码,杰奇源码,thinkphp源码,ecshop模板源码,微擎模板源码,dede源码,织梦源码等。

专业搭建小说网站,小说程序,杰奇系列,微信小说系列,app系列小说

Java之多线程(同步、死锁、经典问题)

免责声明,若由于商用引起版权纠纷,一切责任均由使用者承担。

您必须遵守我们的协议,如果您下载了该资源行为将被视为对《免责声明》全部内容的认可-> 联系客服 投诉资源
www.panoramacn.com资源全部来自互联网收集,仅供用于学习和交流,请勿用于商业用途。如有侵权、不妥之处,请联系站长并出示版权证明以便删除。 敬请谅解! 侵权删帖/违法举报/投稿等事物联系邮箱:2640602276@qq.com
未经允许不得转载:书荒源码源码网每日更新网站源码模板! » Java之多线程(同步、死锁、经典问题)
关注我们小说电影免费看
关注我们,获取更多的全网素材资源,有趣有料!
120000+人已关注
分享到:
赞(0) 打赏

评论抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

您的打赏就是我分享的动力!

支付宝扫一扫打赏

微信扫一扫打赏