https://blog.csdn.net/mockingbirds/article/details/51336105(多线程理解 对象和变量)
https://blog.csdn.net/u013142781/article/details/51697672
volatile 的使用,使变量变成可见性的,每次线程中使用到都到主存中去读取
https://juejin.im/post/5ab70c996fb9a028c812d06f
https://blog.csdn.net/eff666/article/details/66473088
synchronized: 具有原子性,有序性和可见性;
volatile:具有有序性和可见性
对静态方法使用synchronized 会直接封锁整个类
ynchronized关键字和volatile关键字比较
volatile关键字是线程同步的轻量级实现,所以volatile性能肯定比synchronized关键字要好。但是volatile关键字只能用于变量而synchronized关键字可以修饰方法以及代码块。synchronized关键字在JavaSE1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁以及其它各种优化之后执行效率有了显著提升,实际开发中使用synchronized关键字还是更多一些。
多线程访问volatile关键字不会发生阻塞,而synchronized关键字可能会发生阻塞
volatile关键字能保证数据的可见性,但不能保证数据的原子性。synchronized关键字两者都能保证。
volatile关键字用于解决变量在多个线程之间的可见性,而synchronized关键字解决的是多个线程之间访问资源的同步性。
。
1.java并发编程(https://www.cnblogs.com/dolphin0520/p/3920397.html)
1)CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:
CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;
而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;
另外,CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。
2)Semaphore其实和锁有点类似,它一般用于控制对某组资源的访问权限。
2.java内置线程池(https://fangjian0423.github.io/2015/07/24/java-poolthread/)
例:1000个爬虫任务使用了多线程的处理方式,比如开5个线程处理这1000个任务,每个线程分200个任务,然后各个线程处理那200个爬虫任务→_→,太笨了。其实更合理的方法是使用阻塞队列+线程池的方法。
http://www.importnew.com/21089.html
3.关闭线程池执行的几种方策
shutdown() 执行后停止接受新任务,会把队列的任务执行完毕。
shutdownNow() 也是停止接受新任务,但会中断所有的任务,将线程池状态变为 stop。
isShutDown当调用shutdown()或shutdownNow()方法后返回为true。
isTerminated当调用shutdown()方法后,并且所有提交的任务完成后返回为true;
isTerminated当调用shutdownNow()方法后,成功停止后返回为true;
如果线程池任务正常完成,都为false
1.使用 server.shutdown();正常终止线程(), server.shutdownNow()强行终止线程。
BlockingQueue blockingQueue=new LinkedBlockingDeque();
ExecutorService server =Executors.newFixedThreadPool(5);
try {
for (Integer i=0;i<200000;i++){
blockingQueue.put(new User("小猪"+i,18));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
Long start=System.currentTimeMillis();
int batch=5;
CountDownLatch count = new CountDownLatch(batch);
Runnable run = () -> {
int num = 0;
boolean flag = true;
while (flag){
try {
User user = (User) blockingQueue.take();
System.out.println(Thread.currentThread().getName()+user.toString());
if(num == blockingQueue.size())
{
flag = false;
Long end=System.currentTimeMillis();
System.out.println("运行时间为"+(end-start));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
for (int i=0;i<batch;i++){
server.execute(run);
}
server.shutdown();
2.通过不断运行ExecutorService.isTerminated()方法检测全部的线程是否都已经运行结束
public class Main
{
private static final int NTHREDS = 10;
public static void main(String[] args)
{
ExecutorService executor = Executors.newFixedThreadPool(NTHREDS);
for (int i = 0; i < NTHREDS; i++)
{
Runnable worker = new MyRunnable();
executor.execute(worker);
}
executor.shutdown();
while (!executor.isTerminated())
{
}
System.out.println("Finished all threads");
}
}
4 中断线程
public void interrupt()
中断线程。
public boolean isInterrupted()
测试线程是否已经中断。
public static boolean interrupted()
测试当前线程是否已经中断。
5创建线程池的参数
Executors.newCachedThreadPool():无限线程池。
Executors.newFixedThreadPool(nThreads):创建固定大小的线程池。
Executors.newSingleThreadExecutor():创建单个线程的线程池。
优点
重用存在的线程,减少对象创建、消亡的开销,性能佳。
可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
提供定时执行、定期执行、单线程、并发数控制等功能。
corePoolSize 为线程池的基本大小。
maximumPoolSize 为线程池最大线程大小。
keepAliveTime 和 unit 则是线程空闲后的存活时间。
workQueue 用于存放任务的阻塞队列。
handler 当队列和最大线程池都满了之后的饱和策略。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
核心线程池(corePoolSize)
线程池的大小,当核心线程池未满时,任务首先会进入此处
最大线程数(maximumPoolSize)
最大线程数的数量等于核心线程池的大小加上阻塞队列的大小
线程存活时间(keepAliveTime)
阻塞队列中的线程等待时间
时间单位(unit)
阻塞队列中的线程等待时间
阻塞队列(workQueue)
添加任务是,如果核心线程池满了,会进入阻塞队列
线程工厂(threadFactory)
可以自定义线程参数(比如线程名字)
拒绝策略(handler)
达到最大线程池数量的大小之后,再有任务添加,会执行该策略
任务进入线程池时,首先会进入核心线程池,直到达到核心线程数量,如果在有任务添加,会进入阻塞队列,当添加的任务数量达到最大线程池数量时如果有任务添加,会执行拒绝策略。
阻塞队列
ArrayBlockingQueue:由数组结构组成的有界阻塞队列
LinkedBlockingQueue:由链表结构组成的有界阻塞队列(默认值:Integer.MAX_VALUE)
PriorityBlockingQueue:支持优先级排序的无界阻塞队列
DelayQueue:使用优先级队列实现的延迟无界阻塞队列
SynchronousQueue:不存储元素的阻塞队列,即单个元素的队列
LinkedTransferQueue:有链表结构组成的无界阻塞队列
LinkedBlockingDeque:有链表结构组成的双向阻塞队列
拒绝策略
AbortPolicy(默认):直接抛出RejectedExecutionException异常阻止程序正常运行。
CallerRunsPolicy:该策略既不会抛弃任务,也不会抛出异常,而是将任务退回到调用者执行。
DiscardOldestPolicy:抛弃队列中等待最久的任务,然后将新任务加入队列。
DiscardPolicy:直接丢弃任务,不予任务处理也不抛出异常。
5.线程等以上多线程执行完,继续执行任务。
//若当期依然有子线程没有执行完毕 (不靠谱 可能出现异常情况)
while(Thread.activeCount() > 1){
Thread.yield();//使得当前线程(主线程)让出CPU时间片
}
1.闭锁CountDownLatch
闭锁是典型的等待事件发生的同步工具类,将闭锁的初始值设置为与子线程数目相同,每个子线程执行完成都调用CountDown()方法,主线程调用await()方法等待计数值变为零之后恢复继续执行。
2.线程间同步方法
假设有N个子线程,每个子线程执行完成都将一个全局的原子变量加1,待某个线程将原子变量加到N之后,则利用以下三种方法将主线程唤醒。
互斥信号量Semaphore:主线程申请占用信号量失败阻塞,由子线程释放信号量唤醒。
阻塞队列BlockingQueue:主线程尝试从空队列取东西失败阻塞,由子线程放入东西唤醒。
条件队列:主线程wait/await在内置或显式的条件队列上,由子线程notify/signal唤醒。
3.Future的get()方法
提交子线程到线程池执行的时候维护好所有子线程的Future对象,主线程中循环调用所有Future对象的get方法等待子线程执行完成并返回结果。
4.CompletionService的take()方法
提交所有子线程到ExecutorCompletionService线程池执行,主线程通过其take方法等待所有子线程执行完成并返回结果。
5.Thread的join()方法
创建Thread对象并start启动之后维护好所有子线程的Thread对象,在主线程中循环调用所有子线程这个Thread对象的join方法,等待所有子线程执行完成。
6.栅栏CyclicBarrier
栅栏是典型的等待所有线程到达同一栅栏位置的同步工具类。可以在所有子线程的结束位置设置栅栏,待所有子线程达到栅栏位置后执行指定的Runnable线程,此方法并不是严格意义的等待所有子线程执行完成后继续执行主线程,而是执行另一个线程。
7.线程池的awaitTermination()方法
ExecutorService的awaitTermination()方法定义如下:
boolean awaitTermination(long timeout,
TimeUnit unit)
hrowsInterruptedException
6.Java锁Synchronized,对象锁和类锁举例
https://www.cnblogs.com/cangqiongbingchen/p/5806757.html
Java的锁分为对象锁和类锁。
1. 当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内针对该对象的操作只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
2. 然而,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
3. 尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对该object中所有其它synchronized(this)同步代码块的访问将被阻塞。
4. 同步加锁的是对象,而不是代码。因此,如果你的类中有一个同步方法,这个方法可以被两个不同的线程同时执行,只要每个线程自己创建一个的该类的实例即可。
5. 不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法。
6. synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法。
7.对一个全局对象或者类加锁时,对该类的所有对象都起作用。
总结:synchronized作用于静态方法和非静态方法的区别:
/* * 非静态方法: * 给对象加锁(可以理解为给这个对象的内存上锁,注意 只是这块内存,其他同类对象都会有各自的内存锁),这时候 * 在其他一个以上线程中执行该对象的这个同步方法(注意:是该对象)就会产生互斥 * 静态方法: * 相当于在类上加锁(*.class 位于代码区,静态方法位于静态区域,这个类产生的对象公用这个静态方法,所以这块 * 内存,N个对象来竞争), 这时候,只要是这个类产生的对象,在调用这个静态方法时都会产生互斥
评论区