java基础之多线程

多线程

指的是这个程序(一个进程)运行时产生了不止一个线程

并行与并发:

并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。

并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。

创建多线程

继承 Thread 类,扩展线程

Thread 类实现了Runnable接口

class MyThread extends Thread{
    private static int num = 0;
    public MyThread(){
        num++;
    }
    @Override
    public void run() {
        System.out.println("主动创建的第"+num+"个线程");
    }
}

public class Test {
    public static void main(String[] args)  {
        MyThread thread = new MyThread();
        thread.start();
    }
}

实现 Runnable 接口

public class MyRunnable implements Runnable{
    public MyRunnable() {}
    @Override
    public void run() {
        System.out.println("子线程ID:"+Thread.currentThread().getId());
    }
}

调用 

public class Test {
    public static void main(String[] args)  {
        System.out.println("主线程ID:"+Thread.currentThread().getId());
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
}

 同步线程

class SyncThread implements Runnable {
    public void run() {
        synchronized(this) {}
    }
}

SyncThread syncThread = new SyncThread();
Thread thread1 = new Thread(syncThread, "SyncThread1");
thread1.start();

多线程状态

创建(new): 准备好了一个多线程的对象

就绪(runnable): 调用了start()方法, 等待CPU进行调度

运行(running): 执行run()方法

阻塞(blocked): 暂时停止执行, 可能将资源交给其它线程使用,有运行资格,但是没有执行权。

终止(dead): 线程销毁

注:sleep和wait的区别:

  • sleep是Thread类的方法,wait是Object类中定义的方法.
  • Thread.sleep不会导致锁行为的改变, 如果当前线程是拥有锁的, 那么Thread.sleep不会让线程释放锁.
  • Thread.sleep和Object.wait都会暂停当前的线程. OS会将执行时间分配给其它线程. 区别是, 调用wait后, 需要别的线程执行notify/notifyAll才能够重新获得CPU执行时间.

有返回结果的多线程

使用ExecutorService、Callable、Future实现有返回结果的多线程

线程池

Java通过Executors提供四种线程池,分别为:

1、newCachedThreadPool: Executors.newCachedThreadPool(); 

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

cachedThreadPool.execute(new Runnable() {  
    public void run() {  
         System.out.println(index);  
    }  
}); 

2、newFixedThreadPool :Executors.newFixedThreadPool(3);

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
fixedThreadPool.execute(new Runnable() {  
    public void run() {  
     try {  
          System.out.println(index);  
          Thread.sleep(2000);  
     } catch (InterruptedException e) {  
          e.printStackTrace();  
     }  
    }  
});  

3、newScheduledThreadPool:Executors.newScheduledThreadPool(5);

创建一个定长线程池,支持定时及周期性任务执行。

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);  
scheduledThreadPool.schedule(new Runnable() {  
   public void run() {  
        System.out.println("延迟3秒执行");  
   }  
}, 3, TimeUnit.SECONDS);  

4、newSingleThreadExecutor : Executors.newSingleThreadExecutor();  

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();  
singleThreadExecutor.execute(new Runnable() {  
    public void run() {  
     try {  
          System.out.println(index);  
          Thread.sleep(2000);  
     } catch (InterruptedException e) {  
          e.printStackTrace();  
     }  
   }  
});  

参考:

线程池_1:http://cuisuqiang.iteye.com/blog/2019372

线程池_2:http://www.importnew.com/19011.html

Executor

ThreadPoolExecutor机制:http://825635381.iteye.com/blog/2184680

Executor接口只有一个execute方法。

ExecutorService是继承Executor的子接口,增加了一些常用的对线程的控制方法。

AbstractExecutorService是一个抽象类实现ExecutorService接口。

ThreadPoolExecutor就是继承了这个类。

public ThreadPoolExecutor(
    int corePoolSize,
    int maximumPoolSize,
    long keepAliveTime,
    TimeUnit unit,
    BlockingQueue<Runnable> workQueue,
    ThreadFactory threadFactory,
    RejectedExecutionHandler handler
}

线程池submit和execute

参考:

线程池submit与execute的区别:http://blog.youkuaiyun.com/hayre/article/details/53314599

线程池submit与execute的区别2:Java多线程-线程池ThreadPoolExecutor的submit返回值Future_喵了个呜s的博客-优快云博客

Runnable和Callable的区别

参考:Callable,Runnable用法:http://blog.youkuaiyun.com/heyutao007/article/details/19072675

(1)Callable规定的方法是call(),Runnable规定的方法是run().

(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得

(3)call方法可以抛出异常,run方法不可以

(4)运行Callable任务可以拿到一个Future对象,Future 表示异步计算的结果。

它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。

计算完成后只能使用 get 方法来获取结果,如果线程没有执行完,Future.get()方法可能会阻塞当前线程的执行;

如果线程出现异常,Future.get()会throws InterruptedException或者ExecutionException;

如果线程已经取消,会跑出CancellationException。取消由cancel 方法来执行。isDone确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明Future<?> 形式类型、并返回 null 作为底层任务的结果

final ExecutorService exec = Executors.newFixedThreadPool(5);  
Callable<String> call = new Callable<String>() { 
    public String call() throws Exception {
        Thread.sleep(1000 * 10);//休眠指定的时间,此处表示该操作比较耗时  
        return "Other less important but longtime things.";  
    }
};

Future<String> task = exec.submit(call);
String obj = task.get();  
System.out.println(obj);  
//关闭线程池  
exec.shutdown();  

线程安全

synchronized锁

按照修饰对象

修饰代码块

synchronized(this|object) {}
synchronized(类.class) {}

修饰方法

修饰非静态方法-public synchronized void test(){}
修饰静态方法-public synchronized static void test(){}
synchronized:不能修饰接口;不能修复构造方法,但是可以修饰构造方法里面的代码块

按照获取的锁分类

获取对象锁

形式1:synchronized(this|object) {}
形式2:修饰非静态方法-public synchronized void test(){}

获取类锁

形式1:synchronized(类.class) {}
形式2:修饰静态方法-public static synchronized void method1(){}

对象锁与类锁的区别

对象锁:每个实例都会有一个monitor对象,即Java对象的锁,类的对象可以有多个,所以每个对象有其独立的对象锁,互不干扰
类锁:每个类只有一个Class对象,所以每个类只有一个类锁;类锁是加载类上的,而类信息是存在JVM方法区的,并且整个JVM只有一份,方法区又是所有线程共享的,所以类锁是所有线程共享的。

Synchronized缺陷

效率低:锁的释放情况少、试图获得锁时不能设定超时、不能中断一个正在试图获得锁的线程

不够灵活(读写锁更灵活):加锁和释放的时机单一,每个锁仅有单一的条件(某个对象),可能是不够的无法知道是否成功获取到锁

Lock 锁

Lock提供了比synchronized更多的功能

Lock和synchronized对比

1)synchronized是Java语言的关键字,因此是内置特性,Lock不是Java语言内置的,Lock是一个接口,通过实现类可以实现同步访问。

2)synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中

3)在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态。

 接口

void lock()获得锁,最常用的方式。
如果当前锁不可用,当前线程无法调度并进入休眠状态直到获取到锁
boolean tryLock()获取锁(仅当锁在调用时处于空闲状态时才获取锁)。
如果成功获取锁,返回true,否则,返回false;无论如何都会立即返回。
在拿不到锁时不会一直在那等待。
boolean tryLock(long time, TimeUnit unit)获取锁(在规定的等待时间内且线程没有被中断,如果锁处于空闲状态时则获取锁)。
如果成功获取锁,返回true,否则,返回false;
如果当前锁不可用,当前线程无法调度并进入休眠状态直到(1)当前线程获取到锁
(2)当前线程被其它线程中中断
(3)等待时间结束
void lockInterruptibly()获取锁(除非当前线程被中断)。
如果当前锁不可用,当前线程无法调度并进入休眠状态直到当前线程获取到锁或者其它线程中断了当前的线程。
和 tryLock(long time, TimeUnit unit) 方法不同的是等待时间无限长,但是在等待中可以中断当前线程(响应中断)。
注意,当一个线程获取了锁之后,是不会被interrupt()方法中断的。因为调用interrupt()方法不能中断正在运行过程中的线程,只能中断阻塞过程中的线程。
Condition newCondition()获取等待通知组件,返回绑定到此 Lock 实例的新 Condition 实例,当前线程只有获得了锁,才能调用该组件的wait()方法,而调用后,当前线程将释放锁。
void unlock()释放锁

参考:
https://blog.youkuaiyun.com/Qynwang/article/details/131197120
https://www.cnblogs.com/wobuchifanqie/p/12530354.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值