JAVA 多线程知识点

线程创建的方式

  1. 继承Thread类,重写run方法
  2. 实现Runnable接口
  3. 实现Callable接口

获取线程ID和线程名称

  1. Thread子类中调用this.getId()或者this.getName()
  2. 使用Thread.currentThread().getID()和Thread.currentThread().getName()

修改线程名称

  1. 调用线程对象的setName()方法
  2. 使用线程子类的构造方法赋值

线程的状态

  1. 初始:线程对象被创建,即为初始状态。只在堆中开辟内存 ,与常规对象无异。
  2. 就绪:调用start()之后,进入就绪状态。等待OS选中,分配时间片
  3. 运行:获得时间片后,进入运行状态。时间片用完后,进入就绪状态。

常见方法

1. 休眠:public static void sleep(),毫秒数
2. 放弃:public static void yield()。当前线程主动放弃时间片,进入就绪状态。
3. 加入:public final void join(),允许其他线程加入到当前线程
4. 优先级:线程对象.setPriority(),线程优先级为1-10,默认为5,优先级越高,表示获取CPU的时间片机会越大
5. 守护线程:线程对象.setDaemon(ture):设置为守护线程 。垃圾回收器属于守护线程,

线程安全问题

当多线程并发访问临界资源时,如果破坏了原子操作,会造成数据不一致性。

临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性。

同步方式

  1. 同步代码块:
synchronized(临界资源对象){ //对临界资源加锁
	//代码(原子操作)
	}
  1. 每个对象都有一个互斥锁标记,用来分配给线程的。线程退出同步方法时,会释放相应的互斥锁标记。
  2. 同步方法:
synchronized 返回值类型 方法名称 (形参列表0{// 对当前对象(this) 加锁 

//如果是静态方法,锁是当前类。类名.class

// 代码 原子操作
}

同步规则

只有在调用包含同步代码块的方法,或者同步方法时,才需要对象的锁标记。

如果用不包含同步代码块的方法,或普通方法时,则不需要锁标记,可直接调用。

已知JDK中线程安全类:StringBuffer Vector HashTable,这些类的公开方法中,均为synchronized修饰的同步方法。

死锁

当第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。

一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标
记,由此可能造成死锁。

线程通信

等待:

public final void wait()
	
public fianl void wait(long timeout)

必须在对obj加锁的同步代码块中。在一个线程中,调用obj.wait()时,此线程会释放其拥有的所有锁标记。同时此线程阻塞在等待队列中。释放锁,进入等待队列。

通知:

public final void notify()

public final void notifyAll()

生产者,消费者

若干个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个能存储多个产品的缓冲区,生产者将生产的产品放入缓冲区中,消费者从缓冲区中取走产品进行消费,显然生产者和消费者之间必须保持同步,即不允许消费者到一入空的缓冲区中取产品,也不允许生产者向一个满的缓冲区中放入产品。

线程池

问题:

线程是宝贵的内存资源,单个线程约占1MB空间,过多分配易造成内存溢出

频繁的创建和销毁线程会造成虚拟机回收频率,资源开销,造成程序性能下降

线程池:

线程容器,可设定线程分配的数量上限。

将预先创建的线程对象放入池中,并重用线程池中的线程对象

避免频繁的创建和销毁

创建线程池

常用的线程池接口和类(java.util.concurrent)

Executor:线程池的顶级接口

ExecutorService:线程池接口,可通过submit(Runnable task)提交任务代码

Executors工厂类:通过此类可以获得一个线程池。
1.创建固定的线程池
2.创建缓存线程池;
3.创建但线程池
4.创建调度线程池,调度:周期,定时执行。

通过newFixedThreadPoll(int nThreads) 获取固定数量的线程池。参数:指定线程池中线程的数量。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo {
    public static void main(String[] args) {
        // 1.1 创建固定个数的线程池
        ExecutorService es = Executors.newFixedThreadPool(4) ;
        // 1.2 创建缓存线程池
        ExecutorService es1 =Executors.newCachedThreadPool();
        // 1.3 创建单线程池
       ExecutorService es2 = Executors.newSingleThreadExecutor();
        // 1.4 创建调度线程池
        ExecutorService es3 = Executors.newScheduledThreadPool();
        // 2. 提交任务
        Runnable runnable = new Runnable() {
            private int ticket = 100;
            @Override
            public void run() {
                while(true){
                    if(ticket <= 0){
                        break;
                    }
                    System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票");
                    ticket--;
                }
            }
        };
        //3.提交任务
        for (int i = 0; i <4 ; i++) {
            es.submit(runnable);
        }
        // 4. 关闭线程池
        es.shutdown();
        
    
    }
}


Callable 接口

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

JDK1.5之后加入,与Runnable接口类似,实现之后代表 一个线程启动

Callable具有泛型返回值,可以声明异常。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

public class Demo2 {
    public static void main(String[] args) {
        // 1. 创建callable对象
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println(Thread.currentThread().getName()+"开始计算");
               int sum =0;
                for (int i = 0; i <=100 ; i++) {
                    sum+=i;
                    Thread.sleep(100);
                }
               return sum;
            }
        };
        // 2. 把callable对象转成可执行的任务
        FutureTask<Integer> task = new FutureTask<>(callable);
        // 3. 创建线程
        Thread thread = new Thread(task);
        // 4. 启动线程
        thread.start();
        // 5. 获取结果(等待call()执行完毕返回结果)
        Integer sum = null;
        try {
            sum = task.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("结果是"+sum);
    }
}
线程池配合Callable接口

import java.util.concurrent.*;

public class Demo3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1.创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        // 2. 提交任务 Future :表示将要执行玩任务的结果
        Future<Integer> future= executorService.submit(new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {
                System.out.println(Thread.currentThread().getName()+"开始计算");
                int sum =0;
                for (int i = 0; i <=100 ; i++) {
                    sum+= i;
                    Thread.sleep(100);
                }
                return sum;
            }
        });
          // 3.获取任务结果
        System.out.println(future.get());
        
        // 4.关闭线程池
        executorService.shutdown();;
                
                
    }
}

Future 接口

表示将要完成任务的结果

package 多线程.Thread;

import java.util.concurrent.*;

/**
 * @Author 杨栋
 * @Date 2020/7/7 14:39
 * 使用两个线程计算1~50,50~100的和,并将结果汇总
 */

public class Demo4 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1.创建线程池
        ExecutorService es = Executors.newFixedThreadPool(2);
        // 2.提交任务
        Future<Integer> future = es.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum =0;
                for (int i = 0; i <=50 ; i++) {
                    sum+=i;
                }
                System.out.println("1~50计算完毕");
                return sum;
            }
        });
        
        Future<Integer> future1= es.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum =0;
                for (int i = 51; i <=100 ; i++) {
                    sum+= i;
                }
                System.out.println("50-100计算完毕");
                return sum;
            }
        });
        int sum = future.get() + future1.get();

        System.out.println("结果是" + sum);
        // 4. 关闭线程池
        es.shutdown();
        
        
    }
}

线程的同步异步

同步:形容一次方法调用,同步一旦开始,调用者必须等待改方法返回,才能继续。(一条执行路径)

异步:形容一次方法调用,异步一旦开始,像是一次消息传递,调用者告知之后就立即返回。二者竞争时间片,并发执行。(多条执行路径)

Lock接口

JDK5加入,与synchronized比较,显示定义,结构更灵活。

提供更多实用方法,功能更强大,性能更优越

常用方法

	void lock() // 获取锁,如果锁被占用,则等待

	boolean tryLock() // 尝试加锁,(成功返回true,失败返回false)

	void unlock() // 释放锁

重入锁

ReentrantLock():Lock接口的实现类,与synchronized一样具有互斥锁功能


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

public class MyList {
    // 创建重入锁对象
    private final Lock lock = new ReentrantLock();
    private String [] atrs = {"A","B","","",""};
    private  int count = 2; // 元素个数
    // 添加元素
    public void add(String value){
        lock.lock();
        try{
            atrs[count] = value;
            try{
                Thread.sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            count++;
        }finally {
            lock.unlock();
        }

    }
    
    public  String [] getatrs(){
        return atrs;
    }
}

import java.util.ArrayList;
import java.util.Arrays;

public class TestMylist {
    public static void main(String[] args) throws InterruptedException {
        MyList myList = new MyList();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
              myList.add("hello");
              
            }
        };
        
        Runnable runnable1 = new Runnable() {
            @Override
            public void run() {
                myList.add("world");
            }
        };
        Thread t1 =  new Thread(runnable);
        Thread t2 =  new Thread(runnable1);
        t1.start();
        t2.start();
        
        t1.join();
        t2.join();

        System.out.println(Arrays.toString(myList.getatrs()));
        
    }
}

读写锁

ReentrantReadWriteLock:一种支持一写多读的同步锁,读写分离,可分别分配读锁,写锁。支持多次分配读锁,使多个读操作可以并发执行。

互斥规则 :

写–写:互斥,阻塞

读–写:互斥,读阻塞写,写阻塞读

读–读:不互斥,不阻塞

在读操作远远高于写操作的环境中,可在保障线程安全的情况下,提高运行效率。


import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteDemo {
    // 创建读写锁
    private  ReentrantReadWriteLock rrl = new ReentrantReadWriteLock();
    // 获取读锁
    private ReentrantReadWriteLock.ReadLock readLock = rrl.readLock();
    // 获取写锁
    private ReentrantReadWriteLock.WriteLock  writeLock = rrl.writeLock();
    private String value;
    public String getValue(){
        // 使用读上锁
        readLock.lock();
        try{
            Thread.sleep(1000);
            System.out.println("读取:"+value);
            return this.value;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            readLock.unlock();
        }
        return value;
    }
    public void setValue(String value){
        writeLock.lock();
        try {
            Thread.sleep(1000);
            System.out.println("写入:"+value);
            this.value = value;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            writeLock.unlock();
        }
    }
}



import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestReadWite {
    public static void main(String[] args) {
        ReadWriteDemo readWriteDemo = new ReadWriteDemo();
        // 创建线程池
       ExecutorService executorService = Executors.newFixedThreadPool(20);
       Runnable read = new Runnable() {
           @Override
           public void run() {
               readWriteDemo.getValue();
           }
       };
       Runnable write = new Runnable() {
           @Override
           public void run() {
               readWriteDemo.setValue("张三");
           }
       };
        // 分配写的任务
        for (int i = 0; i <2 ; i++) {
            executorService.submit(write);
        }
       // 分配任务
        long stretTime = System.currentTimeMillis();
        for (int i = 0; i <18 ; i++) {
            executorService.submit(read);
        }
      
        // 关闭
        executorService.shutdown();
        
        while(!executorService.isTerminated()){
            
        }
        long endTime = System.currentTimeMillis();
        System.out.println("用时"+(endTime-stretTime));
    }
}

线程安全集合

多线程演示线程不安全集合

结果会出现java.util.ConcurrentModificationException异常


import java.util.ArrayList;
import java.util.Arrays;

public class Demo5 {
    public static void main(String[] args) {
        // 1. 创建arraylist
        ArrayList<String > arrayList = new ArrayList<> ();
        // 2. 创建线程
        for (int i = 0; i <20 ; i++) {
            int temp = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j <10 ; j++) {
                        arrayList.add(Thread.currentThread().getName()+"===="+ temp + "==="+j);
                        System.out.println(arrayList.toString());
                    }
                }
            }).start();
        }
        
    }
   
    
}

Collections中的工具方法


public static <T> Collection<T> synchronizedCollecction(Collection<T> c)

public static <T> List<T> synchronizedList(List<T> list)

public static <T> Set<T> synchronizedSet(Set<T> s )

public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m )

public static <T> SortesSet<T> synchronizedSortesSet(SortesSet<T> s)

public static <K,V> SortMap<K,V> synchronizedSortMap(SortMap<K,V>  m)

JDK1.2提供,接口统一,维护性高,但性能没有提升,均已synchronized实现

// 1. 创建arraylist
        ArrayList<String > arrayList = new ArrayList<> ();
        List<String> synlist = Collections.synchronizedList(arrayList);

CopyOnWriteArrayList

线程安全的ArrayList,加强版的读写分离

写有锁,读无锁,读写之间不阻塞,优于读写锁

写入时,先copy一个容器副本,在添加新元素,最后替换引用

CopyOnWriteArraySet

线程安全的Set,底层使用CopyOnWrityArrayList实现。

唯一不同在于 ,使用addIfAbsent()添加元素,会遍历数组。

如存在元素,则不添加(扔掉副本)

Queue接口(队列)

Collcetion的子接口,表示队列FIFO,先进先出

常用方法:

	抛出异常:
	boolean add(E e) :顺序添加第一个元素(到达上限后,再添加会抛出异常)

	E remove() // 获得第一个元素并移除(如果队列没有 元素时,会抛出异常)

	E element() // 获得第一个元素但不移除(如果队列没有元素,则跑异常)

	返回特殊值:推荐使用
	boolean offer(E e): // 顺序添加一个元素(到达上限后,在添加则会返回false)
	
	E poll()//获得第一个元素并移除(如果队列没有元素,则返回null)

	E peek() // 获得第一个元素但并不移除,(如果队列没有元素时,则返回null)

ConcurrentLinkedQueue

线程安全,可高效读写的队列,高并发下性能最好的队列。

无锁,CAS比较交换算法,修改的方法包含三个核心参数(V,E,N)

V:要更新的变量,E:预期值, N:新值

只有当V==E时,V=N;否则表示已被更新过,则取消 当前操作。


package 多线程;

import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * @Author 杨栋
 * @Date 2020/7/8 17:37
 * ConcurrentLinkedQueue的使用
 */

public class Demo6 {
    public static void main(String[] args) throws InterruptedException {
        // 1.创建安全队列
        ConcurrentLinkedQueue<Integer> queue = new ConcurrentLinkedQueue<>();
        // 2. 入队操作 
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <5 ; i++) {
                    queue.offer(i);
                }
            }
        });
        
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 6; i <10 ; i++) {
                    queue.offer(i);
                }
            }
        });
        // 3. 启动线程
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        
        // 4. 出队操作
        int size = queue.size();
        for (int i = 0; i <size ; i++) {
            System.out.println(queue.poll());
        }
    }
}

BlockQueue接口(阻塞队列)

Queue的子接口,阻塞的队列,增加了两个线程状态为无限期等待的方法

void put(E e) //将指定元素插入此队列中,如果没有可用空间,则等待。

E take() //获取并移除队列头部元素,如果没有可用元素,则等待

可用于解决生产者,消费者问题。

ArrayBlockingQueue

数组结构实现,有界队列。(手工固定上限)

LinkedBlockingQueue

链表结构实现,有界队列。(默认上限Integer.Max_VALUE)

ConcurrentHashMap

初始默认容量为16段(Segment),使用分段锁设计。

不对整个Map加锁,而是为每个Segment加锁。

当多个对象 存入同一个Segment时,才需要互斥

最理想的状态为16个对象分别存入16个Segment,并行数量16

使用方式与HashTable无异。

总结

ExecutrService线程池接口,Executors工厂。

Callable线程任务,Future异步返回值。

Lock,ReentrantLocak重入锁,ReenTrantWriteLock读写锁

CopyOnWriteArrayList线程安全的ArrayList

CopyOnWriteArraySet线程安全的Set

ConcurrentLinkedQueue线程安全的Queue

ArrayBlockQueue线程安全的HashMap

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java 多线程是指在一个程序中可以同时执行多个线程,并且每个线程独立运行,互不干扰。Java 多线程Java 平台提供的重要特性,常用于提高程序并发性能和解决复杂问题。 Java 多线程的重点知识点包括以下几个方面: 1. 线程的创建和启动:可以通过继承 Thread 类或实现 Runnable 接口来创建线程,并使用 start() 方法启动线程。 2. 线程的生命周期:线程的生命周期包括新建、就绪、运行、阻塞和死亡等阶段,可以通过调用 sleep()、wait()、join() 等方法来控制线程的状态转换。 3. 线程同步:多个线程之间可能会共享资源,为了保证数据的一致性和避免冲突,需要使用同步机制,如 synchronized 关键字、Lock 接口等。 4. 线程间通信:多个线程之间可以通过共享内存或者消息传递的方式进行通信,如使用 wait()、notify()、notifyAll() 等方法。 5. 线程池:线程池可以管理和复用线程,减少线程的创建和销毁开销,提高系统的性能。 6. 线程安全性:在多线程环境下,存在资源竞争和线程安全性问题,需要采取相应的措施来保证线程的安全性,如使用 synchronized 关键字、volatile 关键字等。 7. 线程调度:Java 提供了线程调度器来控制线程的执行顺序和优先级,可以使用 yield()、join()、setPriority() 等方法进行调度。 8. 线程异常处理:在多线程环境下,线程的异常需要处理和捕获,可以使用 try-catch 块来捕获异常。 综上所述,Java 多线程Java 平台提供的重要特性,掌握多线程的概念和相关知识点可以帮助开发者提高程序的并发性能和解决复杂问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值