java多线程理解 以及java实现的简单的死锁
2015-04-13 12:54
435 查看
多线程,三大机制中的一个,编程问题的一个难点,在前两天的面试中,就被问到了这个问题,尴尬的是当时居然没回答上来,最后还被刷了,想起来还真是尴尬。
说到多线程,先来理解一下线程吧。
线程,也被称为“轻量级进程”,是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己几乎不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行。
线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制
从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
在java中实现多线程有两种方式:继承Runnable接口和继承Thread方法,无论哪种方式实现多线程都需要建立Thread类或它子类的实例,例如一个简单的线程。
第一种方式:
public class Car1 implements Runnable {
@Override
public void run() {
System.out.println("我是第一种方式创建的线程!");
}
public static void main(String[] args) {
Runnable r = new Car1();
Thread t = new Thread(r);
t.start();
}
}
第二种方式:
public class Car2 extends Thread {
@Override
public void run(){
System.out.println("我是第二种方式创建的线程!");
}
public static void main(String[] args) {
Thread t = new Car2();
t.start();
}
}
两种方式都需要重写run()方法,以实现自己的方法。其次启动线程都需要用start()方法,若直接调用run方法时,则只会有一个线程启动,不能实现多线程。
实现多线程:
从运行的程序结果可以看出,运行的线程都是抢占式的,可以说他们的顺序都是随机的,所以再这种情况下就会出现一些问题,例如对公共变量的操作。
就像下面这样
Synchronized关键字,就是同步锁,关于同步锁的上锁方式的介绍,我没写博文,我看到有一篇写的很详细的推荐一下:http://www.blogjava.net/konhon/archive/2005/08/17/10296.html,谢谢仁兄的博文,
按博文中的第4个将run方法修改为下
public void run() {
synchronized(Car1.class){
//Thread.currentThread().getName()获取当前线程的名字
sum++;
System.out.println("我是小汽车线程==="+Thread.currentThread().getName()+"===我在跑。o 0");
}
}
然后在主线程中将主线程睡眠一小段时间,防止主线程和其他线程一起抢占CPU资源,实现的代码为
try {
Thread.currentThread().sleep(500);
//Thread.currentThread().join();//当前的主线程将等待其他线程全部执行完之后才执行
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("线程的总数是:"+Car1.sum);
这样一来,可以统计出当前的所有总线程数为100.
在想让主线程最后执行的过程中,在网上搜索了很多资料,发现很多都是推荐使用当前线程的join()方法来实现,
但是有的却说,是使用主线程的join,这样可以保证主线程等待其他线程执行完毕之后再执行,还有的说要在每个线程都得加上join方法,Thread最后不加,这样才能保证要求。到底如何呢?我也打算自己来试一试,第一种:主线程中加入join()方法后,join方法后面的一句话未输出,而程序一直结束不了,不知道是什么原因。
第二种:程序运行结果显示,主线程最后一句输出并不是最后执行的,没有达到效果。那到底是怎么回事??
第二天我查找了相关的资料,找到了,器其实第一种是错误的,第二种是正确的,join方法应该是用在线程启动之后,在启动之前使用join方法是没用的。
来说说java实现的死锁吧,synchronized这个关键字肯定是会用到的,那次面试中我也很诧异,当时还没反应过来,给面试官写的java简单的死锁还没用到synchronzied这个关键字,我也是醉了,回来之后,看了一些网上的参考程序,过段时间又快忘记了。还是总结下来的好。
老规矩,上程序,解释已经注释在程序中。
着下面是第一次的
package com.mhc.learn;
/**
*
* @author mhc
*练习java做一个死锁,上一次面试的时候居然没写出来
*/
public class deadLock implements Runnable{
public int flag=0; //设置变量使多个线程的锁定静态变量的顺序不一样
static Object a = new Object(),b=new Object();//****需要静态的类的变量
@Override
public void run() {
System.out.println("flag = " + flag);
if(flag==1){//当为1时先锁0.5sa对象,再锁b对象
synchronized (a) {
try {
// System.out.println("a当前的线程:"+Thread.currentThread().getName());
Thread.currentThread().sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (b) {
System.out.println(" 1 ");
// System.out.println("b当前的线程:"+Thread.currentThread().getName());
}
}
if(flag==0){//锁对象的顺序与1相反
synchronized (b) {
try {
// System.out.println("b当前的线程:"+Thread.currentThread().getName());
Thread.currentThread().sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (a) {
System.out.println(" 0 ");
// System.out.println("a当前的线程:"+Thread.currentThread().getName());
}
}
}
public static void main(String[] args) {
deadLock d1 = new deadLock();
deadLock d2 = new deadLock();
d1.flag = 0;
d2.flag = 1;
for (int i = 0; i < 10; i++) {
new Thread(d1).start();
new Thread(d2).start();
}
}
}
上面的代码,死锁的效果不明显,最后又参考了些资料。发现另外一种更明显的死锁写法,就是在锁a的函数里面锁b,改成嵌套的,而不是顺序的,就更容易锁住
代码:
package com.mhc.learn;
import java.util.concurrent.atomic.AtomicInteger;
/**
*
* @author mhc
*练习java做一个死锁,上一次面试的时候居然没写出来
*/
public class deadLock implements Runnable{
public int flag=0; //设置变量使多个线程的锁定静态变量的顺序不一样
static Object a = new Object(),b=new Object();//需要静态的类的变量
@Override
public void run() {
System.out.println("flag = " + flag);
if(flag==1){
synchronized (a) {
try {
// System.out.println("a当前的线程:"+Thread.currentThread().getName());
Thread.currentThread().sleep(500);
synchronized (b) {
System.out.println(" 1 ");
// System.out.println("b当前的线程:"+Thread.currentThread().getName());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
if(flag==0){
synchronized (b) {
try {
// System.out.println("b当前的线程:"+Thread.currentThread().getName());
Thread.currentThread().sleep(500);
synchronized (a) {
System.out.println(" 0 ");
// System.out.println("a当前的线程:"+Thread.currentThread().getName());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
deadLock d1 = new deadLock();
deadLock d2 = new deadLock();
d1.flag = 0;
d2.flag = 1;
for (int i = 0; i < 10; i++) {
new Thread(d1).start();
new Thread(d2).start();
}
}
}
可发现程序一直未结束。成功的实现了死锁!
总结一下,实现死锁的几个关键点,1、需要类静态成员变量 2、需要区分进入不同对象锁函数的标志(比如本程序中的flag),3、创建多个线程更容易看到效果 4、需要加入线程睡眠时间. 5,还有就是对对象加锁的顺序。(基本数据类型不让加锁)
说到多线程,先来理解一下线程吧。
线程,也被称为“轻量级进程”,是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己几乎不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行。
线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制
从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
在java中实现多线程有两种方式:继承Runnable接口和继承Thread方法,无论哪种方式实现多线程都需要建立Thread类或它子类的实例,例如一个简单的线程。
第一种方式:
public class Car1 implements Runnable {
@Override
public void run() {
System.out.println("我是第一种方式创建的线程!");
}
public static void main(String[] args) {
Runnable r = new Car1();
Thread t = new Thread(r);
t.start();
}
}
第二种方式:
public class Car2 extends Thread {
@Override
public void run(){
System.out.println("我是第二种方式创建的线程!");
}
public static void main(String[] args) {
Thread t = new Car2();
t.start();
}
}
两种方式都需要重写run()方法,以实现自己的方法。其次启动线程都需要用start()方法,若直接调用run方法时,则只会有一个线程启动,不能实现多线程。
实现多线程:
package mhc.learn.Thread; public class Car1 implements Runnable { @Override public void run() { //Thread.currentThread().getName()获取当前线程的名字 System.out.println("我是小汽车线程==="+Thread.currentThread().getName()+"===我在跑。o 0"); } public static void main(String[] args) { Runnable r = new Car1(); // Thread t = new Thread(r); // t.start(); //尝试启动多个线程 Thread t[] = new Thread[100]; for(int i=0;i<100;i++){ t[i] = new Thread(r,"线程"+i);//给每个线程不同的名字,加以区别 } for(Thread thread:t){ thread.start(); } } }该程序的运行结果:
从运行的程序结果可以看出,运行的线程都是抢占式的,可以说他们的顺序都是随机的,所以再这种情况下就会出现一些问题,例如对公共变量的操作。
就像下面这样
public class Car1 implements Runnable { static int sum=0;//创建一个统计线程总数的变量,这个变量是该类对象共享的,每个线程都可以对这个数字进行操作 @Override public void run() { //Thread.currentThread().getName()获取当前线程的名字 sum++; System.out.println("我是小汽车线程==="+Thread.currentThread().getName()+"===我在跑。o 0"); } public static void main(String[] args) { Runnable r = new Car1(); // Thread t = new Thread(r); // t.start(); //尝试启动多个线程 Thread t[] = new Thread[100]; for(int i=0;i<100;i++){ t[i] = new Thread(r,"线程"+i);//给每个线程不同的名字,加以区别 } for(Thread thread:t){ thread.start(); } System.out.println("线程的总数是:"+Car1.sum); } }//结果获得的线程的总数是90,99,93等等,这个数字肯定是比真实的线程总数100少的,这个是可以解释的,当第一个线程取得该值的时候假如sum=1,第二个线程也取得了sum=1,然后第一个线程保存了sum++的结果2,但第二个线程此时也写入了sum=2,所以两个线程操作完sum之后还是2,在这种情况之下就出现了差错。所以加锁就很容易想得到了。
Synchronized关键字,就是同步锁,关于同步锁的上锁方式的介绍,我没写博文,我看到有一篇写的很详细的推荐一下:http://www.blogjava.net/konhon/archive/2005/08/17/10296.html,谢谢仁兄的博文,
按博文中的第4个将run方法修改为下
public void run() {
synchronized(Car1.class){
//Thread.currentThread().getName()获取当前线程的名字
sum++;
System.out.println("我是小汽车线程==="+Thread.currentThread().getName()+"===我在跑。o 0");
}
}
然后在主线程中将主线程睡眠一小段时间,防止主线程和其他线程一起抢占CPU资源,实现的代码为
try {
Thread.currentThread().sleep(500);
//Thread.currentThread().join();//当前的主线程将等待其他线程全部执行完之后才执行
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("线程的总数是:"+Car1.sum);
这样一来,可以统计出当前的所有总线程数为100.
在想让主线程最后执行的过程中,在网上搜索了很多资料,发现很多都是推荐使用当前线程的join()方法来实现,
但是有的却说,是使用主线程的join,这样可以保证主线程等待其他线程执行完毕之后再执行,还有的说要在每个线程都得加上join方法,Thread最后不加,这样才能保证要求。到底如何呢?我也打算自己来试一试,第一种:主线程中加入join()方法后,join方法后面的一句话未输出,而程序一直结束不了,不知道是什么原因。
第二种:程序运行结果显示,主线程最后一句输出并不是最后执行的,没有达到效果。那到底是怎么回事??
第二天我查找了相关的资料,找到了,器其实第一种是错误的,第二种是正确的,join方法应该是用在线程启动之后,在启动之前使用join方法是没用的。
来说说java实现的死锁吧,synchronized这个关键字肯定是会用到的,那次面试中我也很诧异,当时还没反应过来,给面试官写的java简单的死锁还没用到synchronzied这个关键字,我也是醉了,回来之后,看了一些网上的参考程序,过段时间又快忘记了。还是总结下来的好。
老规矩,上程序,解释已经注释在程序中。
着下面是第一次的
package com.mhc.learn;
/**
*
* @author mhc
*练习java做一个死锁,上一次面试的时候居然没写出来
*/
public class deadLock implements Runnable{
public int flag=0; //设置变量使多个线程的锁定静态变量的顺序不一样
static Object a = new Object(),b=new Object();//****需要静态的类的变量
@Override
public void run() {
System.out.println("flag = " + flag);
if(flag==1){//当为1时先锁0.5sa对象,再锁b对象
synchronized (a) {
try {
// System.out.println("a当前的线程:"+Thread.currentThread().getName());
Thread.currentThread().sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (b) {
System.out.println(" 1 ");
// System.out.println("b当前的线程:"+Thread.currentThread().getName());
}
}
if(flag==0){//锁对象的顺序与1相反
synchronized (b) {
try {
// System.out.println("b当前的线程:"+Thread.currentThread().getName());
Thread.currentThread().sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (a) {
System.out.println(" 0 ");
// System.out.println("a当前的线程:"+Thread.currentThread().getName());
}
}
}
public static void main(String[] args) {
deadLock d1 = new deadLock();
deadLock d2 = new deadLock();
d1.flag = 0;
d2.flag = 1;
for (int i = 0; i < 10; i++) {
new Thread(d1).start();
new Thread(d2).start();
}
}
}
上面的代码,死锁的效果不明显,最后又参考了些资料。发现另外一种更明显的死锁写法,就是在锁a的函数里面锁b,改成嵌套的,而不是顺序的,就更容易锁住
代码:
package com.mhc.learn;
import java.util.concurrent.atomic.AtomicInteger;
/**
*
* @author mhc
*练习java做一个死锁,上一次面试的时候居然没写出来
*/
public class deadLock implements Runnable{
public int flag=0; //设置变量使多个线程的锁定静态变量的顺序不一样
static Object a = new Object(),b=new Object();//需要静态的类的变量
@Override
public void run() {
System.out.println("flag = " + flag);
if(flag==1){
synchronized (a) {
try {
// System.out.println("a当前的线程:"+Thread.currentThread().getName());
Thread.currentThread().sleep(500);
synchronized (b) {
System.out.println(" 1 ");
// System.out.println("b当前的线程:"+Thread.currentThread().getName());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
if(flag==0){
synchronized (b) {
try {
// System.out.println("b当前的线程:"+Thread.currentThread().getName());
Thread.currentThread().sleep(500);
synchronized (a) {
System.out.println(" 0 ");
// System.out.println("a当前的线程:"+Thread.currentThread().getName());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
deadLock d1 = new deadLock();
deadLock d2 = new deadLock();
d1.flag = 0;
d2.flag = 1;
for (int i = 0; i < 10; i++) {
new Thread(d1).start();
new Thread(d2).start();
}
}
}
可发现程序一直未结束。成功的实现了死锁!
总结一下,实现死锁的几个关键点,1、需要类静态成员变量 2、需要区分进入不同对象锁函数的标志(比如本程序中的flag),3、创建多个线程更容易看到效果 4、需要加入线程睡眠时间. 5,还有就是对对象加锁的顺序。(基本数据类型不让加锁)
相关文章推荐
- java多线程以及java网络编程实现简单的聊天系统
- Java多线程的简单实现以及耗时操作的效率对比
- java 关于Spring中Aop的简单理解以及SpringBoot如何添加Aop实现步骤
- Java编程之多线程死锁与线程间通信简单实现代码
- JDBC的基本概念理解以及简单实现
- Java多线程笔记二(synchronized的使用以及实现原理)
- synchronized在JVM底层的实现原理及Java多线程锁理解
- Java基础(高级)——多线程的理解和Synchronized实例,以及线程间通信,wait,notify等方法
- java 的哈希码以及Object.toString()简单理解
- Java实现多线程的两种方式以及概述
- Java 多线程(八)——实现简单线程池
- Java多线程实现简单的售票程序
- Java多线程——实现线程的方式以及线程的状态
- Java中有两种实现多线程的方式以及两种方式之间的区别
- Java中Date与Calendar小究以及应用Gregoriancalendar类实现简单的日历
- Java中有两种实现多线程的方式以及两种方式之间的区别
- java实现多线程的方式以及run方法和start方法的区别
- Java数据结构之队列的实现以及队列的应用之----简单生产者消费者应用
- Java中Pattern和Matcher的理解以及简单使用
- UIButton实现上图下字,左图右字等组合形式以及sizeToFit的简单理解