java基础Thread

进程 :正在进行的程序

线程:就是进程中一个执行单元或执行情景或执行路径

一个进程中至少有一个线程。

当一个进程中线程有多个时,就是多线程

多线程解决的问题,可以同时执行多个进程或者一个进程多个线程可以让多部分代码同时执行

什么是同时执行其实都是在瞬间做着快速切换完成的cpu的切换随机的,按照时间片的方式来完成。

我们之前其实java运行执行函数中内同是,垃圾回收期也在回收内存的垃圾。

所以执行main方法的线程,和执行垃圾回收期的线程同时在执行。这就是多线程

硬盘可以持久化保存数据,而内存断电后,数据就丢失了

new LizeDemo();
new LizeDemo();
new LizeDemo();
System.gc();

在实例中你会发现,打印结果每次都不一样

说明cpu在随机切换,所以垃圾回收器回收的结果又不同。

jvm在启动线程的时候,每个线程都有自己要执行 的内容。

其中一个负责执行main函数中的内容:这个线程称之为:主线程main

负责拉紧器运行的称之为垃圾回收线程

我们就可以推理出,为什么要启动多线程呢?

因为很多代码需要同时执行。

而且每一个线程都有自己要执行的内容,这个内容称之为:线程的任务。

简单说启动线程就是为了执行任务,但任务有多个需要同时执行时,就需要多个线程。

     Demo1.java
    private String name;
    Demo1(String name){
    this.name = name;
    }
 public void show(){

    for(int x = 1;x<=10;x++){
        for(int y =9999999;y<999999999;y++){}//刻意写个循环让程序运行程序慢一点
        System.out.println(name+"--"+x);
    }
}

    ThreadDemo.java
    Demo1 d1 =new Demo1("旺财");
    Demo1 d2 = new Demo1("小明");
    d1.show();
    d2.show();

这里存在的问题是d1没执行完,d2不会被执行因为执行主线程就是先执行d1,才执行到d2

希望,可以不可以实现d1和D2同时执行呢?

一条路径是无法解决这个问题的。

需要多条路径来完成。

需要在程序中创建线程,而打印d1和d2的代码就是任务

怎么创建线程呢?

java需要调用底层才能完成进程的建立和线程的创建

那么 java应该有对外描述线程的对象来方便对线程的操作。

先找包 java.lang包中,去找描述线程的类,Thread

通过查阅Thread类的描述

创建执行线程的两种方法

方式一 继承Thread类,覆盖run方法

调用run方法和调用start方法的区别:

调用run方法,仅仅是一般对象在调用对象的方法,并没有开启线程还是有主线程来完成的run方法的运行。

调用start方法,是开启一个线程(一个新的执行路径)。

这个线程执行run方法。

多线程中,每个线程都有自己的栈空间。

当一个栈去发生问题,线程出栈进程还没有结束

/*
 * 通过四个窗口买票
 * 一共有100张票
 * 买票的动作有四个窗口同时使用
 * 那就要使用多线程技术
 * 那就需要创建线程
 * 需要继承Thread类,覆盖run方法。
 * 
 * 这样线程的问题出来的就是买票的过程不可控,会有重复的买一张    票
 * 因为每个线程买的都是自己的一百张票
 * 解决问题,
 *  1 可以让数据共享,static
 * 
   声明一个类实现
 *  
 * */
class Ticket extends Thread {
    private static int num = 100;

    public void run() {
        while (true) {

            if (num > 0) {
                System.out.println("sale..." + currentThread().getName()
                        + "---" + num--);
            }
        }

    }
}

public class SallTicket {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Ticket t1 = new Ticket();
        Ticket t2 = new Ticket();
        Ticket t3 = new Ticket();
        Ticket t4 = new Ticket();
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }

2 第二种解决方案 创建线程的第二种方法 实现Runnable接口

  1. 第一一个类实现Runable接口。
  2. 覆盖Runnable接口中的run方法,将线程要运行的代码存储到run方法中
  3. 创建该接口的子类对象
  4. 通过Thread类进行线程的创建,并将Ruanable接口的子类对象做为Thread类的构造函数的实参进行传递。 为什么要传递呢? 让线程对象创建是,就要明确运行那个run方法,而这个方法是需要被对象调用的,所有将run方法所需的对象传递给Thread类的构造器
  5. 调用Thread类中的start方法开启线程。package thread;

    /*

    • 将售票例子该写成Runnable接口方式。
    • */class Thread2 implements Runnable {private int num = 100;

      @Overridepublic void run() {// TODO Auto-generated method stubwhile (true) {if (num > 0) {System.out.println("sale...." + num--);}}}

    }

    public class SaleTick2 {

    public static void main(String[] args) {
    
    Thread2 th = new Thread2();
    Thread t1 = new Thread(th);
    Thread t2 = new Thread(th);
    Thread t3 = new Thread(th);
    Thread t4 = new Thread(th);
    t1.start();
    t2.start();
    t3.start();
    t4.start();
    

    }

    }线程设计的时候要么继承Thread,要么实现接口Runnable

  6. 继承的线程的时候,线程的子类对象也是个线程,同时复写run方法

  7. 如果有一个类已经有父类(非Thread类),根据java单继承的,所以要先实现线程任务,这样就可以实现Runnable接口来
  8. Runnable 接口设计的思想 ,接口可以很好的扩展程序,其实就将线程的任务进行对象的封装。将线程任务分成对象后,通过Runnable接口可以降低对象的耦合性

如果是继承Thread类,覆盖run方法这种情况,

Thread的子类即封装了线程的任务,而且自身还是一个线程对象

这就是任务和对象耦合性过高。

class Demo extends Thread {
        public void run(){
            //线程任务
     }
   }
Demo d= new Demo();
d.start();

如果实现Runnable接口

class Demo implements Runnable{
    public void run{
        //线程任务
    }
}
Demo d = new Demo();//这是一个任务对象

Thread t = new Thread(d);//创建线程对象去运行指定任务。

t.start()

Runnable接口的出现,线程对象的使用该接口,

我们只要实现该接口即可。

线程的安全问题

出现的现象 多线程的问题,因为cpu的调用线程的随机性,

packackage thread;

/*
 * 
 * 
 */
class Thread2 implements Runnable {
    private int num = 100;

    @Override
    public void run() {
        // TODO Auto-generated method stub
        while (true) {
            if (num > 0) {
                try {
                    Thread.sleep(500L);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println("sale...." + num--);
            }
        }
    }

}

public class SaleTick2 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Thread2 th = new Thread2();
        Thread t1 = new Thread(th);
        Thread t2 = new Thread(th);
        Thread t3 = new Thread(th);
        Thread t4 = new Thread(th);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }

}

运行结果

sale....100
sale....99
sale....100
sale....98
sale....97
sale....94
sale....95
sale....96
sale....93
sale....90
sale....91
sale....92
sale....89
sale....86
sale....88
sale....87
sale....85
sale....82
sale....83
sale....84
sale....81
sale....79
sale....80
sale....78
sale....77
sale....77
sale....76
sale....75
sale....74
sale....72
sale....73
sale....71
sale....70
sale....68
sale....69
sale....67
sale....66
sale....65
sale....64
sale....63
sale....62
sale....61
sale....62
sale....60
sale....58
sale....57
sale....59
sale....56
sale....55
sale....54
sale....53
sale....52
sale....51
sale....50
sale....49
sale....48
sale....47
sale....46
sale....45
sale....44
sale....43
sale....41
sale....42
sale....40
sale....39
sale....37
sale....38
sale....36
sale....35
sale....33
sale....34
sale....32
sale....31
sale....30
sale....29
sale....28
sale....27
sale....25
sale....26
sale....24
sale....23
sale....21
sale....22
sale....20
sale....19
sale....17
sale....18
sale....16
sale....15
sale....14
sale....13
sale....12
sale....10
sale....9
sale....11
sale....8
sale....6
sale....5
sale....7
sale....4
sale....3
sale....2
sale....1
sale....0
sale....-1
sale....-1

结果不是我们愿意看到的,(可能我们运行的结果不一样)因为我们没有可能-1 0 和重复的票 因为线程之间共享100这个数据,如果在内部run内部定义安全问题产生的原因

1.0 多个线程在操作共享数据2.0 操作共享数据的代码有多条一个线程在执行多条操作共享的数据过程中,其他线程参数参与了运算,这时就会发生安全问题。

想要分洗是否安全问题:

依据:线程任务中没有共享数据,该数据是否被多条语句操作啊。

解决方案,一个保证一个线程在执行多条操作共享数据的语句,其运算他进程不能参与即可。当该线程都执行完后,其他线程才可以执行这些语句。

代码表现:java中给我们提供具体的解决语句。

那就是同步代码块。

格式 sychroized(对象){//对象可以是任意的对象{

需要被同步的语句}

}

同步的原理:其实就是将需要同步的代码上加锁,意味着一个进程进去,其它的进不去。

同步的好处:解决多线程的安全问题。

同步的弊端:不断的切换,判断锁进不来,会降低性能。

多线程程序数据问题出现 ,为了解决,加上同步,发现问题依旧

同步中的前提

必须要保证在同步中的与多个线程,因为同步中有一个线程该同步是没有意义。必须是保证多个线程在同步中使用的同一个锁如果在run方法中,Object ob =new Object(),每生成一个对象就是一个锁!方法是里面放同步块,不是同步块里面放方法。

等待唤醒机制

涉及到的方法:

wait()等待,将正在执行的线程释放其执行资格和执行权,并存储到线程池中。

notify():唤醒,唤醒线程池中被wait()的线程,一个唤醒一个,而且是任意的。

notifyAll():唤醒全部,可以将线程池中的所有wait()线程都唤醒。

唤醒的意思就是让线程池中的线程具备执行资格。

这些方法都是在同步中的才有效;

这写方法是在使用是必须表明所属的素,这样可以明确出这些操作到底是那个锁上的线程。

为什么这些操作线程定义在object类中?因为这些方法在使用时,必须要表明所属的锁,而锁的可以是任意对象

wait(),在其他线程调用notify()方法或notifyAll()方法前,导致当前线程等待。换句话说,此方法的行为就好像它有执行权wait()调用

当前线程必须拥有此对象监视器。该线程发布对此监视器的所有权并等待,其他线程通过调用notify()方法,或notifyAll()方法通知在此对象的鉴定器上等待线程醒来。然后该线程将等到重新获得对监听器的所有权后才能继续继承

虽说多生产的问题解决,效率很低那些。自从1.5后 在java.util.concurent lock提供的接口,同步函数还是代码就是隐式的调用锁

同步函数或者同步代码块使用的锁和监视器

lock 接口,Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。 就是将进行单独对象进行封装,而且提供了对锁对象很多功能。比如:lock()获取锁,unlock()释放锁。lock对所的操作都是显示操作。

所以它的出现要比同步函数代码块明确的多。更符合面向对象

简单一句话:Lock接口的出现替代同步

lock()获取锁

unlock()释放锁

public interface Condition

Condition 将Object 监视器方法(wait、notify和notifya)分解成截然不同的对象,以便通过这些对象与任意Lock实现组合使用,为每个对象提供多个等待set(wait-set)。其中Lock替代了sychronized方法和语句的使用。Condition代替了Object监视器方法的作用

条件(也称为条件队列或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为True的另一个线程通知它之前,一直挂起保护,一次该线程()既然其等待。因此访问共享信息状态发生在不同的线程中,所以它必须被保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子的形式释放相关的锁,并挂起当前线程,就像Object、wait做的那样。

Condition 实例实质上被绑定到一个锁上。要为特定Lock实例获得Condition实例。请使用其 newCondition()方法

作为一个示例,假定有一个绑定的缓冲区,它支持put和take()方法。

如果试图在空的缓冲区上执行Take操作,则在某一个项变的可用之前,线程将一直阻塞:如果试图在满的缓冲区上执行put操作,则在有空间变的可用之前,线程将一直阻塞。我们喜欢在独特的等待set中保存put线程和take线程,这样就可一点以在缓冲区中的项或空间变的可用时利用最佳规划,一次只通知一个线程。可以使用两个Condition实例来做到这一点

原来在同步中,锁和监视器就是同一个对象。

现在,升级后,锁是一个单独的对象

而且将监视器的方法也单独封装到了一个对象中,这个对象就是升级后Conditon

升级后,都进行了单独封装锁被封锁成为了Lock对象监视器方法都被封装到了Condition

说白了就是Lock换成了锁,而Condition替代了Object中的监视器方法

Condition中提供了监视器的方法:awati(),singak(),signaAll()

如何让锁和监视器产生联系呢?

直接通过Lock接口中的NewCondition()方法就可以获取到能绑定该Lock对象的上的监视器对象的Condition

class BoundBuffer{
    final Lock lock = new ReentranLock();
    final Condition notFull = lock.newCondition();
    final Condition notEmpty = lock.newCondition();

    fianl Object[] items = new Object[1000];
    int putpr,takeptr,count;

}

之前要解决本方案只要唤醒对方用的方式就是定义连个锁。 synchronized(生产锁){synchorized(消费锁){while(flag)生产wait()...notify消费();//这里用的消费者锁上的notify }}synchronized(消费锁){sychroizd(生产锁){while(!flag) 消费wait()notify生产()//这里用的是生产者锁的上notify 生产的notify}}这样看上去可以解决问题的,但是出现了同步嵌套,很容易发生死锁

所以解决办法就是同意个锁,while的标记判断,,notifyAll的全部唤醒

这种解决方案效率地,因为还可以解决这个问题了。

有了新特性Lock,Condition就可以解决这个问题了之前是两个锁嵌套,容易死锁。现在方案是只有锁,但是锁上可以加入多个监听器。一个监视生产者,一个监视消费者/** * Created with IntelliJ IDEA. * User: jhpc * Date: 13-8-4 * Time: 下午12:11 * To change this template use File | Settings | File Templates. */

import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;

/** * Created with IntelliJ IDEA. * User: jhpc * Date: 13-8-4 * Time: 上午9:45 * To change this template use File | Settings | File Templates. */

class Resou1 {
    private String name;
    private int count = 1;
    private boolean flag = false;
    //创建锁对象    凡是看到ReentrantReadWriteLock.ReadLock();
    //private java.util.concurrent.locks.Lock lock = new java.util.concurrent.locks.ReentrantLock();
    private final Lock lock = new ReentrantLock();
    //获取一个该锁上的监视器
    //创建一个生产者监视器
    private Condition producer_con = lock.newCondition();
    //创建一个消费者监视器
    private Condition consumer_con = lock.newCondition();

    public void set(String name) {
        //获取锁
        lock.lock();
        //如果下面的语句发生了异常 锁资源没有释放,所以我们用try finally让finally中释放锁


        try {
            while (flag) {
                try {
                    producer_con.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                this.name = name + count;
                count++;
                System.out.println(Thread.currentThread().getName() + "...生产者..." + this.name);
                flag = true;
                consumer_con.signal();
            }
        }


        //释放锁
        finally {
            lock.unlock();
        }


    }

    public void get() {
        lock.lock();

        try {
            while (!flag) {
                try {
                    consumer_con.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println(Thread.currentThread().getName() + "...消费者....." + this.name);
                flag = false;
                producer_con.signal();
            }
        } finally {
            lock.unlock();
        }
    }
}

class Producers1 implements Runnable {

    private Resou1 r;

    Producers1(Resou1 r) {
        this.r = r;
    }

    public void run() {
        while (true) {
            r.set("馒头");
        }
    }
}

class Custorms1 implements Runnable {

    private Resou1 r;

    Custorms1(Resou1 r) {
        this.r = r;
    }

    public void run() {
        while (true) {
            r.get();
        }
    }
}

public class LockDemo {
    public static void main(String args[]) {
        Resou1 r = new Resou1();
        Producers1 pro = new Producers1(r);
        Custorms1 cus = new Custorms1(r);
        //连两个个线程负责生产
        Thread t0 = new Thread(pro);
        Thread t1 = new Thread(pro);
        //这两个线程负责消费
        Thread t2 = new Thread(cus);
        Thread t3 = new Thread(cus);
        t0.start();
        t1.start();
        t2.start();
        t3.start();

    }
}

在实际工作中是这样的定义一个资源容器,有一定的容量,在容器没有满的情况下,我们可以随意生产,在满了以后,不能生产;消费者在容器部位空的情况下可以任意的消费。

参考javaapi中的的例子class BoundedBuffer { final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition();

   final Object[] items = new Object[100];
   int putptr, takeptr, count;

   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length) 
         notFull.await();
       items[putptr] = x; 
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();
     } finally {
       lock.unlock();
     }
   }

   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0) 
         notEmpty.await();
       Object x = items[takeptr]; 
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();
       return x;
     } finally {
       lock.unlock();
     }
   } 
 }

停止线程 1定义循环结束的标记因为线程运行代码一般都是循环,只要控制了循环即可。2使用interrupt(中断)方法。

该方法要结束线程的冻结状态,使线程回到运行床带中,注stop方法已经过时,不在使用。

如果读不到标记怎么办?比如在任务中让线程处于了冻结状态释放了执行资格,无法执行标记,run方法没结束,

解决办法中断线程,如果当前线程线程中断它自己,线程类中有一个interrupt()方法。可以清楚线程冻结状态,可以让线程具备执行权

则该线程的checkAcess方法会被调用,这可能抛出SecurityException

如果线程在调用Object类的wait(),wait(long)、或wait(long)f方法,或者该类的join()、join(long),join(long,int),sleep(long)或sleep(long int)方法过程中受阻,则其中断状态呗清楚,它还将接受到一个InterupdatedExcption

如果该线程在可中断的通道上的I/O操作中受阻,则该通道将关闭,该线程的中断状态将设并设置受到一个CloseByInterruException.

如果该线程在以一个Selection中受阻,则该线程中断状态将被设置,它置,它将立即从选择操作上返回,并可能带有一个非零值,就好像调用了选择器的wakeup方法一样

如果以前的条件都没有保存,则该线程的中断状态将被设置

抛出 SecurityException 如果点钱线程无法修改该线程

守护线程 public final void setDeamon(boolean on)将改线程标记设为守护线程或用户线程。当真在运行的线程都是守护线程时,java虚拟机退出。

该方法必须在启动线程的CheckAccess方法,且不带任何参数。这可能抛出SecurityException(当前线程中)

参数on如果为true,则将该线程标记设为守护线程

抛出 IllegalThreadStateException 如果该线程处于活动状态。

SecurityException 如果当前线程无法修改该线程。

另请参见

isDaemon(),checkedAccess()

测试该线程是否是守护线程。

返回 如果线程是守护线程,则被返回true;否则返回false。 另参见 setDaemon(boolean)

线程类的其他方法

setPriority(int num)设置线程的优先级,如果优先级只能是一到十之间,默认优先级5

setDaemon(boolean b)

join()等待该线程终止 抛出 InterrupdateException 如果任何线程中断了当前线程。当抛出该线程异常时,当前线程的中断状态呗清除。

自定义线程名称

toString()

ThreadGroup 线程组

Thread.yield()释放执行权

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值