Java 线程应用

一旦线程启动,它就永远不能再重新启动。只有一个新的线程可以被启动,并且只能一次。//否则报:java.lang.IllegalThreadStateException

一系列线程以某种顺序启动并不意味着将按该顺序执行。对于任何一组启动的线程来说,调度程序不能保证其执行次序,持续时间也无法保证。

      队列形式是指当一个线程完成“一轮”时,它移到可运行队列的尾部等待,直到它最终排队到该队列的前端为止,它才能被再次选中。

      尽管我们没有无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式。

//线程创建方式:两种

1:Thread类衍生创建

2:实现Runnable()接口

//注意单线程 && 多线程 的执行顺序可控



每个线程的数据区是独立的。

线程传参:尽可能使用 final 修饰。【线程安全】{注意:final修饰的值往往是固定的, 但是不要带变量!}




redis:缓存、消息中间件

MQ:消息中间件【看系统的吞吐量】

Hadoop:选举机制

【中间件:独立于系统之外】

多线程情况下,会出现什么问题:

1.对旧值不可见,从而造成数据的重复、延迟、滞后。

synchronized 线程同步、同步锁!

---同步代码块、同步方法

---1.线程持锁,其他线程进入对象锁阻塞状态

---2.synchronized代码执行完毕后,其他线程开始竞争持锁。

线程调度

线程是不可控的,那么如何干预线程的执行?

1.等待:让某个线程进入等待状态,等到条件满足后,被其他线程唤醒。

this.wait();一直等待下去

this.wait(3000);//等待3S后,如果3s内未被唤醒,3s后自动结束【结束后,争夺锁资源】

2.休眠:是让当前线程进入休眠状态,这是Thread的静态方法,休眠只能自己醒!

【1】本质:至少休眠指定时间。休眠之后,等待资源分配!

【2】休眠不可以被唤醒!

3.让步【让线程从 运行状态回到就绪状态】

线程优先级1~10;默认为5;

【多线程调度应用最好不要依赖于线程的优先级】——可做为一种提高程序效率的方法。【不可依赖!】

yield——【让相同优先级的线程之间能适当的轮转执行】

【让步没有经过阻塞,直接回到就绪状态】

4.合并【让并行的线程,变为串行线程】

t1.start();t1.join();t2.start();

[先执行再合并]

------------------------------------------------------------------------------------------------

什么情况要合并线程?

【某个线程要等到前一个线程的结果时,需要合并,方便来等待前面的结果】

ex:支付、秒杀

MQ:异步通知。


5.守护线程【GC:垃圾回收】

t1.start();//用户线程

t2.setDaemon(true);//设置为守护线程

t2.start();


线程同步:

同步方法:保护整个方法当中所有数据的安全。

同步代码块:保护方法中某个区域的数据安全。【让那些不需要考虑到同步的数据先执行,提高效率】

一般来说:属于线程本身数据区的数据和读的数据,不影响。


同步:只能同步方法,而不能同步变量和类。

同一个类可以同时拥有同步和非同步方法。

多个线程共用同一个实例的时候,同步生效。

---wait()  \  notiryall()

线程休眠不释放资源。

同一个线程可以获得多个锁。


静态方法同步

非静态同步,锁时对象锁,只有相同实例才存在哦同步

静态同步,锁的时类。只要时这个类,就会触发同步!

静态的锁:类。


不建议:同时使用 静态和非静态锁!

同步静态锁:

public static synchronized int test();

public void test(){

synchronized(D.class);

}

非同步静态锁:

publci synchronized int test();

public void test1(){

C c= new C();

synchronized(c);

}

//考虑阻塞,一定要考虑锁那个对象!


image

【等待需要在同步环境中,不持锁,报错!——对应上图】

线程同步和锁机制

1.线程安全类

集合:Collections

List<String> list = Collections.synchronizedList(Arrays.asList(new String[] {"hello","world"}));
         list.contains("tesst");

流的:ByteArrayInputStream

FileInputStream fStream = new FileInputStream(new File(""));
         byte[] bytes = new byte[fStream.available()];
         fStream.read(bytes);
         ByteArrayInputStream bStream = new ByteArrayInputStream(bytes);
         bStream.read();

StringBuffer\StringBuilder

注意:

即便是操作的线程安全类,安全仅仅局限于:对该类本身的操作。并不意味着操作其他变量也一定安全。

【整个操作流程安全才是封装好的】


2.死锁:两个线程持有对方的锁,且互相等待。

【概率很小,但是无论代码中发生死锁的概率有多小,一旦发生死锁,程序就死掉了!】

避免死锁的策略:按照相同的顺序获取锁,并释放锁。


锁的特性:

1.互斥:

2.可见性



线程同步:为了保护多个线程访问一个资源时对资源的破坏。

实现方法:线程一旦获取对象锁,其他访问该对象的线程就无法获得该对象的其他同步方法。【即进入阻塞状态】

而:对于静态同步方法,锁是针对该类,静态与非静态方法的锁互不干预。

原子化:【与事务的原子性一致】

ex: volatile变量

1.不能用作线程计数器。

2.只能保障独立线程安全问题。

常见模式:

【1】状态标志:布尔状态标志。

【2】一次性安全发布:单例、静态【final变量不支持,因为 fanal变量是禁止修改的,也不存在线程安全的问题】

【3】独立观察,定期发布。——一写多读

【4】volatile bean 模式,适用于 JavaBean。【实体类:getter、setter方法】??

[5]开销较低的读-写策略



Volatile应用案例




线程整体研究深入——时间问题!(基础研究问题)

1:线程池,对象池的思想。

本质:对象池,在其中放入若干未死亡的线程,等到必要的时候,再调用start()。

//java.util.concurrent包下:

线程池中:execute(Runnable 对象),即:通过Thread类实现的线程,也是实现Runnable接口!

2:线程状态:运行状态、等待状态。

(1)归池:尽量减少对象生成。

MyThread treadThread = new MyThread();
         pool.execute(treadThread);
         pool.execute(treadThread);pool.execute(treadThread);pool.execute(treadThread);
         pool.execute(treadThread);pool.execute(treadThread);


【1】固定尺寸的线程池

//核心线程个数:每一次并发的线程最大个数。当该线程死亡后,会生成一个新的线程。

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

public class ExecutorServiceDemo {
     public static void main(String[] args) {
         ExecutorService pool = Executors.newFixedThreadPool(3);
         pool.execute(new MyThread());
         pool.execute(new MyThread());
         pool.execute(new MyThread());
         pool.execute(new MyThread());
         pool.execute(new MyThread());
         pool.execute(new MyThread());
     }
}

class MyThread implements Runnable{
     public void run() {
         // TODO Auto-generated method stub
         try {
             System.out.println(Thread.currentThread().getName());
             Thread.sleep(3000);
         } catch (InterruptedException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
     }
}

//

shutdown():当线程池调用后,会关闭!否则,会持续往该线程池当中添加新的线程。


【2】单任务线程池

//一次只执行一次,并且:该线程池当中,只有一个线程。

//用完以后归池

ExecutorService pool = Executors.newSingleThreadExecutor();



【3】可变尺寸连接池

ExecutorService pool = Executors.newCachedThreadPool();


//上述三个归结为一类!

【4】延迟连接池

ScheduledExecutorService pool = Executors.newScheduledThreadPool(3);

pool.execute(treadThread);

pool.execute(treadThread);

//延迟的线程不会阻塞当前队列池中的其他线程的执行。

pool.schedule(treadThread, 5000, TimeUnit.MILLISECONDS);

【5】单任务延迟连接池

ScheduledExecutorService pool = Executors.newSingleThreadScheduledExecutor();

pool.execute(treadThread);

pool.execute(treadThread);

//同样支持:延迟

pool.schedule(treadThread, 5000, TimeUnit.MILLISECONDS);


【6】自定义线程池【均继承于 ThreadPoolExecutor类】

所有的线程池,都是使用自定义线程池实现的。

如果核心数满了,则加入队列;如果队列满了,没达到上限,则新建线程。

corePoolSize:【核心线程数:关心不是那个线程是核心,而是线程数量,没有哪个线程是核心】

maximumPoolSize:【】

workQueue

//实现:

BlockingQueue<Runnable> bQueue = new ArrayBlockingQueue<Runnable>(2);//归池操作
ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 4, 0, TimeUnit.MICROSECONDS, bQueue);

//

耗时线程:pool-1-thread-1
pool-1-thread-4
pool-1-thread-3
耗时线程:pool-1-thread-2
pool-1-thread-3
pool-1-thread-4

//


public class MyPoolService {
     public static void main(String[] args) {
         BlockingQueue<Runnable> bQueue = new ArrayBlockingQueue<Runnable>(2);
         ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 4, 0, TimeUnit.MICROSECONDS, bQueue);
         MyThread3 myThread3 = new MyThread3();
         MyThread3 myThread31 = new MyThread3();
         MyThread4 myThread4 = new MyThread4();
         MyThread5 myThread5 = new MyThread5();
        
         pool.execute(myThread3);
         pool.execute(myThread31);
        
         pool.execute(myThread4);
         pool.execute(myThread4);
        
         pool.execute(myThread5);
         pool.execute(myThread5);
        
     }
}

class MyThread3 implements Runnable{
     public void run() {
         // TODO Auto-generated method stub
         try {
             System.out.println("耗时线程:"+Thread.currentThread().getName());
             Thread.sleep(5000);
         } catch (InterruptedException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
     }
}

class MyThread4 implements Runnable{
     public void run() {
         // TODO Auto-generated method stub
         try {
             System.out.println(Thread.currentThread().getName());
             Thread.sleep(1000);
         } catch (InterruptedException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
     }
}

class MyThread5 implements Runnable{
     public void run() {
         // TODO Auto-generated method stub
         try {
             System.out.println(Thread.currentThread().getName());
             Thread.sleep(1000);
         } catch (InterruptedException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
     }
}


串行线程【解决依赖】&&&并行线程【多线程节约资源】



比如:

2:异步【线程的消息系统】

锁、信号量和阻塞

//加锁  XX   解锁

public class MyLock {
     public static void main(String[] args) throws Exception{
         MyThread8 thread = new MyThread8();
         Thread thread2 = new Thread(thread);
         thread2.start();
     }
}

class MyThread8 implements Runnable{
     Lock lock = new ReentrantLock();
     public void run() {
         // TODO Auto-generated method stub
         System.out.println("加锁");
         lock.lock();
         try {
             System.out.println(Thread.currentThread().getName());
             Thread.sleep(3000);
         } catch (InterruptedException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
         lock.unlock();
         System.out.println("解锁");
     }
}

//重入锁、读写锁

【对共享资源有读和写的操作,且写操作没有读操作这么频繁】

——1.如果没有写,其他都可读

——2.如果有写,其他线程不支持读和写

//实现

public class MyReadWriteLock2 {
     public static void main(String[] args) throws Exception{
         A a = new A();
         new Thread(new MyThread12(a)).start();
         new Thread(new MyThread11(a)).start();
         new Thread(new MyThread11(a)).start();
         new Thread(new MyThread11(a)).start();
     }
}

class MyThread11 implements Runnable{
     private final A a;
     public MyThread11(final A a) {
         this.a = a;
     }
     public void run() {
         a.read();
     }
}

class MyThread12 implements Runnable{
     private final A a;
     public MyThread12(final A a) {
         this.a = a;
     }
     public void run() {
         a.write();
     }
}

class A {
     private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
     public void read() {
        lock.readLock().lock();
         System.out.println("读加锁");

         try {
//            System.out.println(Thread.currentThread().getName());
             Thread.sleep(3000);
         } catch (InterruptedException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
         lock.readLock().unlock();
         System.out.println("读解锁");

     }
     public void write() {
         lock.writeLock().lock();
         System.out.println("写加锁");

         try {
//            System.out.println(Thread.currentThread().getName());
             Thread.sleep(3000);
         } catch (InterruptedException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
         lock.writeLock().unlock();
         System.out.println("写解锁");

     }
}

读写锁的三个特质:

1.公平和不公平

   【公平锁】获取锁的顺序:线程调用的顺序。

   【不公平锁:默认】获取锁的顺序和调用锁的顺序没有关系;相对来说,非公平锁的效率较高。

2.读锁和写锁,都支持线程重进入。【一个线程可以同时拥有读锁和写锁

3.锁降级:获取写锁 –> 获取读锁 –>释放写锁

【获取锁的顺序:先获取写锁,在获取读锁】——重进入

【读写锁顺序不支持顺序颠倒】

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class MyReadWriteLock3 {
     public static void main(String[] args) throws Exception{
         ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
         readWriteLock.writeLock().lock();
         System.out.println("写 占锁");
         readWriteLock.readLock().lock();
         System.out.println("读 占锁");

        readWriteLock.writeLock().unlock();
         System.out.println("释放写锁");
         //此时效果相当于将写锁降级为读锁


     }
}

信号量

public class MySemaphore {
     public static void main(String[] args) throws Exception{
         Semaphore semaphore = new Semaphore(10);
         semaphore.acquire(5);//添加信号量
         System.out.println(semaphore.availablePermits());
         semaphore.acquire(3);
         System.out.println(semaphore.availablePermits());
         semaphore.acquire(1);
         System.out.println(semaphore.availablePermits());
         semaphore.release(5);//释放信号量
         System.out.println(semaphore.availablePermits());
         semaphore.acquire(4);
         System.out.println(semaphore.availablePermits());
     }
}

阻塞队列:当队列中的数据满的时候,进入阻塞状态

组赛队列分为:阻塞和非阻塞两种状态。

public static void main(String[] args) throws Exception{
     BlockingQueue<String> bqueue = new ArrayBlockingQueue<String>(10);
     bqueue.add("hello");
     bqueue.offer("world");
     //put()添加元素,如果没有多余空间,方法会一直阻塞,直到队列中没有多余的空间   
}

阻塞栈【类似于 LinkedList】

//本质:将集合进一步封装!        // 集合中 内部类 Deque \queue 等

public static void main(String[] args) throws Exception{
     BlockingDeque<String> bqueue = new LinkedBlockingDeque<String>(10);
     bqueue.addFirst("1");
     bqueue.putFirst("3");//有阻塞状态
}



条件变量

        Lock lock = new ReentrantLock();
         Condition condition = lock.newCondition();
         condition.await();
//        condition.notifyAll();
         condition.signalAll();


原子量:针对Java中所有的变量类型设计的一种原子化操作的变量。

【原子量只能保证该原子量安全】

//应用

AtomicInteger ai = new AtomicInteger();
         AtomicBoolean ab;
         AtomicIntegerArray aisArray = new AtomicIntegerArray(3);
         aisArray.getAndSet(0, 11);
         aisArray.getAndSet(1, 12);
         aisArray.getAndSet(2, 11);
         AtomicReference<Double> aReference;
         AtomicReferenceArray<Short> asArray;

//原子量自身是安全的,但是如果操作涉及其他变量,不能保证其他变量的线程安全。


障碍器:必须等到所有子线程执行完毕以后,才会执行主线程,实际上,是子线程通知后进入等待状态,主线程执行完毕后,子线程停止等待。

//将阻塞器与需要阻塞线程关联

CyclicBarrier cyclicBarrier2 = new CyclicBarrier(5, new MainThread());
         new Thread(new SubThread(cyclicBarrier2)).start();
         new Thread(new SubThread(cyclicBarrier2)).start();
         new Thread(new SubThread(cyclicBarrier2)).start();
         new Thread(new SubThread(cyclicBarrier2)).start();
         new Thread(new SubThread(cyclicBarrier2)).start();

class MainThread implements Runnable{

    @Override
     public void run() {
         // TODO Auto-generated method stub
         System.out.println("主线程启动");
     }
}

class SubThread implements Runnable{
     private final CyclicBarrier cyclicBarrier;
     public SubThread(final CyclicBarrier cyclicBarrier) {
         // TODO Auto-generated constructor stub
         this.cyclicBarrier =  cyclicBarrier;
     }
     @Override
     public void run() {
         // TODO Auto-generated method stub
         System.out.println("子线程启动");
         try {
             cyclicBarrier.await();//通知次数达到 阻塞器定义的次数,启动对应的线程
             System.out.println("通知结束"); //主线程执行完毕后,唤醒所有等待的子线程

         } catch (InterruptedException | BrokenBarrierException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
     }
    
}

有返回值的线程:

一个有返回值的线程

但是:必须等到他执行完之后,才能拿到结果。

//

ExecutorService pool = Executors.newFixedThreadPool(3);
        
         Future<String> future = pool.submit(new Callable<String>() {

            @Override
             public String call() throws Exception {
                 // TODO Auto-generated method stub
//                return null;
                 return "这是callable返回字符串";
             }
         });
        
         String result = future.get();
         System.out.println(result);      
         pool.execute(new Thread());
         pool.execute(new MyThread7());      
         pool.shutdown();

运算阻塞:执行过程中线程不可中断,否则得不到结果。【考虑串行】

最大线程并发数:CPU内核数。

如果能够预见某次运算的执行期间过长,才可以判断其是运算阻塞。

IO流阻塞:执行短暂,比如:读取某个数据,读取文件。【考虑并行】


分布式:重【架构思想】——完整项目,若干模块!【系统架构】

微服务【设计思路】为了解决这个问题。【业务设计】



分段计算:Hadoop【不建议造成运算阻塞】【算MR / 存HDFS】

1:计算大批量数据【MR-HIVE—JDBC】

简单数据模型:流式计算

Map: 拆分成若干块,可以把拆分的数据分发到不同主机上运算。拆分后的数据运算量合理范围!

Shuffer:各个主机运算完后,把数据会陆续汇总到该阶段,用来负责分组。

Reduce:汇总累计。

可以通过合理的设计,避免运算阻塞,可以使其转化为IO流阻塞。



Spring 单例

Java EE:

JDBC——数据库

Servelet——页面交互【JSP】

Rest\Restful ——Jason【本质】




一个线程包含以下内容:

1.一个指向当前被执行指令的指令指针

2.一个栈

3.一个寄存器值的集合

4.一个私有的数据区

posted @ 2020-03-25 16:37  小海_macro  阅读(175)  评论(0编辑  收藏  举报