java-多线程1
2015-09-14 20:54
821 查看
进程概念:
进程就是一个正在执行的程序,每一个进程执行都有一个执行的顺序,该顺序是一个执行的路径,或者叫过一个控制单元。
线程就是进程中的一个独立的控制单元,线程在控制着进程的执行。一个进程至少有一个线程。
一、自定义一个线程:
java中已经提供了对线程的描述,就是Thread类。
方法一:
1,继承thread类,并且重写run方法。
2,调用线程的start方法
如:
方法二:
就是声明线程实例类,实现Runnable接口,并覆盖Runnable中的run方法。但是要要注意的是,创建线程还是只能通过继承
Thread类,或者是直接new Thread类来创建线程对象。
步骤:
1,定义类实现Runnable接口
2,覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中。
3,通过Thread类建立线程对象。
4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数。
因为,自定义的run方法所属的对象是Runnable接口的子类对象。
所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。
5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
实现方式和继承方式有什么区别呢?
1,实现方式好处:避免了单继承的局限性。
2,在定义线程时,建立使用实现方式。
两种方式区别:
1,继承Thread:线程代码存放Thread子类run方法中。
2,实现Runnable,线程代码存在接口的子类的run方法。
小例子:
1,start(),该状态是启动线程的,当创建线程后,使用start()方法就可以启动线程了。
2,sleep(),wait():这两种就是让进程进入冻结状态,说白了就是暂停,notify()可以换醒,
3,stop(),消亡状态
4,还有一种特殊的状态,那就是阻塞状态,这中状态是自然存在的,由于CPU同一时间只能执行一个线程,所以当某一线程具有执行权的时候,其它线程必须等待。
三、多线程的安全性
这是多线程使用过程中最常出现的问题,当多个线程操作共享数据的时,由于CPU的特性,经常会出现数据错乱的情况,就像上述例子中,虽然每次打印tick都有判断tick>0,但是当线程一判断完了tick>0之后便处于阻塞状态了,这时线程二又对tick进行判断,当tick=1的时候就会出现打印出tick为负数的情况了。解决的方法就是给操作共享数据的代码加锁,这样在锁内的代码块就叫做同步代码快,里面的代码在同一时间内只能被一条线程执行!如下例子:
如下代码:
1,必须是多个线程,并要操作共享数据。
2,必须使用同一个监视器(锁)。
注意:同步虽然是解决了安全性的问题,但是也降低了程序的性能,因为线程每次都要去判断锁。
同步函数:
1,当我没要运行的的功能函数里面的代码都需要进行同不步的时候,我们可以使用synchronizd关键字修饰函数,使函数具有同步的特性。
2,同步函数使用的监视器(锁)是this关键字,也就是当前对象,但是要注意的一点就是当该函数是静态的,并且被synchronized修饰了,那么这时候的锁就不在是this了,而是函数所在类的字节码文件,类名.class。
死锁:
同步还有个小问题,那就是死锁,当同步代码块中嵌套了同步代码块的时候就很容易出现死锁了。
看下面的例子:
进程就是一个正在执行的程序,每一个进程执行都有一个执行的顺序,该顺序是一个执行的路径,或者叫过一个控制单元。
线程就是进程中的一个独立的控制单元,线程在控制着进程的执行。一个进程至少有一个线程。
一、自定义一个线程:
java中已经提供了对线程的描述,就是Thread类。
方法一:
1,继承thread类,并且重写run方法。
2,调用线程的start方法
如:
<span style="font-size:14px;">class Demo extends Thread { public void run()//重新run方法 { for(int x=0;x<100;x++) { System.out.println("run start"); } } } class ThreadDemo { public stitac void main(String[] args) { Demo d = new Demo(); //开启线程的时候,该线程会自动调用run方法。 d.start(); } }</span>注意:当我们使用d.run();这样的方式调用run方法也是可以的,但是这样就是主线程在执行,而新建立的线程并没有执行。
方法二:
就是声明线程实例类,实现Runnable接口,并覆盖Runnable中的run方法。但是要要注意的是,创建线程还是只能通过继承
Thread类,或者是直接new Thread类来创建线程对象。
步骤:
1,定义类实现Runnable接口
2,覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中。
3,通过Thread类建立线程对象。
4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数。
因为,自定义的run方法所属的对象是Runnable接口的子类对象。
所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。
5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
实现方式和继承方式有什么区别呢?
1,实现方式好处:避免了单继承的局限性。
2,在定义线程时,建立使用实现方式。
两种方式区别:
1,继承Thread:线程代码存放Thread子类run方法中。
2,实现Runnable,线程代码存在接口的子类的run方法。
小例子:
<span style="font-size:14px;">class Ticket implements Runnable { private tick=100; public run() { while(true) { if(tick>0) System.out.println("num:"+tick--); } } } class ThreadDemo { public stitac void main(String[] args) { //这里要注意,new Ticket()并非创建一个线程,因为Ticket类并没有继承Thread。 Ticket tic = new Ticket(); //这里才是创建线程,并把所要操作的对象tic传入。 Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); t2.start();; } }</span>二、线程的状态
1,start(),该状态是启动线程的,当创建线程后,使用start()方法就可以启动线程了。
2,sleep(),wait():这两种就是让进程进入冻结状态,说白了就是暂停,notify()可以换醒,
3,stop(),消亡状态
4,还有一种特殊的状态,那就是阻塞状态,这中状态是自然存在的,由于CPU同一时间只能执行一个线程,所以当某一线程具有执行权的时候,其它线程必须等待。
三、多线程的安全性
这是多线程使用过程中最常出现的问题,当多个线程操作共享数据的时,由于CPU的特性,经常会出现数据错乱的情况,就像上述例子中,虽然每次打印tick都有判断tick>0,但是当线程一判断完了tick>0之后便处于阻塞状态了,这时线程二又对tick进行判断,当tick=1的时候就会出现打印出tick为负数的情况了。解决的方法就是给操作共享数据的代码加锁,这样在锁内的代码块就叫做同步代码快,里面的代码在同一时间内只能被一条线程执行!如下例子:
<span style="font-size:14px;">class Ticket implements Runnable { private tick=100; Object obj = new Object(); public run() { while(true) { //这里添加锁 synchronized(obj) { //同步代码块 if(tick>0) System.out.println("num:"+tick--); } } } } class ThreadDemo { public stitac void main(String[] args) { //这里要注意,new Ticket()并非创建一个线程,因为Ticket类并没有继承Thread。 Ticket tic = new Ticket(); //这里才是创建线程,并把所要操作的对象tic传入。 Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); t2.start();; } }</span>还有个比较特殊的地方也经常会出现安全性的问题,那就是在单例设计模式的时候(懒汉式),同样需要加锁去解决,但是会降低性能,我们通过把代码改成注释的部分,加入双重判断,这样就能够减少线程对锁的判断,稍微提高一些程序的性能。
如下代码:
<span style="font-size:14px;">class Single { private static Single single = null; private Single(){} public static Single getInstance() { //此处如果不加锁也会出现安全性的问题 synchronized(Single.class) { if(single==null) single = new Single(); return single; } /* 此处加入了双重判断,是为了提高效率 if(single==null) { synchronized(Single.class) { if(single==null) single = new Single(); return single; } } */ } }</span>使用同步的前提:
1,必须是多个线程,并要操作共享数据。
2,必须使用同一个监视器(锁)。
注意:同步虽然是解决了安全性的问题,但是也降低了程序的性能,因为线程每次都要去判断锁。
同步函数:
1,当我没要运行的的功能函数里面的代码都需要进行同不步的时候,我们可以使用synchronizd关键字修饰函数,使函数具有同步的特性。
2,同步函数使用的监视器(锁)是this关键字,也就是当前对象,但是要注意的一点就是当该函数是静态的,并且被synchronized修饰了,那么这时候的锁就不在是this了,而是函数所在类的字节码文件,类名.class。
死锁:
同步还有个小问题,那就是死锁,当同步代码块中嵌套了同步代码块的时候就很容易出现死锁了。
看下面的例子:
<span style="font-size:14px;">class Test implements Runnable { private boolean flag; Test(boolean flag) { this.flag = flag; } public void run() { if(flag) { while(true) { synchronized(MyLock.locka)//假如t1线程拿到A锁 { System.out.println(Thread.currentThread().getName()+"...if locka "); //B锁在线程t2中所以t1线程只能等待 synchronized(MyLock.lockb) { System.out.println(Thread.currentThread().getName()+"..if lockb"); } } } } else { while(true) { synchronized(MyLock.lockb)//假如t2线程拿到B锁 { System.out.println(Thread.currentThread().getName()+"..else lockb"); //B锁在线程t1中所以t2线程只能等待 synchronized(MyLock.locka) { System.out.println(Thread.currentThread().getName()+".....else locka"); } } } } } } class MyLock { //这个类仅仅是为了比较方便的创建两个锁而已。 static Object locka = new Object(); static Object lockb = new Object(); } class DeadLockTest { public static void main(String[] args) { Thread t1 = new Thread(new Test(true)); Thread t2 = new Thread(new Test(false)); t1.start(); t2.start(); } }</span>上述这个列子就是出现死锁的表现,在实际开发中应该尽量避免这种情况的出现。
相关文章推荐
- 1.spring-bean-1工程源码浅析(来源郝佳的书)
- Java Web 中文乱码的问题
- Spring AOP
- Java线程中的wait, notify and notifyAll
- spring事物注解不起作用的解决方式
- 如何判断二叉树是否是结构性对称的?
- java_单列集合复习
- java中this关键字的用法
- Java实现中文算数验证码(算数运算+-*/)
- java基础 iterator
- java学习之正则表达式
- MyEclipse不能编译的解决方案
- Java Unit Testing - JUnit & TestNG
- Java基础知识记录
- java io流的一部分解析
- JAVA String 不可变对象
- java 模拟多个客户端与服务器建立UDP连接
- Java习题4—IO流与异常
- Java生成双击可执行的jar包
- 可以增删改查数组的Java类