多线程2

如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。


其实,线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

演示卖票多线程案例:
package cn.itcast.selltickets;

public class TheadDemo {

public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread t0 = new Thread(ticket);
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
t0.start();
t1.start();
t2.start();
}
}

 

package cn.itcast.selltickets;

public class Ticket implements Runnable {


private int num = 100;

public void run() {
while(true) {
if (num > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "出售第" + num--);
}
}
}
}

 


Thread-0出售第100
Thread-1出售第99
Thread-2出售第100
Thread-0出售第98
Thread-1出售第97
Thread-2出售第96
Thread-0出售第95
Thread-1出售第94
Thread-2出售第93
Thread-0出售第92
Thread-2出售第91
Thread-1出售第90
Thread-0出售第88
Thread-2出售第89
Thread-1出售第87
Thread-0出售第86
Thread-1出售第85
Thread-2出售第84
Thread-0出售第83
Thread-1出售第82
Thread-2出售第81
Thread-0出售第80
Thread-2出售第79
Thread-1出售第78
Thread-0出售第77
Thread-1出售第76
Thread-2出售第75
Thread-0出售第74
Thread-2出售第73
Thread-1出售第72
Thread-0出售第71
Thread-2出售第70
Thread-1出售第69
Thread-0出售第68
Thread-1出售第67
Thread-2出售第66
Thread-0出售第65
Thread-1出售第64
Thread-2出售第63
Thread-0出售第62
Thread-2出售第61
Thread-1出售第60
Thread-0出售第59
Thread-2出售第58
Thread-1出售第57
Thread-0出售第56
Thread-1出售第55
Thread-2出售第54
Thread-0出售第53
Thread-2出售第52
Thread-1出售第51
Thread-0出售第50
Thread-1出售第49
Thread-2出售第49
Thread-0出售第48
Thread-1出售第47
Thread-2出售第46
Thread-0出售第45
Thread-2出售第44
Thread-1出售第43
Thread-0出售第42
Thread-1出售第41
Thread-2出售第40
Thread-0出售第39
Thread-2出售第38
Thread-1出售第37
Thread-0出售第36
Thread-1出售第35
Thread-2出售第34
Thread-0出售第33
Thread-2出售第32
Thread-1出售第31
Thread-0出售第30
Thread-2出售第29
Thread-1出售第28
Thread-0出售第27
Thread-2出售第26
Thread-1出售第25
Thread-0出售第24
Thread-1出售第23
Thread-2出售第22
Thread-0出售第21
Thread-2出售第20
Thread-1出售第19
Thread-0出售第18
Thread-2出售第17
Thread-1出售第16
Thread-0出售第15
Thread-2出售第14
Thread-1出售第13
Thread-0出售第12
Thread-1出售第11
Thread-2出售第10
Thread-0出售第9
Thread-2出售第8
Thread-1出售第7
Thread-0出售第6
Thread-1出售第5
Thread-2出售第4
Thread-0出售第3
Thread-1出售第2
Thread-2出售第1
Thread-0出售第0
Thread-1出售第-1

用同步代码块synchronized解决
package cn.itcast.selltickets;

public class Ticket implements Runnable {

private int num = 100;

private Object o = new Object();

public void run() {
while (true) {
synchronized (o) {
if (num > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "出售第" + num--);
}
}
}
}
}

用同步方法解决
package cn.itcast.selltickets;

public class Ticket implements Runnable {

private int num = 100;


public synchronized void run() {
while (true) {
if (num > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "出售第" + num--);
}
}
}
}

java中提供了线程同步机制,它能够解决上述的线程安全问题。
线程同步的方式有两种:
? 方式1:同步代码块
? 方式2:同步方法
同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。

 

1.2.2 同步方法
? 同步方法:在方法声明上加上synchronized
public synchronized void method(){
可能会产生线程安全问题的代码
}
同步方法中的锁对象是 this

? 静态同步方法: 在方法声明上加上static synchronized
public static synchronized void method(){
可能会产生线程安全问题的代码
}
静态同步方法中的锁对象是 类名.class


用lock接口解决安全性
package cn.itcast.selltickets;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Ticket implements Runnable {

private int num = 100;


private Lock lock = new ReentrantLock();
public void run() {
while (true) {
lock.lock();
if (num > 0) {
try {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "出售第" + num--);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
lock.unlock();

}
}

}
}
}

 


同步锁使用的弊端:当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步。这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁。

 

 


死锁代码实现
package cn.itcast.deadlock;

public class DeadLock implements Runnable {

private int i = 0;
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
if (i % 2 == 0) {
synchronized(LockA.locka){
System.out.println("if...locka" + Thread.currentThread().getName());
synchronized(LockB.lockb) {
System.out.println("if...lockb" + Thread.currentThread().getName());
}
}
}else {
synchronized (LockB.lockb) {
System.out.println("else...lockb" + Thread.currentThread().getName());
synchronized (LockA.locka) {
System.out.println("else...locka" + Thread.currentThread().getName());
}
}
}
//i=8,刚从if出来,正准备加1,这时cpu被另一个线程抢了,另一个想成走if,把A锁拿了,这时,cpu又被抢了另一个线程i++,走else,把B锁抢了
//,这时拿到A锁的线程即使抢到cpu资源由于没有b锁,也阻塞了,另一个线程同样的.
i++;
}
}

}

 


package cn.itcast.deadlock;

public class Demo {

public static void main(String[] args) {
DeadLock dead = new DeadLock();
Thread t0 = new Thread(dead);
Thread t1 = new Thread(dead);
t0.start();
t1.start();
}
}

 


package cn.itcast.deadlock;

public class LockA {
private LockA() {
}

public static final LockA locka = new LockA();
}

 


package cn.itcast.deadlock;

public class LockB {

private LockB(){}
public static final LockB lockb = new LockB();
}

 


if...lockaThread-0
if...lockbThread-0
else...lockbThread-0
else...lockaThread-0
if...lockaThread-0
if...lockbThread-0
else...lockbThread-0
else...lockaThread-0
if...lockaThread-0
if...lockbThread-0
else...lockbThread-0
if...lockaThread-1

 

 

线程通信:
package cn.itcast.threadnotify;

public class Demo {

public static void main(String[] args) {
Resource r = new Resource();
Output out = new Output(r);
Input in = new Input(r);
Thread t0 = new Thread(in);
Thread t1 = new Thread(out);
t0.start();
t1.start();


}
}

 

package cn.itcast.threadnotify;

public class Input implements Runnable {

private Resource r;

public Input(Resource r) {
this.r = r;
}

@Override
public void run() {
// TODO Auto-generated method stub
int i = 0;
while (true) {
synchronized (r) {
if (r.flag) {
try {
r.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (i % 2 == 0) {
r.name = "张三";
r.sex = "男";
} else {
r.name = "lisi";
r.sex = "nv";
}
//将对方线程唤醒,标记改为true
r.flag = true;
r.notify();
}
i++;
}
}

}

 

 

 

package cn.itcast.threadnotify;

public class Output implements Runnable {

private Resource r;

public Output(Resource r) {
this.r = r;
}

@Override
public void run() {
// TODO Auto-generated method stub

while (true) {
synchronized (r) {
if (!r.flag) {
try {
r.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

System.out.println(r.name + ".." + r.sex);
r.flag = false;
r.notify();
}
}
}

}

 

 

 

package cn.itcast.threadnotify;

public class Resource {

public String name;
public String sex;
public boolean flag = true;

}

 

 

 

 

 

 

 

在开始讲解等待唤醒机制之前,有必要搞清一个概念——线程之间的通信:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。通过一定的手段使各个线程能有效的利用资源。而这种手段即—— 等待唤醒机制。

等待唤醒机制所涉及到的方法:

l  wait() :等待,将正在执行的线程释放其执行资格 和 执行权,并存储到线程池中。

l  notify():唤醒,唤醒线程池中被wait()的线程,一次唤醒一个,而且是任意的。

l  notifyAll(): 唤醒全部:可以将线程池中的所有wait() 线程都唤醒。

其实,所谓唤醒的意思就是让 线程池中的线程具备执行资格。必须注意的是,这些方法都是在 同步中才有效。同时这些方法在使用时必须标明所属锁,这样才可以明确出这些方法操作的到底是哪个锁上的线程。

仔细查看JavaAPI之后,发现这些方法 并不定义在 Thread中,也没定义在Runnable接口中,却被定义在了Object类中,为什么这些操作线程的方法定义在Object类中?

因为这些方法在使用时,必须要标明所属的锁,而锁又可以是任意对象。能被任意对象调用的方法一定定义在Object类中。

 

 

l  同步锁

多个线程想保证线程安全,必须要使用同一个锁对象

n  同步代码块

         synchronized (锁对象){

    可能产生线程安全问题的代码

}

                           同步代码块的锁对象可以是任意的对象

 

n  同步方法

         public synchronized void method()

              可能产生线程安全问题的代码

}

                           同步方法中的锁对象是 this

 

n  静态同步方法

public synchronized void method()

              可能产生线程安全问题的代码

}

静态同步方法中的锁对象是 类名.class

 

l  多线程有几种实现方案,分别是哪几种?

         a, 继承Thread类

         b, 实现Runnable接口

         c, 通过线程池,实现Callable接口

 

l  同步有几种方式,分别是什么?

         a,同步代码块

         b,同步方法

           静态同步方法

 

l  启动一个线程是run()还是start()?它们的区别?

         启动一个线程是start()

         区别:

                  start: 启动线程,并调用线程中的run()方法

                  run  : 执行该线程对象要执行的任务

 

l  sleep()和wait()方法的区别

         sleep: 不释放锁对象, 释放CPU使用权

                           在休眠的时间内,不能唤醒

                  wait(): 释放锁对象, 释放CPU使用权

                           在等待的时间内,能唤醒

 

l  为什么wait(),notify(),notifyAll()等方法都定义在Object类中

         锁对象可以是任意类型的对象

 

posted @ 2018-05-29 21:32  写代码的地方  阅读(97)  评论(0编辑  收藏  举报