栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java多线程

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java多线程

进程与线程

进程通常指的是一个软件列如QQ.exe等。一个进程里面有多个线程,至少包含一个线程,java中默认有两个线程(main,GC)

java中开启线程三种方式:Thread,Runnable,callable

java不可以开启线程

通过group.add()把当前线程添加到当前线程组,调用本地方法start0(),调用的是底层c++,因为java无法直接操作硬件,java是运行在虚拟机上的。所以需要通过c++完成。

    public synchronized void start() {
        
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                
            }
        }
    }

    private native void start0();
并发编程:并发与并行

并发编程的本质:充分利用CPU的资源

并发:多个线程操作同一个资源

        一核CPU,模拟多条线程

并线:多个一块行动

       多核CPU,模拟多条线程可以同时执行

public static void main(String[] args) {
        //获得cpu核数
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
线程的状态
//线程新生
NEW,
//运行
RUNNABLE,
//阻塞     
BLOCKED,
//等待
WAITING,
//超时等待
TIMED_WAITING,
//终止
TERMINATED;
wait/sleep的区别
  • 类不同
    • wait是Object类中的,sleep是Threa中的
  • 锁的释放
    • wait不释放锁,sleep不释放锁
  • 使用范围不同
    • wait必须在同步代码块中才能使用,sleep不做限制
  • wait/sleep都需要捕获异常InterruptedException中断异常
Lock锁

lock的使用

 Lock l = ...;
 l.lock();
 try {
   // access the resource protected by this lock
 } finally {
   l.unlock();
 }

lock接口的实现类

ReentrantLock:可重入锁

ReentrantReadWriteLock.ReadLock:读锁

ReentrantReadWriteLock.WriteLock:写锁

ReentrantLock的构造方法:

默认是非公平锁

//非公平锁
public ReentrantLock() {
        sync = new NonfairSync();
}
//公平锁
public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
}

synchronized和lock的区别

  1. synchronized是java中的关键字,lock是一个java类
  2. synchronized无法判断获得锁的状态,lock可以判断是否获得锁
  3. synchronized会自动释放锁,lock需要手动释放锁,不释放锁会死锁
  4. 使用synchronized 线程1获得了锁后面的线程将会等待,线程1要是阻塞后面的线程就会一直等待。lock锁使用tryLock()就不会等待
  5. synchronized可重入锁,不可以中断,非公平锁,lock可重入锁,可以中断,可以字节设置非公平锁和公平锁
  6. synchronized适合少量的代码同步问题,lock适合锁大量的同步代码
生成者与消费者

synchronized:

public class Demo01 {
    public static void main(String[] args) {
        Data data  = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.incte();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.derce();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}
class Data{
    private Integer num = 0;
    public synchronized void incte() throws InterruptedException{
        while(num != 0 ){
            this.wait();
        }
        num ++;
        System.out.println(Thread.currentThread().getName()+" -->"+ num);
        this.notifyAll();
    }
    public synchronized void derce() throws InterruptedException{
        while(num == 0 ){
            this.wait();
        }
        num --;
        System.out.println(Thread.currentThread().getName()+" -->"+ num);
        this.notifyAll();
    }
}

JUC:

通过lock找到Condition,

Lock替换synchronized方法和语句的使用, Condition取代了对象监视器方法的使用。

代码演示

public class Demo01 {
    public static void main(String[] args) {
        Data data  = new Data();
        new Thread(()->{for (int i = 0; i < 10; i++)data.incte();},"A").start();
        new Thread(()->{for (int i = 0; i < 10; i++)data.derce();},"B").start();
        new Thread(()->{for (int i = 0; i < 10; i++)data.incte();},"C").start();
        new Thread(()->{for (int i = 0; i < 10; i++)data.derce();},"D").start();
    }
}
class Data{
    private Integer num = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    public  void incte(){
        lock.lock();
        try {
            while(num != 0 ){
                condition.await();
            }
            num ++;
            System.out.println(Thread.currentThread().getName()+" -->"+ num);
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
           lock.unlock();
        }
    }
    public void derce(){
        lock.lock();
        try {
            while(num == 0 ){
                condition.await();
            }
            num --;
            ;System.out.println(Thread.currentThread().getName()+" -->"+ num);
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

Condition实现精准唤醒

public class Demo02 {
    public static void main(String[] args) {
        DataTwo dataTwo = new DataTwo();
        new Thread(()->{for (int i = 0; i < 10; i++)dataTwo.prinA();},"A").start();
        new Thread(()->{for (int i = 0; i < 10; i++)dataTwo.prinB();},"B").start();
        new Thread(()->{for (int i = 0; i < 10; i++)dataTwo.prinC();},"C").start();
    }
}
class DataTwo{
    Lock l = new ReentrantLock();
    Condition condition = l.newCondition();
    private int num = 1;
    public void prinA(){
        l.lock();
        try {
            while (num != 1){
                condition.await();
            }
            System.out.println(Thread.currentThread().getName()+"正在执行");
            num=2;
            condition.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            l.unlock();
        }
    }
    public void prinB(){
        l.lock();
        try {
            while (num != 2){
                condition.await();
            }
            System.out.println(Thread.currentThread().getName()+"正在执行");
            num=3;
            condition.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            l.unlock();
        }
    }
    public void prinC(){
        l.lock();
        try {
            while(num != 3){
                condition.await();
            }
            System.out.println(Thread.currentThread().getName()+"正在执行");
            num = 1;
            condition.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            l.unlock();
        }
    }
}
CopyOnWriteArrayList

在并发下ArrayList是线程不安全的,会出现异常java.util.ConcurrentModificationException(并发修改异常)

public static void main(String[] args) {
        List list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,4));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }

那么怎么解决这个并发修改异常问题呢?

方法一

list集合中Vector集合是线程安全的所以可以使用Vector解决

List list = new Vector();

方法二

可以使用Collections中的synchronizedList()将ArrayLIst改变为线程安全的

List list = Collections.synchronizedList(new ArrayList<>());

  方法三

使用CopyOnWriteArrayList

List list = new CopyOnWriteArrayList<>();

使用Vector本事就是线程安全的在添加数据的时候就是同步的,

使用Collections.synchronizedList()就是将传过去的list添加同步锁

使用CopyOnWriteArrayList读写分离,在写入数据的时候复制一份并且在他的底层是使用lock锁

 推荐使用CopyOnWriteArrayList因为使用synchronized效率会比较低但。

如果需要将map改变为线程安全需要使用到ConcurrentHash Map()

Map map = new ConcurrentHashMap();

Collable

Callable接口类似于Runnable,因为它们都是为其实例可能由另一个线程执行的类设计的。 然而,A Runnable不返回结果,也不能抛出被检查的异常。

Callable与Runnable不同于,Callable可以抛出异常,使用的方法不同Callable使用call方法,Callable可以有返回值

ReadWriteLock(读写锁)

java并发包中ReadWriteLock是一个接口,主要有两个方法

public interface ReadWriteLock {
    Lock readLock();
    Lock writeLock();
}

 实现类:ReadWriteLockView,ReentrantReadWriteLock

readLock是读锁,writeLock是写锁,使用ReadWriteLock可以更加细粒度控制

public class ReadWriteLockTest {
    static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    public static void main(String[] args) {
        Date date = new Date();
        Test test = new Test();
        new Thread(()->test.start(true,sdf.format(date)),"write-----1").start();//读
        new Thread(()->test.start(true,sdf.format(date)),"write-----2").start();//读
        new Thread(()->test.start(false,sdf.format(date)),"read-----2").start();//写
        new Thread(()->test.start(false,sdf.format(date)),"read-----1").start();//写
        new Thread(()->test.start(false,sdf.format(date)),"read-----3").start();//写
    }

}
class Test{
        ReadWriteLock rel = new ReentrantReadWriteLock();//构建ReentrantReadWriteLock使锁细腻话
        //创建读锁
        Lock readLock = rel.readLock();
        //创建写锁
        Lock writeLock = rel.writeLock();
    public  void start(boolean flse, T t){
        if(flse)read(t);else write(t);
    }
    //读方法
    public  void read(T t){
        readLock.lock();//使用读锁
        try {
            System.out.println("线程"+Thread.currentThread().getName()+"正在读,当前时间是:"+t);
            TimeUnit.SECONDS.sleep(2);//停滞两秒
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readLock.unlock();//释放读锁
        }
    }
    //写方法
    public  void write(T t){
        writeLock.lock();//使用读锁
        try {
            System.out.println("线程"+Thread.currentThread().getName()+"正在写,当前时间是:"+t);
            TimeUnit.SECONDS.sleep(2);//停滞两秒
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            writeLock.unlock();//释放读锁
        }
    }
}
线程池

使用线程池的优点:

  • 降低资源的消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗
  • 提高影响速度,通过重复利用已创建的线程降低线程创建和销毁造成的消耗
  • 提高线程的可管理性,

线程池的使用

JDK 5.0起提供了线程池相关API:ExecutorService 和Executors

ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor

创建四种方式:

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

public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("threadName;"+Thread.currentThread().getName());
                }
            });
        }
    }
  • newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
public static void main(String[] args) {
    //1.创建可固定长度的线程池
    ExecutorService newExecutorService = Executors.newFixedThreadPool(3);
    //创建了10个线程
    for (int i = 0; i < 10; i++) {
        int temp = i;
            newExecutorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("threadName;"+Thread.currentThread().getName()+",i"+temp);
                }
        });
    }
}
  • newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例
 public static void main(String[] args) {
        //1.创建可定时线程池
        ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 10; i++) {
            final int temp = i;
            newScheduledThreadPool.schedule(new Runnable() {
                public void run() {
                    System.out.println("i:" + temp);
                }
            }, 3, TimeUnit.SECONDS);//时间数值,时间单位
        }

    }
  • newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行需要关闭
public static void main(String[] args) {
        //1.创建单线程
        ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            newSingleThreadExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("index:" + index);
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }

        newSingleThreadExecutor.shutdown();
    }

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1028984.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号