线程同步、死锁和通信——Java多线程(二)
一、多线程同步
上一篇随笔中,我曾遇到对多线程程序的多次运行结果不一致的情况,这主要是因为没有对这些线程在访问临界资源做必要的控制,而接下来就用线程的同步来解决这个问题。
1.同步代码块
class RunnableDemo implements Runnable
{
private int tickets=5;
public void run()
{
while(true)
{
synchronized(this)//同步代码块语法定义如下
{
if(tickets<=0) break;
try{
Thread.sleep(100);
}
catch(Exception e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"出售票:"+tickets);
tickets -= 1;
}
}
}
} public class ThreadTest
{
public static void main(String[] args)
{
RunnableDemo r = new RunnableDemo();
new Thread(r).start();
new Thread(r).start();
new Thread(r).start();
new Thread(r).start();
}
}
在同一时刻只能由一个线程进入同步代码块内运行,只有当该线程离开同步代码块后,其他线程才能进入同步代码块内运行。
2.同步方法
即:把上述例子中同步代码块的内容,专门封装在一个方法里,通过在run()方法中调用创建的方法实现相应的功能。
class RunnableDemo implements Runnable
{
private int tickets=5;
public void run()
{
while(tickets>0)
{
sale();
}
}
public synchronized void sale()//同步方法
{
if(tickets>0){
try{
Thread.sleep(100);
}
catch(Exception e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"出售票:"+tickets);
tickets -= 1;
}
}
} public class ThreadTest
{
public static void main(String[] args)
{
RunnableDemo r = new RunnableDemo();
new Thread(r).start();
new Thread(r).start();
new Thread(r).start();
new Thread(r).start();
}
}
二、死锁
如果有一组进程(或线程),线程1 已经占据资源R1,并持有资源R1上的锁,而且正在等待资源R2开锁;线程2已经占据资源R2,并拥有资源R2上的锁,却正在等待R1开锁。那么这两个线程都不释放自己占据的资源,同时申请不到对方资源上的锁,它们只能永远等待下去。这种现象就叫做死锁。
预防死锁的一种方法:利用有序资源分配策略——要求线程申请资源必须按照以编号上升的次序依次申请。
三、线程间通信
同属于一个进程的多个线程,是共享地址空间的,它们可以相互通信,共同协作来完成指定任务。
Java是通过Object类的wait()、notify()、notifyAll()这几个方法来实现线程间的通信。
wait():线程进入睡眠状态,直到其他线程进入并调用notify()或notifyAll()为止。
notify():唤醒在该同步代码块中第1个调用wait()的线程。
notifyAll():唤醒在该同步代码块中所有调用wait()的线程,高优先级最先被唤醒。
class Producer implements Runnable
{
Person q = null;
public Producer(Person q)
{
this.q=q;
}
@Override
public void run()
{
for(int i=0;i<10;i++)
{
if(i%2==0)
{
q.set("张三","男");
}
else{
q.set("李四","女");
}
}
}
}
class Consumer implements Runnable
{
Person q = null;
public Consumer(Person q)
{
this.q=q;
}
@Override
public void run()
{
for(int i=0;i<10;++i)
{
q.get();
}
}
}
class Person
{
private String name = "李四";
private String sex = "女";
private boolean bFull = false;//当Consumer线程取走数据后,false
public synchronized void set(String name,String sex)
{
if(bFull)
{
try
{
wait();//后来的线程要等待
}catch(InterruptedException e){
e.printStackTrace();
}
}
this.name = name;
this.sex = sex;
bFull = true;//当Producer线程放入数据后,true
notify();//唤醒最先到达的线程
}
public synchronized void get()
{
if(!bFull)
{
try{
wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println(name+"——>"+sex);
bFull = false;
notify();
}
}
public class ThreadCommunation
{
public static void main(String[] args)
{
Person q = new Person();
new Thread(new Producer(q)).start();
new Thread(new Consumer(q)).start();
}
}
***注意:wait()、notify()、notifyAll()这三个方法必须在synchronized方法中调用,该线程必须得到该对象的所有权。
四、线程的生命周期
控制线程生命周期的方法:suspend()、resume()、stop()方法,但是这三个方法都不推荐使用。
若想控制线程的生命周期,推荐使用在run()方法中添加循环条件的方法来实现对线程生命周期的控制。
线程同步、死锁和通信——Java多线程(二)的更多相关文章
- 多线程,线程类三种方式,线程调度,线程同步,死锁,线程间的通信,阻塞队列,wait和sleep区别?
重难点梳理 知识点梳理 学习目标 1.能够知道什么是进程什么是线程(进程和线程的概述,多进程和多线程的意义) 2.能够掌握线程常见API的使用 3.能够理解什么是线程安全问题 4.能够知道什么是锁 5 ...
- java 多线程二
java 多线程一 java 多线程二 java 多线程三 java 多线程四 线程中断: /** * Created by root on 17-9-30. */ public class Test ...
- java多线程(二)
线程的阻塞状态: 参考java多线程(一)多线程的生命周期图解,多线程的五种状态. 1.1 join(),如果在A线程体里面执行了B线程的join()方法,那么A线程阻塞,直到B线程生命周期结 ...
- java多线程二之线程同步的三种方法
java多线程的难点是在:处理多个线程同步与并发运行时线程间的通信问题.java在处理线程同步时,常用方法有: 1.synchronized关键字. 2.Lock显示加锁. 3.信号量Se ...
- Java多线程(二) 多线程的锁机制
当两条线程同时访问一个类的时候,可能会带来一些问题.并发线程重入可能会带来内存泄漏.程序不可控等等.不管是线程间的通讯还是线程共享数据都需要使用Java的锁机制控制并发代码产生的问题.本篇总结主要著名 ...
- java多线程(八)-死锁问题和java多线程总结
为了防止对共享受限资源的争夺,我们可以通过synchronized等方式来加锁,这个时候该线程就处于阻塞状态,设想这样一种情况,线程A等着线程B完成后才能执行,而线程B又等着线程C,而线程C又等着线程 ...
- Java多线程(二) —— 深入剖析ThreadLocal
对Java多线程中的ThreadLocal类还不是很了解,所以在此总结一下. 主要参考了http://www.cnblogs.com/dolphin0520/p/3920407.html 中的文章. ...
- 从零开始学习Java多线程(二)
前面已经简单介绍进程和线程,为后续学习做铺垫.本文讨论多线程传参,Java多线程异常处理机制. 1. 多线程的参数传递 在传统开发过程中,我们习惯在调用函数时,将所需的参数传入其中,通过函数内部逻辑处 ...
- 经典线程同步问题(生产者&消费者)--Java实现
生产者-消费者(producer-consumer)问题是一个著名的线程同步问题.它描述的是:有一群生产者线程在生产产品,并将这些产品提供给消费者线程去消费. 为使生产者与消费者之间能够并发执行,在两 ...
随机推荐
- netty百万连接跟踪记录
0. 启动客户端和服务端 # 测试环境: centos7 jdk8 2核16G# 服务端启动nohup java -Xmx8192m -Xms4096m -XX:+UseG1GC -XX:Parall ...
- 文件的上传(可以上传照片,word文档,等单个文件)
jsp: jsp页面: <LINK href="${basePath}plugins/uploadify/uploadify.css" type="text/css ...
- Java基础12一IO流
1.IO流的原理 利用数据通道实现程序和数据源之间数据的的读写操作. 2.IO流分类 输入流.输出流.字节流.字符流.节点流.过滤流 3.InputStream 字节输入流 实现类FileInpu ...
- (转)vuex2.0 基本使用(2) --- mutation 和 action
我们的项目非常简单,当点击+1按钮的时候,count 加1,点击-1按钮的时候,count 减1. 1, mutation The only way to actually change state ...
- CNN结构:SPP-Net为CNNs添加空间尺度卷积-神经元层
前几个CNN检测的框架要求网络的图像输入为固定长宽,而SPP-Net在CNN结构中添加了一个实现图像金字塔功能的卷积层SPP层,用于在网络中实现多尺度卷积,由此对应多尺度输入,以此应对图像的缩放变换和 ...
- [Advanced Algorithm] - Exact Change
题目 设计一个收银程序 checkCashRegister(),其把购买价格(price)作为第一个参数 , 付款金额 (cash)作为第二个参数, 和收银机中零钱 (cid) 作为第三个参数. ci ...
- Uoj #274. 【清华集训2016】温暖会指引我们前行 LCT维护边权_动态最小生成树
Code: 行#include<bits/stdc++.h> #define ll long long #define maxn 1000000 #define inf 100000000 ...
- esp32使iOS 获取蓝牙外设的Mac地址
最近在做一个需要上下位机的项目,我负责的任务下位机,使用的主控芯片是esp32.这个项目中有一项是需要手机扫描二维码然后连接作为esp32的蓝牙.二维码中包含了mac地址信息,在手机扫描周围设备的时候 ...
- 【双系统】windows 和 Ubuntu 双系统安装
本博客主要讲述如何在已安装windows系统的计算机上安装Ubuntu双系统,涉及系统安装和相应磁盘空间分配等问题. 所需环境: 电脑已安装windows系统 下载Ubuntu16.04系统镜像 ...
- 洛谷P1012 拼数【字符串+排序】
设有nn个正整数(n≤20)(n≤20),将它们联接成一排,组成一个最大的多位整数. 例如:n=3n=3时,33个整数1313,312312,343343联接成的最大整数为:3433121334331 ...