01-多线程

2019-05-01  本文已影响0人  四叶草_2d32

Java多线程

线程和进程的区别

线程和线程池

线程创建的三种方法

1.继承Thread创建多线程

此时每次创建的Thread对象并不能共享线程类的实例变量,也就是下面程序中的i。

public class FirstThread extends Thread{
    private int i;
    @Override
    public void run() {
        for(i=0;i<10;i++) 
            System.out.println(getName()); // 继承自Thread 
    }
    
    public static void main(String[] args) {    
        new FirstThread().start();
        new FirstThread().start(); // 注意启动线程需要用Start
    }
}

2.实现Runnable接口创建线程类

Runnable接口是一个函数式接口(可以使用Lambda表达式),通常做法是重写接口中的run方法,此时方法体即为线程执行体,使用Runnable接口实现类的实例作为Thread的target来创建Thread对象,此时因为使用一个共同的target线程执行体,多个线程可以共享一个实例变量。

public class SecondThread implements Runnable{
    private int i;
    @Override
    public void run() {
        for(;i<10;i++) {
            System.out.println(Thread.currentThread().getName() + " "+ i);
        }
    }
    
    public static void main(String[] args) {
        SecondThread targetRunnable = new SecondThread();
        new Thread(targetRunnable,"线程1").start();
        new Thread(targetRunnable).start();
    }
}

3.使用Callable和Future创建线程

Callable类似于Runnable,提供一个Call()方法作为线程执行体,并且可以有返回值,以及抛出异常,那么我们如何拿到返回值,java提供了future接口,在接口里定义了一些公共方法来控制关联它的Callable任务,然后java还贴心的给了FutureTask类,该类实现了Future接口和Runnable接口,所以FutureTask的实例就可以作为Thread的Target,所以通常做法是创建Callable接口实现类,并对该实现类的实例使用FutureTask来包装。

public class ThridThread {
    public static void main(String[] args) {        
        // lambda 表达式 + functionInterface 类型转换
        // Callbable: 有返回值
        FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)()->{
            int i =0;
            for(;i<100;i++) {
                System.out.println(Thread.currentThread().getName() + " "+ i);
            }
            return i;
    }); 
        new Thread(task,"有返回值的线程").start();
        try {
            System.out.println("子线程的返回值"+task.get());
        }catch(Exception e) {
            e.printStackTrace();
        }
 }
}

线程的生命周期

image.png
image.png

线程控制

1.join()线程

让一个线程等待另一个线程,当在某个线程执行流中调用其他线程的join()方法,该线程将被阻塞,知道join线程执行完毕为止。

2.后台线程

后台线程又称为\color{red}{Daemon Thread},守护线程,JVM的垃圾回收线程就是典型的后台线程。特征是:\color{red}{如果所有前台线程都死亡,那么后台线程自动死亡}。调用Thread对象的setDaemon(true)可以将指定线程设置为后台线程,注意\color{red}{需要在Start()之前调用,主线程默认为前台线程,}
\color{red}{前台线程创建的子线程默认为前台线程,后台线程创建的子线程默认为后台线程。}

3.yield():线程让步

Thread的静态方法,使得正在执行的线程暂停,但不会阻塞线程,只是交出CPU的控制权,将线程转为就绪状态,让系统调度器重新调度一次。当某个线程调用yield方法暂停后,只有优先级与当前线程相同,或者优先级比当前线程更高的线程才有可能获得执行机会。

4.setPriority(int newPriority)

改变线程优先级,高优先级的线程能获得更多的执行机会。

线程同步

线程的同步的意义在于线程安全,也就是说有多个线程并发访问同一个对象,而线程调度的不确定性可能带来潜在的安全问题

同步方法块:显式指定同步监视器

public class DrawThread extends Thread {
    private Account account;
    private double drawaccout;
    
    public DrawThread(String name,Account account,double drawaccount) {
        super(name);
        this.account = account;
        this.drawaccout= drawaccount;
    }
    
    public void run() {
        synchronized(account) {
            if(account.getBlance()>=drawaccount) {
                System.out.println(getName()+"取钱成功");
                try {
                    Thread.sleep(1);
                }catch(InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

同步方法

  public synchronized void draw(double amount) {
        ……
    }

同步监视器的释放

同步锁

lock,更加强大的线程同步机制,通过显式定义锁对象来实现同步,也就是Lock对象,线程在访问共享资源之前,需要先获得锁对象。线程安全控制中比较常用的是ReetrantLock可重入锁。一个线程可以对已经加锁的ReetrantLock再度加锁。

class X{
        private final ReentrantLock lock = new ReentrantLock();
        
        //需要定义线程安全的方法
        public void foo() {
            lock.lock();//加锁
            try {
                // 需要保证线程安全的代码
            }
            finally {
                lock.unlock();//使用finally块保证释放锁
            }
        }
    }

线程通信

Object类提供的wait(),notify(),notifyAll()三个方法

Condition控制线程通信

使用Condition控制线程通信:使用Lock对象来保证同步问题时,我们可以使用Condition类来释放Lock以及唤醒其他等待线程。

阻塞队列(BlockingQueue)

当生产者线程试图向Blocking Queue中放入元素时,如果队列已满,则该线程被阻塞,当消费者线程试图从BlockingQueue中取出元素时,如果队列已空,则该线程阻塞。

线程池

使用线程池执行线程任务步骤:

public class Testjava{
  public static void main(String[] args)
  throws Exception{
      ExecutorService pool = Executors.newFixedThreadPool(6);
      Runnable target = ()->{
          for(int i=0;i<100;i++) {
          System.out.println(Thread.currentThread().getName()
                      + "的i值为:"+ i);
          }
      };      
      // 向线程池中提交两个线程
      pool.submit(target);
      pool.submit(target);        
      pool.shutdown();
  }
}
上一篇 下一篇

猜你喜欢

热点阅读