Java Tread多线程(2)多线程安全问题
2014-09-17 22:40
405 查看
本文演示,Tread多线程安全问题,以及几种解决多线程安全方式(线程同步)。
1)一个线程不安全的Demo
2)线程同步(synchronized,函数同步,this锁,Class对象锁)
一、小Demo演示引出线程安全问题:
运行显示结果:
以上打印出了0,-1,-2等数字,发现没?(线程出问题了吧!!!)
问题:出问题的原因是什么呢?
解释:线程1进入,强制睡眠10ms;此时线程2进入,又强制睡眠10ms;线程3进入又强制睡眠10ms;线程4进入再强制睡眠10ms;
注意,以上4个线程睡眠时都已经进入了if语句,进入的时候x>0还是成立的;
好了,线程1醒来,开始打印打印5,4,3,2,这时候--x还没执行,线程2就醒来了,抢去了cpu的执行权.....................
二、线程同步
问题:对于上面的问题,我们是不是可以采取一个这样的措施?当线程1执行run代码段的时候,我们不让其他的线程来执行,直到线程1执行完,其他的线程才可以进入。
解决方案:好在Java里面本来就有这样的函数,将代码段包裹起来,就可以达到上面问题描述的效果。函数名:synchronized,需要一个参数,随便传个对象就ok了(具体参数区别分析,请往下拉见附录1)。
1)一个简单的解决方案:
2)换一种更简单的解决方法:函数同步(将synchronized直接添加在函数前面)
说明:有没有发现,函数就是对多个语句的打包?但是,函数只是打包,而没有像上面的synchronized一样实现同步。但是有一种方法可以达到这个效果,看下面的小Demo就明白了。
附录1:对于函数锁中synchronized中的参数的解释分析(我们先看一下下面这个小Demo)
郁闷吧,又出现线程安全问题了,可是我们分明都加了锁的呀?这是什么原因导致的呢?其实,问题就出在了,synchronized里面的参数上。
函数同步锁定的是this,而上面的小Demo里面,一个是函数同步锁(this),另外一个是obj锁,发现没?解决方案:将obj锁改为this锁就没问题了。
解决后的小Demo:
附录2:提出另外一个问题,既然函数同步锁定的是this,那么如下的这个函数要如何解释?静态函数没有this,如何锁定?
解决后的小Demo:
运行显示结果:(同上)
1)一个线程不安全的Demo
2)线程同步(synchronized,函数同步,this锁,Class对象锁)
一、小Demo演示引出线程安全问题:
package thread.runable1.qdj; //1.定义类实现Runnable接口 class RunDemo1 implements Runnable { private int x = 5; //2.覆盖Runnable接口中的run方法 //将线程代码存放在run中 public void run() { while (true) { if (x > 0) { //添加sleep(),注意:sleep()会抛出异常 try { Thread.sleep(10); //让线程睡眠10ms } catch (Exception e) { e.printStackTrace(); } System.out.println("Runnable:"+x); --x; } } } } public class CRunableDemo1 { public static void main(String[] args) { RunDemo1 r = new RunDemo1(); //3.通过Thread类建立线程对象,并将Runnable接口的子类对象作为参数 Thread t1 = new Thread(r); Thread t2 = new Thread(r); Thread t3 = new Thread(r); Thread t4 = new Thread(r); //4.使用start开启线程 t1.start(); t2.start(); t3.start(); t4.start(); } }
运行显示结果:
以上打印出了0,-1,-2等数字,发现没?(线程出问题了吧!!!)
问题:出问题的原因是什么呢?
解释:线程1进入,强制睡眠10ms;此时线程2进入,又强制睡眠10ms;线程3进入又强制睡眠10ms;线程4进入再强制睡眠10ms;
注意,以上4个线程睡眠时都已经进入了if语句,进入的时候x>0还是成立的;
好了,线程1醒来,开始打印打印5,4,3,2,这时候--x还没执行,线程2就醒来了,抢去了cpu的执行权.....................
二、线程同步
问题:对于上面的问题,我们是不是可以采取一个这样的措施?当线程1执行run代码段的时候,我们不让其他的线程来执行,直到线程1执行完,其他的线程才可以进入。
解决方案:好在Java里面本来就有这样的函数,将代码段包裹起来,就可以达到上面问题描述的效果。函数名:synchronized,需要一个参数,随便传个对象就ok了(具体参数区别分析,请往下拉见附录1)。
1)一个简单的解决方案:
package thread.runable1.qdj; //1.定义类实现Runnable接口 class RunDemo1 implements Runnable { private int x = 5; Object obj = new Object(); //2.覆盖Runnable接口中的run方法 //将线程代码存放在run中 public void run() { while (true) { synchronized (obj) { if (x > 0) { //添加sleep(),注意:sleep()会抛出异常 try { Thread.sleep(10); //让线程睡眠10ms } catch (Exception e) { e.printStackTrace(); } System.out.println("Runnable:"+x); --x; } } } } } public class CRunableDemo1 { public static void main(String[] args) { RunDemo1 r = new RunDemo1(); //3.通过Thread类建立线程对象,并将Runnable接口的子类对象作为参数 Thread t1 = new Thread(r); Thread t2 = new Thread(r); Thread t3 = new Thread(r); Thread t4 = new Thread(r); //4.使用start开启线程 t1.start(); t2.start(); t3.start(); t4.start(); } }运行结果显示:
2)换一种更简单的解决方法:函数同步(将synchronized直接添加在函数前面)
说明:有没有发现,函数就是对多个语句的打包?但是,函数只是打包,而没有像上面的synchronized一样实现同步。但是有一种方法可以达到这个效果,看下面的小Demo就明白了。
package thread.runable1.qdj; //同步函数 class SynFunc { private int x = 5; public synchronized void aFunc() { while (true) { if (x > 0) { //添加sleep(),注意:sleep()会抛出异常 try { Thread.sleep(10); //让线程睡眠10ms } catch (Exception e) { e.printStackTrace(); } System.out.println("Runnable:"+x); --x; } } } } //1.定义类实现Runnable接口 class RunDemo1 implements Runnable { private SynFunc syn = new SynFunc(); //2.覆盖Runnable接口中的run方法 //将线程代码存放在run中 public void run() { syn.aFunc(); } } public class CRunableDemo1 { public static void main(String[] args) { RunDemo1 r = new RunDemo1(); //3.通过Thread类建立线程对象,并将Runnable接口的子类对象作为参数 Thread t1 = new Thread(r); Thread t2 = new Thread(r); Thread t3 = new Thread(r); Thread t4 = new Thread(r); //4.使用start开启线程 t1.start(); t2.start(); t3.start(); t4.start(); } }运行结果显示:(同上)
附录1:对于函数锁中synchronized中的参数的解释分析(我们先看一下下面这个小Demo)
package thread.runable1.qdj; //1.定义类实现Runnable接口 class RunDemo1 implements Runnable { private int x = 100; Object obj = new Object(); boolean flag = true; //2.覆盖Runnable接口中的run方法 //将线程代码存放在run中 public void run() { if (flag) { while (true) { synchronized(obj) { if (x > 0) { //添加sleep(),注意:sleep()会抛出异常 try { Thread.sleep(10); //让线程睡眠10ms } catch (Exception e) { e.printStackTrace(); } System.out.println("Runnable:"+x); --x; } } } } else { while (true) aFunc(); } } public synchronized void aFunc() { while (true) { if (x > 0) { //添加sleep(),注意:sleep()会抛出异常 try { Thread.sleep(10); //让线程睡眠10ms } catch (Exception e) { e.printStackTrace(); } System.out.println("Runnable:"+x); --x; } } } } public class CRunableDemo1 { public static void main(String[] args) { RunDemo1 r = new RunDemo1(); //3.通过Thread类建立线程对象,并将Runnable接口的子类对象作为参数 Thread t1 = new Thread(r); Thread t2 = new Thread(r); //4.使用start开启线程 t1.start(); //添加sleep(),注意:sleep()会抛出异常 try { Thread.sleep(10); //让线程睡眠10ms } catch (Exception e) { e.printStackTrace(); } r.flag = false; t2.start(); } }运行显示结果:
郁闷吧,又出现线程安全问题了,可是我们分明都加了锁的呀?这是什么原因导致的呢?其实,问题就出在了,synchronized里面的参数上。
函数同步锁定的是this,而上面的小Demo里面,一个是函数同步锁(this),另外一个是obj锁,发现没?解决方案:将obj锁改为this锁就没问题了。
解决后的小Demo:
package thread.runable1.qdj; //1.定义类实现Runnable接口 class RunDemo1 implements Runnable { private int x = 100; Object obj = new Object(); boolean flag = true; //2.覆盖Runnable接口中的run方法 //将线程代码存放在run中 public void run() { if (flag) { while (true) { synchronized(this) { if (x > 0) { //添加sleep(),注意:sleep()会抛出异常 try { Thread.sleep(10); //让线程睡眠10ms } catch (Exception e) { e.printStackTrace(); } System.out.println("Runnable:"+x); --x; } } } } else { while (true) aFunc(); } } public synchronized void aFunc() { while (true) { if (x > 0) { //添加sleep(),注意:sleep()会抛出异常 try { Thread.sleep(10); //让线程睡眠10ms } catch (Exception e) { e.printStackTrace(); } System.out.println("Runnable:"+x); --x; } } } } public class CRunableDemo1 { public static void main(String[] args) { RunDemo1 r = new RunDemo1(); //3.通过Thread类建立线程对象,并将Runnable接口的子类对象作为参数 Thread t1 = new Thread(r); Thread t2 = new Thread(r); //4.使用start开启线程 t1.start(); //添加sleep(),注意:sleep()会抛出异常 try { Thread.sleep(10); //让线程睡眠10ms } catch (Exception e) { e.printStackTrace(); } r.flag = false; t2.start(); } }运行显示结果:
附录2:提出另外一个问题,既然函数同步锁定的是this,那么如下的这个函数要如何解释?静态函数没有this,如何锁定?
public static synchronized void aFunc() { while (true) { if (x > 0) { //添加sleep(),注意:sleep()会抛出异常 try { Thread.sleep(10); //让线程睡眠10ms } catch (Exception e) { e.printStackTrace(); } System.out.println("Runnable:"+x); --x; } } }解决方案:静态函数,是类函数由类调用。其实这里static函数的锁是Class对象。下面注意看如下小Demo中synchronized的参数。
解决后的小Demo:
package thread.runable1.qdj;
//1.定义类实现Runnable接口
class RunDemo1 implements Runnable
{
private static int x = 100;
boolean flag = true;
//2.覆盖Runnable接口中的run方法
//将线程代码存放在run中
public void run()
{
if (flag)
{
while (true)
{
synchronized(RunDemo1.class)
{
if (x > 0)
{
//添加sleep(),注意:sleep()会抛出异常
try {
Thread.sleep(10); //让线程睡眠10ms
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Runnable:"+x);
--x;
}
}
}
}
else
{
while (true)
aFunc();
}
}
public static synchronized void aFunc() { while (true) { if (x > 0) { //添加sleep(),注意:sleep()会抛出异常 try { Thread.sleep(10); //让线程睡眠10ms } catch (Exception e) { e.printStackTrace(); } System.out.println("Runnable:"+x); --x; } } }
}
public class CRunableDemo1 {
public static void main(String[] args) {
RunDemo1 r = new RunDemo1();
//3.通过Thread类建立线程对象,并将Runnable接口的子类对象作为参数
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
//4.使用start开启线程
t1.start();
//添加sleep(),注意:sleep()会抛出异常
try {
Thread.sleep(10); //让线程睡眠10ms
} catch (Exception e) {
e.printStackTrace();
}
r.flag = false;
t2.start();
}
}
运行显示结果:(同上)
相关文章推荐
- Java Tread多线程(2)多线程安全问题
- 黑马程序员_java的多线程(对第十一课创建多线程及多线程安全问题总结)
- 《黑马程序员》java笔记->多线程安全问题
- java多线程的等待唤醒机制及如何解决同步过程中的安全问题
- java 学习-----多线程的安全问题
- 黑马程序员:Java基础——多线程之安全问题与同步
- Java多线程技术初识——4. 多线程安全问题的解决
- 黑马程序员--Java 多线程的安全问题
- java基础知识回顾之java Thread类学习(四)--java多线程安全问题(锁)
- Java——多线程安全问题
- java多线程安全问题
- Java笔记3 多线程<1>线程概述、多线程的创建、多线程的安全问题、静态同步函数的锁、死锁
- 【黑马程序员】Java基础05:多线程与其安全问题
- java基础知识回顾之java Thread类学习(五)--java多线程安全问题(锁)同步的前提
- Java 学习笔记16:用ThreadLocal解决多线程安全问题
- 黑马程序员——java中关于同步函数(或同步代码块)解决多线程安全问题的加锁
- Java基础——多线程的安全问题
- java 多线程(线程间通信-解决安全问题)
- 黑马程序员——JAVA基础之多线程的安全问题
- java基础知识回顾之java Thread类学习(七)--java多线程安全问题(死锁)