多个线程在操作统一资源,但操作的动作不同。
等待唤醒机制:
例1:
class Res{
String name;
String sex;
boolean flag = false;
}
class Input implements Runnable{
Res r = new Res();
Input(Res r){
this.r = r;
}
public void run(){
int x = 0;
while(true){
synchronized(r){//输入和输出采用同一个锁
if(r.flag){
try {
r.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(x==0){
r.name = "李雷";
r.sex = "男";
}else{
r.name = "韩梅梅";
r.sex = "女";
}
x = (x+1)%2;
r.flag = true;
r.notify();
}
}
}
}
class Output implements Runnable{
Res r = new Res();
Output(Res r){
this.r = r;
}
public void run() {
while(true){
synchronized(r){
if(!r.flag){
try {
r.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(r.name+"..."+r.sex);
r.flag= false;
r.notify();
}
}
}
}
public class InputOutputDemo {
public static void main(String[] args) {
Res r = new Res();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
运行结果:
上述代码中输入一个输出一个的机理:等待唤醒机制。
多线程加了同步依然不安全需检查是否满足以下三个条件:
wait()、notify()和notifyAll()都是用在同步中,因为都需要持有锁(监视器monitor)的线程操作,而只有同步中才有锁。
等待和唤醒必须用同一个锁,锁可以用任意Object对象,所以可以被任意对象调用的方法定义Object类中。
例2(例1优化):
class Res{
private String name;
private String sex;
private boolean flag = false;
public synchronized void set(String name,String sex){
if(flag)
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
this.name = name;
this.sex = sex;
this.flag = true;
this.notify();
}
public synchronized void out(){
if(!this.flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name+"..."+sex);
this.flag= false;
this.notify();
}
}
class Input implements Runnable{
Res r = new Res();
Input(Res r){
this.r = r;
}
public void run(){
int x = 0;
while(true){
if(x==0){
r.set("李雷", "男");
}else{
r.set("韩梅梅", "女");
}
x = (x+1)%2;
}
}
}
class Output implements Runnable{
Res r = new Res();
Output(Res r){
this.r = r;
}
public void run() {
while(true){
r.out();
}
}
}
public class InputOutputDemo {
public static void main(String[] args) {
Res r = new Res();
Thread t1 = new Thread(new Input(r));
Thread t2 = new Thread(new Output(r));
t1.start();
t2.start();
}
}
两个对象多个线程问题:
class Res{
private String name;
private String sex;
private boolean flag = false;
private int count = 1;
public synchronized void set(String name,String sex){
while(flag)
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
this.name = name+count;
this.sex = sex;
count++;
System.out.println("设置"+this.name+"..."+sex);
this.flag = true;
this.notifyAll();//需全部唤醒,否则按等待顺序下一个还是set,会覆盖之前的set
}
public synchronized void out(){
while(!this.flag){//由于存在多个线程,每次拿到锁都必须判断
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("输出..."+name+"..."+sex);
this.flag= false;
this.notifyAll();
}
}
class Input implements Runnable{
Res r = new Res();
Input(Res r){
this.r = r;
}
public void run(){
int x = 0;
while(true){
if(x==0){
r.set("李雷", "男");
}else{
r.set("韩梅梅", "女");
}
x = (x+1)%2;
}
}
}
class Output implements Runnable{
Res r = new Res();
Output(Res r){
this.r = r;
}
public void run() {
while(true){
r.out();
}
}
}
public class InputOutputDemo {
public static void main(String[] args) {
Res r = new Res();
Thread t1 = new Thread(new Input(r));
Thread t2 = new Thread(new Input(r));
Thread t3 = new Thread(new Output(r));
Thread t4 = new Thread(new Output(r));
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行结果:
注意:
while判断和notify会导致死锁出现。
Jdk1.5中将同步和锁封装为对象,并将操作锁的隐式方式定义到该对象中,将隐式动作变为显示动作。
同步中synchronized替换为了Lock,Condition 替代了 Object 监视器方法的使用。
Lock接口可以在一个锁上加上多组监视器。
Condition接口:出现替代了Object中的wait、notify、notifyAll方法。将这些监视器方法单独进行了封装,变成Condition监视器对象,可以任意锁进行组合。
Condition接口中的await方法对应于Object中的wait方法。
Condition接口中的signal方法对应于Object中的notify方法。
Condition接口中的signalAll方法对应于Object中的notifyAll方法。
import java.util.concurrent.locks.*;
class Res{
private String name;
private String sex;
private boolean flag = false;
private int count = 1;
//创建一个锁对象
private Lock lock = new ReentrantLock();
//通过已有锁获取两组监视器,一组监视设置函数,一组监视输出函数
private Condition con_set = lock.newCondition();
private Condition con_out = lock.newCondition();
public void set(String name,String sex) throws InterruptedException{
lock.lock();
try{
while(flag)
con_set.await();
this.name = name+count++;
this.sex = sex;
System.out.println("设置"+this.name+"..."+sex);
flag = true;
con_out.signalAll();
}finally{
lock.unlock();
}
}
public void out() throws InterruptedException{
lock.lock();
try{
while(!flag){//由于存在多个线程,每次拿到锁都必须判断
con_out.await();
}
System.out.println("输出..."+name+"..."+sex);
flag= false;
con_set.signalAll();
}finally{
lock.unlock();
}
}
}
class Input implements Runnable{
Res r = new Res();
Input(Res r){
this.r = r;
}
public void run(){
int x = 0;
while(true){
if(x==0){
try {
r.set("李雷", "男");
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
try {
r.set("韩梅梅", "女");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
x = (x+1)%2;
}
}
}
class Output implements Runnable{
Res r = new Res();
Output(Res r){
this.r = r;
}
public void run() {
while(true){
try {
r.out();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class InputOutputDemo {
public static void main(String[] args) {
Res r = new Res();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(in);
Thread t3 = new Thread(out);
Thread t4 = new Thread(out);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行结果:
开启多线程通常为循环结构,只要控制循环,就可以让run方法结束,即线程结束。
特殊情况:
线程处于冻结状态,无法读取标记,程序无法结束。
可以使用Thread中的interrupt()方法将线程从冻结强制恢复至运行状态,按api中的描述:
“如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。”
此时会抛出InterruptedException异常,则进行相应处理。
public class StopThreadDemo {
public static void main(String[] args) {
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
int num = 0;
while(true){
if(num++ == 60){
t1.interrupt();
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+"..."+num);
}
System.out.println("over");
}
}
class StopThread implements Runnable{
private boolean flag = true;
public synchronized void run(){
while(flag){
try {
wait();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"..Exception");
flag =false;
}
System.out.println(Thread.currentThread().getName()+"..run");
}
}
public void changeFlag(){
flag = false;
}
}
运行结果:
setDaemon()方法:
public class StopThreadDemo {
public static void main(String[] args) {
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.setDaemon(true);
t2.setDaemon(true);
t1.start();
t2.start();
int num = 0;
while(true){
if(num++ == 60){
break;
}
System.out.println(Thread.currentThread().getName()+"..."+num);
}
System.out.println("over");
}
}
class StopThread implements Runnable{
private boolean flag = true;
public synchronized void run(){
while(flag){
try {
wait();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"..Exception");
flag =false;
}
System.out.println(Thread.currentThread().getName()+"..run");
}
}
}
运行结果:
ps:作为守护线程,主线程停止,守护线程也停止。
yield方法
public static void yield()
暂停当前正在执行的线程对象,并执行其他线程。
setPriority方法
public final void setPriority(int newPriority)
更改线程的优先级。
首先调用线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException。
在其他情况下,线程优先级被设定为指定的 newPriority 和该线程的线程组的最大允许优先级相比较小的一个。