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);
}
//考虑阻塞,一定要考虑锁那个对象!
【等待需要在同步环境中,不持锁,报错!——对应上图】
线程同步和锁机制
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.一个私有的数据区