package cn.hncu.lang.thread_;
/**
* 项目名:实例变量和线程安全
* 时间 :2017-9-17 下午2:14:02
*/
/*
* 【实例变量与线程安全】:
* 1.自定义线程类中的实例变量指针对其他线程可以有共享与不共享之分,这在多个线程之间进行交互时是很重要的。
* 2.不共享数据的情况:—–OneThread
* 3.由下面结果看出,一共创建了三个线程,每个线程都有各自的count变量,每个线程是自己减少自己的count变量的值,
* 这样的情况就是变量不共享,此示例并不存在多个线程访问同一个实例变量的情况。
* 4.如何该共享数据?
*/
class OneThread extends Thread{
private int count = 6;
public OneThread(String name){
this.setName(name);
}
@Override
public void run() {
while(count>0){
count--;
System.out.println("由"+this.currentThread().getName()+
"计算,count = "+count);
}
}
public static void main(String[] args) {
OneThread a = new OneThread("A");
OneThread b = new OneThread("B");
OneThread c = new OneThread("C");
a.start();
b.start();
c.start();
/*执行结果为:
由C计算,count = 5
由B计算,count = 5
由B计算,count = 4
由B计算,count = 3
由A计算,count = 5
由A计算,count = 4
由A计算,count = 3
由A计算,count = 2
由B计算,count = 2
由C计算,count = 4
由B计算,count = 1
由A计算,count = 1
由B计算,count = 0
由C计算,count = 3
由C计算,count = 2
由C计算,count = 1
由C计算,count = 0
由A计算,count = 0
*/
}
}
/*
* 【实例变量与线程安全】:
* 1.共享数据的情况:—-TwoThread
* 2.我们想让多个线程操作同一个数据,并且每个线程处理后的结果值都是不同的。
* 3.好比就是投票:A投了一票count变为6,B投了一票count变为7,C投了一票count变为8,
* 此时如果A还想投一票,则count变为9。
*
* 4.但是此时运行结果和预想的不相同,B、A、C结果都为2,存在相同的结果值。
* 5.这就说明数据没有共享,产生了“非线程安全”的问题。
* 6.但是这是什么原因呢?
* 因为在某些JVM中,i–的操作分成如下三步:(1)取得原有i值;(2)计算i-1;(3)对i进行赋值。
* 在这三个步骤中,如果有多个线程同时访问,那么一定会出现非线程安全的问题。
*
* 7.如何处理这个问题呢?—-加锁 synchronized
* 要求:在某个线程将上面的这三个步骤执行完以后,其他的线程才可以操作i,不然,其他线程一直在等待这个线程。
* 此时就会用到一个关键字来执行这一个操作:synchronized
* 8.这时就需要使多个线程之间进行同步,也就是用按顺序排队方式进行减1操作。
*
*/
class TwoThread extends Thread{
private int count = 5;
/*版本1:
@Override
public void run() {
count--;
System.out.println("由"+this.currentThread().getName()+
"计算,count = "+count);
}*/
/*版本2:为这个run方法加锁,让当前对象将这个run方法执行完毕以后才解锁。
* 【synchronized说明】:
* 1.通过在run方法前加入synchronized关键字,使多个线程在执行run方法时 ,以排队的方式进行处理。
* 2.当一个线程调用run前,先判断run方法有没有被上锁,如果上锁,说明有其他线程正在调用run方法,必须等
* 其他线程对run方法调用结束后才可以执行run方法。这样也就实现了排队调用run方法的目的,也就达到了按顺序
* 对count变量减1的效果了。
* 3.synchronized可以在任意对象及方法上加锁,而加锁的这段代码称为“互斥区”或“临界区”。
* 4.当一个线程想要执行同步方法里面的代码时,线程首先尝试去拿着把锁,若果能够拿到这把锁,
* 那么这个线程就可以执行synchronized里面的代码。如果不能拿到这把锁,那么这个线程就
* 会不断的尝试拿这把锁,直到能够拿到为止,而且是多个线程同时去争抢这把锁。
*
*/
@Override
synchronized public void run() {
count--;
System.out.println("由"+this.currentThread().getName()+
"计算,count = "+count);
}
public static void main(String[] args) {
TwoThread twoThread = new TwoThread();
Thread t1 = new Thread(twoThread, "A");
Thread t2 = new Thread(twoThread, "B");
Thread t3 = new Thread(twoThread, "C");
Thread t4 = new Thread(twoThread, "D");
Thread t5 = new Thread(twoThread, "E");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
/*版本1执行结果:
由B计算,count = 2
由A计算,count = 2
由E计算,count = 0
由C计算,count = 2
由D计算,count = 1
版本2执行结果:
由A计算,count = 4
由E计算,count = 3
由D计算,count = 2
由C计算,count = 1
由B计算,count = 0
*/
}
}
public class InstanceVariableAndThreadSafe {
public static void main(String[] args) {
}
}
/*
* 【非线程安全】:
* 1.非线程安全主要是指多个线程对同一个对象中的同一个实例变量进行操作时会出现
* 值被更改、值不同步的情况,进而影响程序的执行流程。
*
* 【说明】对于LoginServlet中的doPost方法的说明:
* 1.两个线程同时访问同一个类中的静态方法,因此doPost的操作是:
* 2.doPost中传入两个参数,首先usernameRef = username;赋值;
* 3.赋值以后,如果:username.equals(“a”),则当前线程睡眠,其他线程进入此doPost方法;
* 4.首先usernameRef = username;赋值;如果username.equals(“a”)不成立,则
* 5.passwordRef = password;,此时输出当前传入的两个参数,usernameRef、password
* 6.最后,另外一个线程开始运行,开始执行: passwordRef = password;,
* 7.此时输出当前传入的两个参数,usernameRef、password,但是usernameRef已被另一个线程改掉。
B username = a, password = bb
A username = a, password = aa
加锁以后:
A username = a, password = aa
B username = b, password = bb
*
*/
class LoginServlet{
private static String usernameRef;
private static String passwordRef;
synchronized public static void doPost(String username,String password){
try {
usernameRef = username;
if(username.equals("a")){
Thread.sleep(5000);
}
passwordRef = password;
System.out.println(Thread.currentThread().getName()+" username = "
+usernameRef+", password = "+password);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ALogin extends Thread{
@Override
public void run() {
this.setName("A");
LoginServlet.doPost("a", "aa");
}
}
class BLogin extends Thread{
@Override
public void run() {
this.setName("B");
LoginServlet.doPost("b", "bb");
}
}
class Run{
public static void main(String[] args) {
ALogin a = new ALogin();
BLogin b = new BLogin();
a.start();
b.start();
}
}