java线程

来源:互联网 发布:淘宝北极熊单人电热毯 编辑:程序博客网 时间:2024/05/16 09:27

课程内容

程序、进程、线程的概念
Java中多线程的创建和使用
继承 Thread 类与实现 Runnable  接口
Thread类的主要方法
线程的调度与设置优先级
线程的生命周期
线程的同步
线程的通信


进程(process)是程序的一次执行过程,或是正在运行的一个程序。动态过程:有它自身的产生、存在和消亡的过程。
如:运行中的QQ,运行中的360.

线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。(一个进程可以有多个线程)

何时需要多线程?

程序需要同时执行两个或多个任务。

需要一些后台运行的程序时。(如java的垃圾回收机制)

多线程的创建和启动

Java语言的JVM允许程序运行多个线程,它通过java.lang.Thread类来实现。
Thread类的特性
每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为线程体
通过该Thread对象的start()方法来调用这个线程


/*
 * 创建一个子线程,完成1-100之间自然数的输出。同样地,主线程执行同样的操作
 * 创建多线程的第一种方式:继承java.lang.Thread类
 */
//1.创建一个继承于Thread的子类
class SubThread extends Thread{
    //2.重写Thread类的run()方法.方法内实现此子线程要完成的功能
    public void run(){
        for(int i = 1;i <= 100;i++){
            System.out.println(Thread.currentThread().getName() +":" + i);
        }
    }
}

public class TestThread {
    public static void main(String[] args) {
        //3.创建子类的对象
        SubThread st1 = new SubThread();
        SubThread st2 = new SubThread();

        //4.调用线程的start():启动此线程;调用相应的run()方法
        //一个线程只能够执行一次start(),如果一个对象调用了两次start()方法,则会抛异常。
        //不能通过Thread实现类对象的run()去启动一个线程,直接调用run()方法,不会开启一个新的线程。
        st1.start();
        st2.start();
        
        for(int i = 1;i <= 100;i++){
            System.out.println(Thread.currentThread().getName() +":" + i);
        }
    }
}


Thread的常用方法:
 * 1.start():启动线程并执行相应的run()方法
 * 2.run():子线程要执行的代码放入run()方法中
 * 3.currentThread():静态的,调取当前的线程
 * 4.getName():获取此线程的名字
 * 5.setName():设置此线程的名字
 * 6.yield():调用此方法的线程释放当前CPU的执行权(A,B两个线程同时执行,A调用yield()方法后,A线程放弃cup的执行权,然后过一会,A,B两个线程再去抢cpu的执行权,所以其实这个方法没什么卵用)
 * 7.join():在A线程中调用B线程的join()方法,表示:当执行到此方法,A线程停止执行,直至B线程执行完毕,
 * A线程再接着join()之后的代码执行
 * 8.isAlive():判断当前线程是否还存活
 * 9.sleep(long l):显式的让当前线程睡眠l毫秒
 * 10.线程通信:wait()   notify()  notifyAll()

设置线程的优先级
 * getPriority() :返回线程优先值
   setPriority(int newPriority) :改变线程的优先级

线程优先级,并不是表示优先级高的先执行,然后再执行优先级低的,而是优先级高的获取cpu的概率高。


启动线程的第二种方式:

class MyThread implements Runnable{
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 100; i++) {
            System.out.println(i);
        }
    } 
}

MyThread t1=new MyThread();
        Thread t=new Thread(t1);
        t.start();


对java线程源码 代理模式的理解:

public interface Runnable {

      public abstract void run();
}

代理类:

public class Thread implements Runnable {

    private Runnable target;

    public Thread(){}

    public Thread(Runnable target) {
        this.target=target;
    }

    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

}

咱们自己创建的类是被代理类

class MyThread implements Runnable{
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 100; i++) {
            System.out.println(i);
        }
    } 
}

最后用这种方式启动:

        MyThread t1=new MyThread();
        Thread t=new Thread(t1);
        t.start();

用实现接口的方式启动线程时,如果要创建多个线程:

        MyThread t1=new MyThread();//不需要再重新创建
        Thread t=new Thread(t1);
        Thread t2=new Thread(t1);
        t.start();
        t2.start();

对比一下继承的方式 vs 实现的方式
 * 1.联系:public class Thread implements Runnable
 * 2.哪个方式好?实现的方式优于继承的方式
 *    why?  ① 避免了java单继承的局限性
 *            ② 如果多个线程要操作同一份资源(或数据),更适合使用实现的方式


线程的生命周期:

JDK中用Thread.State枚举表示了线程的几种状态
要想实现多线程,必须在主线程中创建新的线程对象。Java语言使用Thread类及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历如下的五种状态:
新建: 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件
运行:当就绪的线程被调度并获得处理器资源时,便进入运行状态, run()方法定义了线程的操作和功能
阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中止自己的执行,进入阻塞状态
死亡:线程完成了它的全部工作或线程被提前强制性地中止 


此程序存在线程的安全问题:打印车票时,会出现重票、错票
 * 1.线程安全问题存在的原因?
 *   由于一个线程在操作共享数据过程中,未执行完毕的情况下,另外的线程参与进来,导致共享数据存在了安全问题。
 *   
 * 2.如何来解决线程的安全问题?
 *      必须让一个线程操作共享数据完毕以后,其它线程才有机会参与共享数据的操作。
 *
 * 3.java如何实现线程的安全:线程的同步机制
 *         
 *         方式一:同步代码块
 *         synchronized(同步监视器){
 *             //需要被同步的代码块(即为操作共享数据的代码)
 *         }
 *         1.共享数据:多个线程共同操作的同一个数据(变量)
 *         2.同步监视器:由一个类的对象来充当。哪个线程获取此监视器,谁就执行大括号里被同步的代码。俗称:锁
 *         要求:所有的线程必须共用同一把锁
 *         注:在实现的方式中,考虑同步的话,可以使用this来充当锁。但是在继承的方式中,慎用this!
class Window2 implements Runnable {
    int ticket = 100;// 共享数据
    public void run() {
        while (true) {

           //this表示当前对象,用实现的方式创建线程,this是同一个

            synchronized (this) {
                    if (ticket > 0) {
                       System.out.println(Thread.currentThread().getName()
                               + "售票,票号为:" + ticket--);
                      }
            }
        }
    }
}
 *         方式二:同步方法

将操作共享数据的方法声明为synchronized。即此方法为同步方法,能够保证当其中一个线程执行
 *         此方法时,其它线程在外等待直至此线程执行完此方法。
 *         >同步方法的锁:this(必须是同一把锁)


public void run() {
        while (true) {
            show();
        }
    }

    public synchronized void show() {
        if (ticket > 0)
            System.out.println(Thread.currentThread().getName() + "售票,票号为:"
                    + ticket--);
        }
    }


懒汉式单例模式的完善:

public class TestSingleTon {
    private TestSingleTon() {
    }

    private static TestSingleTon instance;

    public static TestSingleTon getInstance() {
        if (instance == null) {//外层这个if判断是为了提高效率
            synchronized (TestSingleTon.class) {
                if (instance == null) {
                    return new TestSingleTon();
                }
            }
        }
        return instance;
    }
}


死锁:

不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁

例子:

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) {
    s2.append("A");
    synchronized (s2) {
       s2.append("B");
       System.out.print(s1);
       System.out.print(s2);
}}}}.start();

new Thread() {
public void run() {
synchronized (s2) {
   s2.append("C");
   synchronized (s1) {
      s1.append("D");
      System.out.print(s2);
      System.out.print(s1);
}}}}.start();
}}

线程通信:

wait() 与 notify() 和 notifyAll()
wait():令当前线程挂起并放弃CPU、同步资源,使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问(放弃同步锁)
notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待(优先唤醒优先级高的)
notifyAll ():唤醒正在排队等待资源的所有线程结束等待.

wait()和notify()同时存在,wait()状态的线程,只有notify之后才可以被唤醒。

Java.lang.Object提供的这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则会报java.lang.IllegalMonitorStateException异常


使用两个线程打印 1-100. 线程1, 线程2 交替打印
class MyThread1 implements Runnable{
    int number=100;
    @Override
    public void run() {
        while(number>0){
            synchronized (this) {
                this.notify();//唤醒另一个线程
                System.out.println(Thread.currentThread().getName() + "打印了"
                        + number--);
                try {
                    this.wait();//进入等待状态,释放锁。
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
    
}




0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 胃变大凸出来了怎么办 坐完月子脊背疼怎么办 月子过后脊背疼该怎么办 怀孕的时候牙疼怎么办 练瑜伽后弯腰疼怎么办 练完瑜伽腰椎疼怎么办 瑜伽开髋动作受伤怎么办 才学瑜伽教培上课紧张怎么办 瑜伽馆不给退费怎么办 脚运动后酸痛该怎么办 婴儿误吞拉链头怎么办 肠功能蠕动慢便秘怎么办 胃肠型和蠕动波怎么办 胃不蠕动了怎么办偏方 喂母乳母亲奶头裂开怎么办 给宝宝吃奶被吃到奶头裂开怎么办 宝宝吃奶奶头裂开了怎么办 小孩吃奶奶头裂开了怎么办 站久坐久腰酸痛怎么办 孕39周胎儿头小怎么办 怀孕腰两侧长肉怎么办 怀孕四个月半月吃点就饱怎么办啊 怀孕四个月睡眠不好怎么办 二胎七个月肚子太大怎么办 上火牙疼牙龈肿怎么办 孕30周乳房胀痛怎么办 怀孕长妊娠纹了怎么办 坐久了肚子胀疼怎么办 怀孕后胖的太快怎么办 怀孕牙齿全坏了怎么办 怀孕脸胖了好多怎么办 孕晚期不爱吃肉怎么办 怀孕期间胖了怎么办啊 孕期长得太胖怎么办 狗吃马肉脸肿了怎么办 狗过敏了脸肿了怎么办 孕初期外阴很痒怎么办 怀孕了吃了田鸡怎么办 孕妇睡眠质量差怎么办吃什么 39周2天了还不生怎么办 孕中期体重猛长怎么办