目录
一,线程的状态
二,线程池
1,以前写多线程的弊端
2,线程池的实现步骤
3,代码实现
a:创建默认个数的线程池
b:创建指定个数的线程池
c:自己创建线程池
三,Volatile
四,原子性
1,AtomicInteger原子型类:
2,使用原子性解决送冰淇凌问题
3,AtomicInteger原理
4,Synchroized和CSA的区别
五,并发工具类
1,Hashtable
2,ConcurrentHashMap
3,CountDownLatch类
4,Semaphore类
一,线程的状态
线程的状态分为,新建状态,就绪状态,运行状态,死亡状态
当线程通过start()方法到达就绪状态的时候,强盗CPU的执行权就会到运行状态,没有就等待。
在运行状态会遇到阻塞队列,wait()等待方法,和sleep()都会使这条线程回到就绪状态。
虚拟机中线程的六种状态:
新建状态(new)------ 创建线程对象
就绪状态(RUNNABLE) ------ start方法
阻塞状态(BLOCKED) ------ 无法获得到锁对象
等待状态(WAITING) ------ wait方法
计时状态(TIMED_WAITING) ------ sleep方法
结束状态(TERMINATED) ------ 全部代码运行完毕
二,线程池 1,以前写多线程的弊端(1),用到线程就创建
(2),用完之后线程就消失
2,线程池的实现步骤(1),创建一个池子,池子中是空的
(2),有任务需要执行时,才会创建线程对象,当任务执行完毕,线程对象归还给池子
3,代码实现(1),创建一个池子,池子中是空的 ------ 创建Executor中的静态方法
(2),有任务需要执行时,创建线程对象 -------- submit方法
任务执行完毕,线程对象归还给池子
(3),所有的任务全部执行完毕,关闭连接池 ------ shutdown方法
a:创建默认个数的线程池public class MyThreadPoolDemo01 { public static void main(String[] args) throws InterruptedException { //1,创建一个默认的线程池对象,池子中默认是空的,默认最多可以容纳int类型的最大值。 ExecutorService executorService = Executors.newCachedThreadPool(); //Executors -- 可以帮助我们创建线程池对象 //ExecutorService --- 可以帮助我们控制线程池 //Executors方法是静态的,通过类名调用,返回值是ExecutorService; executorService.submit(()->{ Thread.currentThread().setName("线程一"); System.out.println(Thread.currentThread().getName()+"在执行了"); }); Thread.sleep(2000);//睡眠的话,第一个线程结束。放进了线程池。 //下次开启线程的话,是从线程池中获取线程的。 executorService.submit(()->{ }); executorService.shutdown(); } }
默认的线程池,最多可以 int类型最大值数量个线程
ExectursService中的submit()方法会自动创建线程,线程执行完之后,也会自动返回到池子中
线程池执行完毕之后一定要使用shoutdown方法关闭。
b:创建指定个数的线程池// static ExecutorService new FixedThreadPool(int nThreads); // 创建一个指定最多线程数量的线程池 public class MyThreadPoolDemo02 { public static void main(String[] args) throws InterruptedException { //这个里面的参数不是初始值,而是最大值。创建出来的线程池也是空的。 ExecutorService executorService = Executors.newFixedThreadPool(10); //强转 ThreadPoolExecutor pool = (ThreadPoolExecutor) executorService; System.out.println(pool.getPoolSize());//这里时使用ThreadpoolExecutor中的 //getPoolSize()方法获取线程池中的线程个数 //线程一 executorService.submit(() -> { Thread.currentThread().setName("线程一"); System.out.println(Thread.currentThread().getName() + "在执行了"); }); //线程二 executorService.submit(() -> { Thread.currentThread().setName("线程二"); System.out.println(Thread.currentThread().getName() + "在执行了"); }); //线程三 executorService.submit(() -> { Thread.currentThread().setName("线程三"); System.out.println(Thread.currentThread().getName() + "在执行了"); }); //关闭线程 executorService.shutdown(); //获取线程池中线程的个数 System.out.println(pool.getPoolSize()); } }
指定线程池中的最大数量就是表示线程池中最大可以有的线程数。
c:自己创建线程池构造方法:
ThreadPoolExecutor tpe = new ThreadPoolExecutor
(核心线程数量,最大线程数量,空闲线程最大存活时间,存活时间单位,任务队列,创建线程工厂,任务的拒绝策略);
1,参数一:核心线程数量
2,参数二:最大线程数量
3,参数三:空闲线程最大存活时间
4,参数四:空闲线程存活时间的单位-- TimeUnit类
5,参数五:任务队列--阻塞队列ArrayBlockingQueue
让任务在队列中等着,等有空闲线程了,再从这个队列中获取任务并执行。
6,参数六:创建线程工厂--怎么创建线程的对象--Executors中的静态方法,defaultThreadFactory()
按照默认的方式创建线程对象
7,参数七:任务的拒绝策略,ThreadPoolExecutor中的内部类对象---new ThreadPoolExecutor.AborPolicy();
(1)什么时候拒绝
当提交的任务大于池子中最大线程数量+任务队列容量
(2)怎么拒绝
//自己创建线程池 public class ThreadPoolExecutorDemo01 { public static void main(String[] args) { MyThread01 mt = new MyThread01(); ThreadPoolExecutor tpe = new ThreadPoolExecutor(2, 5, 2, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); tpe.submit(()->{//创建第一个线程 Thread.currentThread().setName("线程一"); System.out.println(Thread.currentThread().getName()+"执行了"); }); tpe.submit(()->{//创建第二个线程 Thread.currentThread().setName("线程二"); System.out.println(Thread.currentThread().getName()+"执行了"); }); System.out.println(tpe.getPoolSize());//获取线程的数量 tpe.shutdown();//关闭线程池 } //也可以使用创建资源类的方法来写,只需要把资源类对象传入tpe.submit()方法中。 }
参数四是使用TimeUnit类来调用时间单位的
Days:天
HOURS:小时
MICROSECONDS:微妙
MILLISECONDS:毫秒
MINUTES:分钟
NANOSECONDS:纳秒
SECONDS:秒
参数七,任务拒绝策略
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。默认的策略
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常,这是不推荐的做法
ThreadPoolExecutor.DiscardOldestPolicy:抛弃队列中等待最久的任务然后把当前任务加入队列中
ThreadPoolExecutor.CallerRunsPlolicy:调用run()方法绕过线程池直接执行
三,Volatile1,堆内存是唯一的,每一个线程都有直接的线程栈
2,每一个线程在使用堆里面变量的时候,都会先拷贝一份到变量的副本中
3,在线程中,每一次使用时从变量的副本中获取饿
问题:
如果A线程修改了堆中共享变量的值
那么其他线程不一定能及时使用最新的值
Volatile关键字
强制线程每次在使用的时候,都会看一下共享区域最新的值
public class Money { public static volatile int money = 100000; }
使用只需要在共享数据前加上Volatile关键字即可
也可以使用Synchronized同步代码块
1,线程获得锁
2,清空变量副本
3,拷贝共享变量最新的值到变量副本中
4,执行代码
5,将修改后变量副本中的值赋值给贡献数据
6,释放锁
四,原子性所谓的原子性时指在一次 *** 作或者多次 *** 作中,要么所有的 *** 作全部都得到了执行并且不受到任何因素的干扰而打断,要么所有的 *** 作都不执行,多个 *** 作是一个不可分割的整体
coubt++:不是一个原子性 *** 作,也就是在执行的过程中有可能被其他线程打断 *** 作。
public class MyAtom01 implements Runnable{ private int count = 0; @Override public void run() { for (int i = 0; i < 100; i++) { //1,从共享数据中读取数据到本线程栈中 //2,修改本线程栈变量副本的值 //3,会把本线程栈变量副本的值赋值给共享数据 synchronized (this) { count++; System.out.println("送出了"+count+"个"); } } } }
Volatile关键字:只能保证线程每次使用共享数据的时候是最新指,
但是不能保证原子性。
使用Synchronized加锁可以解决代码的原子性,但是效率太低。
1,AtomicInteger原子型类:JDK1.5之后,使用原子类(Atomiclnteger)来更新共享数据
1,构造方法:
public class AtomicInteger01 { public static void main(String[] args) { AtomicInteger ac = new AtomicInteger();//初始化一个默认值为0的原子型Integer System.out.println(ac); AtomicInteger ac2 = new AtomicInteger(10); //初始化一个指定值的原子型Integer System.out.println(ac2); }
2,成员方法:
public class AtomicInteger02 { public static void main(String[] args) { AtomicInteger ac = new AtomicInteger(10); int i = ac.get(); System.out.println(i); int andIncrement = ac.getAndIncrement(); System.out.println(andIncrement); int i1 = ac.incrementAndGet(); System.out.println(i1); int i2 = ac.addAndGet(10); System.out.println(i2); int andSet = ac.getAndSet(19); System.out.println(andSet); System.out.println(ac.get()); } }2,使用原子性解决送冰淇凌问题
线程体类
public class MyAtom01 implements Runnable { // private int count = 0; AtomicInteger ac = new AtomicInteger(0); @Override public void run() { for (int i = 0; i < 100; i++) { int i1 = ac.incrementAndGet(); System.out.println("送出了" + i1 + "个"); } } }
测试类
public class Demo01 { public static void main(String[] args) { MyAtom01 mt = new MyAtom01(); for (int i = 0; i < 100; i++) { new Thread(mt).start(); } } }3,AtomicInteger原理
自旋锁+ CAS算法
CAS算法:有3个 *** 作数(内存值V,旧的预期值A,要修改的值B)
当旧的预期值A == 内存值 此时修改成功 ,将V改为B
当旧的预期值A !=内存值 此时修改失败,不做任何 *** 作
并重新获取现在的最新值(这个重新获取的动作就是自旋)
在修改共享数据的时候,把原来的旧值记录下来。如果现在内存中的值跟原来的旧值一样,证明没有其他线程 *** 作过内存值,则修改成功。如果现在内存中跟原来的旧址不一样了,证明已经有其他线程 *** 作过内存值了,则修改失败,需要获取现在的最新值,再次进行 *** 作,这个重新 *** 作的过曾就是自旋。
4,Synchroized和CSA的区别相同点:在多线程情况下,都可以保证共享数据的安全性
不同点:Synchroized总是从最坏的角度出发,认为每次获取数据的时候,别人有可能修改,所以在每次 *** 作共享数据之前,都会上锁(悲观锁)
csa是从乐观的角度除法,假设每次过去数据别人都不会修改,所以不会上锁,只不过在修改共享数据的时候,会检查一下,别人有没有修改过这个数据
如果别人修改过,那么我们再次获取现在的最新值
如果没有修改过,那么就直接修改这个共享数据的值
(乐观锁)
五,并发工具类HashMap是线程不安全的(多线程环境下可能出现问题)
为了保证数据的安全性我们可以使用Hashtable,但是Hashtable的效率低下
1,Hashtable(1),Hashtable采取悲观锁synchronized的形式保证数据的安全性
(2),只要有线程访问,会将整张表全部锁起来,所以Hashtable的效率底下
2,ConcurrentHashMapMap接口:HashMap,Hashtable ,TreeMap, ConcurrentMap
ConcurrentHashMap1.8版本底层原理是:哈希表(数组,链表,红黑树结合体)
结合CAS机制+synchronized同步代码块形式保证线程安全
3,CountDownLatch类方法:
当前线程执行完毕
运用
等待的资源类
public class MotherThread extends Thread{ private CountDownLatch cdl; public MotherThread(CountDownLatch cdl) { this.cdl = cdl; } @Override public void run() { try { cdl.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("洗碗"); } }
在执行的资源类
public class ChileThread1 extends Thread{ private CountDownLatch cdl; public ChileThread1(CountDownLatch cdl) { this.cdl = cdl; } @Override public void run() { for (int i = 1; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "在吃第" + i + "个饺子。"); } cdl.countDown(); } }
测试类:
public class Demo { public static void main(String[] args) { CountDownLatch cdl = new CountDownLatch(1); MotherThread mt = new MotherThread(cdl); mt.start(); ChileThread1 c1 = new ChileThread1(cdl); c1.setName("孩子一"); c1.start(); } }
小结:
使用场景:让某一条线程等待其他线程执行王碧之后再执行
CountDownLatch(int count) :参数写等待线程的数量,并定义一个计数器
await():让线程等待,当计数器为0时,会唤醒等待的线程
countDown():线程执行完毕时调用,会将技术器减1;
4,Semaphore类Semaphore可以设置线程的通行数量
资源类
public class MyRunnable01 implements Runnable{ private Semaphore Semaphore; public MyRunnable01(Semaphore ap) { this.Semaphore = ap; } @Override public void run() { try { Semaphore.acquire(); System.out.println("获得了通行证。"); Semaphore.release(); System.out.println("归还。"); } catch (InterruptedException e) { e.printStackTrace(); } } }
测试类
public class Demo01 { public static void main(String[] args) { Semaphore ap = new Semaphore(2); MyRunnable01 mr = new MyRunnable01(ap); for (int i = 0; i < 10; i++) { new Thread(mr).start(); } } }
Semaphore s = new Semaphore(参数);
获取管理员对象,参数是最大可执行的线程数
s.acquire();获取通行证的方法
s.release();归还通行证的方法
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)