线程同步、死锁和通信——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)问题是一个著名的线程同步问题.它描述的是:有一群生产者线程在生产产品,并将这些产品提供给消费者线程去消费. 为使生产者与消费者之间能够并发执行,在两 ...
随机推荐
- java 关键字与保留字
Java 关键字列表 (依字母排序 共51组),所有的关键字都是小写,在MyEclipse中都会显示不同的颜色: abstract, assert,boolean, break, byte, case ...
- 怪异的Ubuntu
怪异的Ubuntu 简单记录ubuntu上出现并且网上不好找到甚至压根找不到解决方案的疑难杂症. lvextend扩展逻辑卷的容量不能被系统检测到 问题发生在Ubuntu 16.04系统上. 逻辑卷/ ...
- 【sicily】 1934. 移动小球
Description 你有一些小球,从左到右依次编号为1,2,3,...,n. 你可以执行两种指令(1或者2).其中, 1 X Y表示把小球X移动到小球Y的左边, 2 X Y表示把小球X移动到小球Y ...
- 08 Django组件-Forms组件
Django的Forms组件主要有以下几大功能: 页面初始化,生成HTML标签 校验用户数据(显示错误信息) HTML Form提交保留上次提交数据 一.小试牛刀 1.定义Form类 from dja ...
- laravel中ubuntu下执行php artisan migrate总是报错
ubuntu14.0 + xampp + laravel5下 laravel中ubuntu下执行php artisan migrate总是报错: [PDOException] could not fi ...
- 洛谷P1025 数的划分【dp】
将整数nn分成kk份,且每份不能为空,任意两个方案不相同(不考虑顺序). 例如:n=7n=7,k=3k=3,下面三种分法被认为是相同的. 1,1,51,1,5; 1,5,11,5,1; 5,1,15, ...
- [luogu2329 SCOI2005] 栅栏(二分+搜索)
传送门 Solution 纯搜索80分,加二分90分,再补一个小剪枝满分qwq 真.小剪枝:如果下一个的需求和当前相同,那么不需要再次从头开始试(看代码就明白了233) Code #include & ...
- ldap 用户组和用户(4)
Posixgroup用户组属性 默认情况下openldap的用户组属性是Posixgroup,Posixgroup用户组属性和用户没有实际的对应关系.如果我们一定要把Posixgroup和user对应 ...
- PHP过滤html注释
过滤html注释: 所谓过滤,不过是字符串的匹配与替换,这里我们用到的正则匹配替换函数preg_replace(reg,replace,string);,PHPer都清楚,这个函数的关键在于reg的精 ...
- keycode键盘 按键 - 键码 对应表
字母和数字键的键码值(keyCode) 按键 键码 按键 键码 按键 键码 按键 键码 A 65 J 74 S 83 1 49 B 66 K 75 T 84 2 50 C 67 L 76 U 85 3 ...