线程基础

来源:互联网 发布:试题软件 编辑:程序博客网 时间:2024/06/05 06:03

Java Thread:

Java 语言的JVM运行程序运行时创建多个线程,它通过java.lang.Thread类来实现

Thread类的特性:

1.每个线程都是通过某个特定Thread对象的run方法来完成操作的,经常把run方法的主体叫做线程体。

2.通过该Thread对象的start()方法来调用这个线程。

3.每个程序至少有一个线程,主线成main

4.守护线程,垃圾回收机制。

创建线程的方式一: extends Thread

创建多线程的第一种方式,继承java.lang.Thread类

  1. 创建一个继承于Thread的子类
  2. 重写Thread类的run()方法,在方法内实现子线程要完成的功能
  3. 创建一个子类对象
  4. 调用线程的start()方法,启动子线程,调用相应的run方法

线程中常用方法:

  • currentThread():静态的,当前线程对象

  • setName():设置线程名称

  • getName():获取当前线程名称

  • yield():方法,调用此方法的线程,释放当前CPU的执行权

  • join():在A线程中调用B线程的join方法,表示当执行到此方法,A线程停止执行,直至B线程执行完毕,A线程再接着执行join方法之后代码。

  • isAlive():判断当前线程是否存活,返回boolean

  • sleep(long i):显示的让当前线程睡眠 i 毫秒

  • 线程通信:wait(),notify(),notifyAll()

  • 设置线程优先级:

  • setPriority(int newPriority):改变线程的优先级

  • getPriority():获取线程的优先级

创建多线程的方式二:implements Runnable

创建多线程,实现runnable接口

1.创建一个实现Runnable接口的类

2.实现run方法

3.创建一个实现Runnable接口实现类的对象

4.将这个对象作为形参传递给thread类的构造器中,创建Thread对象,此对象即为一个线程

5.调用start方法启动线程,并执行run方法

创建多线程的两种方式比较:

  • 1.继承方式extend Thread VS 2.实现的方式 implements Runnable

    实现接口的方式优于继承的方式。

1.避免了java中单继承的局限性。
2.多个线程,操作共享变量时更适合。
3.需要注意的是,多个线程同时操作共享变量时容易出现线程安全性问题。

线程的分类:

1.守护线程,2.用户线程

1.他们在几乎每个方面都是相同的,唯一的区别时判断JVM何时离开。

2.守护线程是用来服务用户线程的。

通过在start()方法之前调用thread.setDaemon(true),可以把一个用户线程编程一个守护线程

3.java垃圾回收就是一个典型的守护线程。

4.若JVM中都是守护线程,当前JVM将退出。


线程的生命周期:

Thread.State枚举表示了线程的集中状态,5种状态:

1.新建:当一个Thread类或其子类的对象被声明并创建时,新生成的线程对象处于新建状态。2.就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU分配执行权,此时它具备了运行的条件。3.运行:当就绪的线程得到CPU的执行权,便进入运行状态,执行run方法中的操作。4.阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU执行权并临时中止自己的执行。5.死亡:线程完成了它的全部工作或线程被提前强制性的中止。

生命周期

关于单例模式中懒汉式的线程安全问题:使用同步机制

 / *     * 对于一般的方法内,使用同步代码块,可以考虑使用this * 对于静态方法而言,使用当前类本身充当锁: 当前类名.class */class Singleton{    private static Singleton single=null;    private Singleton(){}    public static Singleton getInstance(){        if(single==null){//双重判断            synchronized (Singleton.class) {                if(single==null){                    single= new Singleton();                }            }        }        return single;    }}public class TestSingleton {    public static void main(String[] args) {        Singleton s1=Singleton.getInstance();        Singleton s2=Singleton.getInstance();        System.out.println(s1==s2);    }}

线程的死锁:

死锁例子:

public class TestDeadLock {static StringBuffer s1=new StringBuffer();static StringBuffer s2=new StringBuffer();   public static void main(String[] args) {    new Thread(){        public void run() {            synchronized (s1) {                                   try {                    Thread.currentThread().sleep(1);                } catch (InterruptedException e) {                    e.printStackTrace();                }                s1.append("A");                synchronized (s2) {                    s2.append("B");                    System.out.println(s1);                    System.out.println(s2);                }            }        }    }.start();    new Thread() {        public void run() {            synchronized (s2) {                try {                                            Thread.currentThread().sleep(1);                } catch (InterruptedException e) {                    e.printStackTrace();                }                                    s1.append("C");                synchronized (s1) {                    s2.append("B");                    System.out.println(s1);                    System.out.println(s2);                }            }        }    }.start();      }}

线程通信:

线程通信

这里写图片描述

1.read 和 load 阶段:线程从主内存复制变量到当前线程工作内存。
2.use 和 asign 阶段:执行代码,改变共享变量值。
3.store 和 write 阶段:用工作内存数据刷新主内存对应变量的值。

在多线程环境下,use 和 asign 是多次出现的,不具备原子性。即工作内存与主内存变量不同步,从而导致非线程安全问题。
voliate 关键字解决的是变量读取时的可见性问题,但无法保证变量的原子性。对于多个线程访问同一个实例变量还是需要加锁同步。

sleep 与 wait 的区别?

1.这两个方法来自不同的类分别是Thread和Object
2.最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
3.wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在 任何地方使用

  synchronized(x){       x.notify()      //或者wait()       }

4.sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常

另外注意:wait()方法立即释放锁,而notify()方法执行后,并不立即释放锁,而是notif()所做synchronized同步代码块执行完毕后才释放锁。

wait():方法可以使调用该方法的线程释放共享资源的锁,然后从运行状态退出,进入等待队列,直到被再次唤醒。
notify():方法可以随机唤醒等待队列中等待同一共享资源的“一个”线程,使得该线程退出等待队列,进入运行状态。即:notify()
方法仅通知“一个”线程。
notifyAll():方法可以是所有正在等待队列中等待同一共享资源的“全部”线程从等待状态退出,进入可运行状态。此时他们共同抢占cpu资源,一般来说优先级高的先执行。

线程释放锁的情况?

1.执行完同步代码块就会释放对象的锁。
2.在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放。
3.在执行同步代码块的过程中,执行了锁对象的wait()方法也会释放锁。

0 0
原创粉丝点击