java线程学习笔记

来源:互联网 发布:笔记本防护软件 编辑:程序博客网 时间:2024/05/29 08:36

一、线程和进程

关于线程和进程的关系:进程是一个一个单独运行的程序,一个进程就是一个独立的执行环境。进程有着完整的,私有的基本的运行时资源,尤其是每个进程都有自己的内存空间而线程是进程中的一个工作流。比如QQ,就是一个进程,但是打开QQ之后可以和很多个人聊天、视频、传递文件等等操作,这就是一个一个的线程。

二、线程的状态

线程的4中状态:新建状态(new)、可执行状态(runnable)、阻塞状态(blocked)、死亡状态(dead)

新建状态:线程已经创建,但是还没开始执行,尚未被调用,没有执行start()方法;

可执行状态:线程对象执行了start()方法,在线程池中等待cpu分配时间片段开始执行;

阻塞状态:一个运行中的线程通过wait(),sleep()等方法,有运行状态变成停滞状态就是阻塞状态,使用wait()方法使线程进入阻塞状态,可通过调用notify()或者notifyAll()方法唤醒   进入执行状态,sleep()方法在睡眠时间结束之后自动进入可执行状态;(wait是Object的方法,sleep是线程的方法)

死亡状态:一个线程正常结束之后就处于死亡状态;

三、线程的实现

实现线程一般有两种方法:一种是直接继承Thread类,一种就是实现Runnable接口;

继承Thread的方式:

public class Demo extends Thread{

public void run(){

System.out.println("this is extends Thread");

}

public static void main(String[] args){

Thread t = new Demo();

t.start();

}

}

实现Runnable的方式:

public class Demo implements Runnable{

public void run(){

System.out.println("this is implements Runnable");

}

public static void main(String args[]){

Runnable r = new Demo();

Thread t = new Thread(r);

t.start();

}

}

由于java不支持多继承,所以一般创建一个线程都是实现Runnable接口

四、终止线程的方法

一般终止线程有3中方法:一是自动终止,在程序执行结束之后线程就会自动终止;二是调用stop()方法强制终止线程;三是使用interrupt方法终止线程

1)public class Demo implements Runnable{

public volatile boolean exit = false;   

public void run(){

while(!exit);

}

public static void main(String args[]) throws Exception{

Demo r = new Demo();

Thread t = new Thread(r);

t.start();

t.sleep(5000);//主线程延迟5s

r.exit = true;//终止线程

}

}

/**定义了一个退出标志exit,当exit为true时,while退出循环;定义exit的时候使用了volatile关键字,目的是为了使exit同步,在同一时刻只能由一个线程来修改exitd的值 */

2)使用stop()方法强制退出:t.stop()

/**不推荐使用stop方法,使用stop方法是很危险的,就象突然关闭计算机电源,而不是按正常程序关机一样,可能会产生不可预料的结果,因此,并不推荐使用stop方法来终止线程。*/

3)public class Demo extends Thread
{
    public void run()
    {
        try
        {
            sleep(50000);  // 延迟50秒
        }
        catch (InterruptedException e)
        {
            System.out.println(e.getMessage());
        }
    }
    public static void main(String[] args) throws Exception
    {
        Thread t = new Demo();
        t.start();
        System.out.println("在50秒之内按任意键中断线程!");
        System.in.read();
        t.interrupt();
        System.out.println("线程已经退出!");
    }
}

/**

在调用interrupt方法后, sleep方法抛出异常,然后输出错误信息:sleep interrupted.

   注意:在Thread类中有两个方法可以判断线程是否通过interrupt方法被终止。一个是静态的方法interrupted(),一个是非静态的方法isInterrupted(),这两个方法的区别是interrupted用来判断当前线是否被中断,而isInterrupted可以用来判断其他线程是否被中断。因此,while (!isInterrupted())也可以换成while (!Thread.interrupted())。

*/

五、wait()和sleep()的区别

wait:使一个线程处于等待状态,并且释放所持有的对象的lock;

sleep:使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉interruptException异常

notify:唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。

notifyAll:唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争

关于线程的同步,先区别一下wait()和sleep()的区别

1)这两个方法来自不同的类,wait()方法来自Thread而sleep()来自于Object

2)最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法

3)wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用(使用范围)

4)sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常(使用sleep方法后,不会释放所持有的对象锁,但有可能被其他对象调用它的interrupt()方法,产生

interruptException异常,如果不捕获这个异常,线程就会意外终止)


六、线程同步


同步的使用可以保证在多线程运行的环境中,程序不会产生设计之外的错误结果;同步的实现有两种方式:同步方法和同步代码块,使用关键字synchronized

给一个方法添加synchronized修饰之后就可以使他成为同步方法,这个方法可以是静态的也可以是非静态的,但是不能是抽象类的抽象方法,也不能是接口中的接口方法

public void synchronized Method(){

//do somthing

}

public static synchronized void Method(){

//do somthing

}

线程在执行同步方法时是具有排它性的。当任意一个线程进入到一个对象的任意个同步方法时,这个对象的所有同步方法都被锁定了,在此期间,其他任何线程都不能访问这个对象的任意一个同步方法,直到这个线程执行完它所调用的同步方法并从中退出,从而导致它释放了该对象的同步锁之后。在一个对象被某个线程锁定之后,其他线程是可以访问这个对象的所有非同步方法的。

同步代码块是通过锁定一个指定的对象,来对同步块中包含的代码进行同步;而同步方法是对这个方法块里的代码进行同步,而这种情况下锁定的对象就是同步方法所属的主体对象自身。如果这个方法是静态同步方法呢?那么线程锁定的就不是这个类的对象了,也不是这个类自身,而是这个类对应的java.lang.Class类型的对象。同步方法和同步块之间的相互制约只限于同一个对象之间,所以静态同步方法只受它所属类的其它静态同步方法的制约,而跟这个类的实例(对象)没有关系。

如果一个对象既有同步方法,又有同步块,那么当其中任意一个同步方法或者同步块被某个线程执行时,这个对象就被锁定了,其他线程无法在此时访问这个对象的同步方法,也不能执行同步块

synchronized的目的是使同一个对象的多个线程,在某个时刻只有其中的一个线程可以访问这个对象的synchronized 数据

示例1:

public class ThreadTest implements Runnable{

public synchronized void run(){

for(int i=0;i<10;i++) {

System.out.print(" " + i);

}

}

public static void main(String[] args) {

Runnable r1 = new ThreadTest(); //也可写成ThreadTest r1 = new ThreadTest();

Runnable r2 = new ThreadTest();

Thread t1 = new Thread(r1);

Thread t2 = new Thread(r2);

t1.start();

t2.start();

}}

示例2:

public class ThreadTest implements Runnable{

public synchronized void run(){

for(int i=0;i<10;i++){

System.out.print(" " + i);

}

}

public static void main(String[] args){

Runnable r = new ThreadTest();

Thread t1 = new Thread(r);

Thread t2 = new Thread(r);

t1.start();

t2.start();

}}

示例3:

public class ThreadTest implements Runnable{

public void run(){

synchronized(this){

for(int i=0;i<10;i++){

System.out.print(" " + i);

}} }

public static void main(String[] args){

Runnable r = new ThreadTest();

Thread t1 = new Thread(r);

Thread t2 = new Thread(r);

t1.start();

t2.start();

}}

示例1,虽然同步了run()方法,但是保护的不是共享数据,因为是两个对象的线程,而不同对象的数据是不同的

示例2,使用synchronized修饰了run()方法, 保护的是同一个对象的共享数据,输出的数据总是为0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9

示例3,效果跟示例2一样,在可能的情况下,应该把保护范围缩到最小



0 0
原创粉丝点击