**1、**串行,并行,并发,分布式:
串行:A和B两个任务运行在一个CPU线程上,在A任务执行完之前不可以执行B。即,在整个程序的运行过程中,仅存在一个运行上下文,即一个调用栈一个堆。程序会按顺序执行每个指令。
并行:并行性指两个或两个以上事件或活动在同一时刻发生。在多道程序环境下,并行性使多个程序同一时刻可在不同CPU上同时执行。比如,A和B两个任务可以同时运行在不同的CPU线程上,效率较高,但受限于CPU线程数,如果任务数量超过了CPU线程数,那么每个线程上的任务仍然是顺序执行的。
并发:并发指多个线程在宏观(相对于较长的时间区间而言)上表现为同时执行,而实际上是轮流穿插着执行,并发的实质是一个物理CPU在若干道程序之间多路复用,其目的是提高有限物理资源的运行效率。 并发与并行串行并不是互斥的概念,如果是在一个CPU线程上启用并发,那么自然就还是串行的,而如果在多个线程上启用并发,那么程序的执行就可以是既并发又并行的。
分布式:分布式在并行处理的基础上,强调任务正在执行的物理设备,如处理器、内存等等硬件,在物理上是分开的。而并行计算是指在一台计算机上的计算,在物理上不分开。
2、线程基础:
进程与线程的区别:进程是系统分配和管理资源的基本单位,线程是进程的一个执行单元,是进程内调度的实体,CPU调度和分派的基本单位,线程也被称为轻量级的进程。
线程的状态和相互转换:
线程的创建方式:(常选择实现接口,因为java只允许单继承但可以实现多个接口)
1、继承Thread类创建线程
2、实现Runnable接口创建线程
3、使用Callable和Future创建线程
4、使用线程池例如Executor
详见博客:线程创建的四种方式
还可以用匿名内部类或lambda写:
public class Lambda{
new Thread (()->{
System.out.println(Thread.currentThread().getName());
}).start();
}
线程的挂起与恢复:
什么是挂起线程?
实际上就是将线程进入“非可执行”状态下,在这个状态下CPU不会分给线程时间片,进入这种状态可以暂停一个线程的运行,在线程挂起之后可以通过唤醒线程使之恢复运行。
为什么要挂起线程?
CPU分配的时间片非常短和珍贵,避免浪费资源。
如何挂起线程?
被废弃的方法:thread.suspend() 该方法不会释放线程所占用的资源,此方法可能会使其他等待资源的线程死锁。
thread.resume()不能独立于suspend方法存在。
可使用的方法:
wait()暂停执行,放弃以获得的锁、进入等待状态
notify() 随机唤醒一个等待锁的线程
notifyAll()唤醒所有在等待的线程,自行抢占CPU资源。
什么时候使用挂起线程?
我等的船还不来,我等的人还不明白(等待某些未就绪的资源)
线程的中断:
1、stop()废弃的方法,开发中不要使用。一调用线程就立即停止,有可能引发线程安全问题。
2、Tread.interrupt方法
3、自定义一个标志位,用来判断是否继续执行。
线程的优先级:
尽可能的优先运行优先级高的线程,这并不表示优先级低的线程不会运行。
线程优先级设置可以为1-10的任意数字,Thread类中定义了三个优先级:
MIN_PRIORITY(1)、NORM_PRIORITY(5)、MAX_PRIORITY(10),一般推荐使用这几个常量,不要自行设置数值。
3、死锁
指多个进程因为竞争共享资源而造成的一种僵局,若无外力作用这些进程将不能向前推进。
产生死锁的原因:(1)竞争系统资源 (2)进程的推进顺序不当
产生死锁的必要条件:
互斥条件:进程要求对所分配的资源进行排他性控制,即在某段时间内某资源仅被一进程所占用。
请求和保持:当进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源在未使用完之前不能剥夺,只能在使用完之后自己释放。
环路等待条件:发生死锁时必然存在一个进程一资源的环行链。
预防死锁:
资源一次性分配、可剥夺资源、资源有序分配。
避免死锁:
预防死锁的几种策略,会严重地损害系统性能。因此在避免死锁时,要施加较弱的限制,从而获得 较满意的系统性能。由于在避免死锁的策略中,允许进程动态地申请资源。因而,系统在进行资源分配之前预先计算资源分配的安全性。若此次分配不会导致系统进入不安全状态,则将资源分配给进程;否则,进程等待。其中最具有代表性的避免死锁算法是银行家算法。
检测死锁
1、首先为每个进程每个资源指定唯一的号码。
2、然后建立资源分配表和进程等待表。
使用Jstack命令或Jconsole工具。
线程安全问题:
什么是线程安全?
当多个线程访问某个类,不管运行时环境采用何种调度方式或者这些线程如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那就称这个类为线程安全的-------------------《并发编程实战》
原因1、两个或两个以上的线程同时访问临界资源;
2、多线程操作共享资源代码有多个语句,即不是原子操作。
volatile关键字:
参照博客:https://www.cnblogs.com/dolphin0520/p/3920373.html
线程安全问题解决方案
1、同步代码块
格式:synchronize(锁对象){
需要被同步的代码
}
同步代码块需要注意的事项:
1.锁对象可以是任意的一个对象;
2.一个线程在同步代码块中sleep了,并不会释放锁对象;
3.如果不存在线程安全问题,千万不要使用同步代码块;
4.锁对象必须是多线程共享的一个资源,否则锁不住。
例子:三个售票窗口:
class SaleTicket extends Thread{
static int num = 50;//票数 非静态的成员变量,非静态的成员变量数据是在每个对象中都会维护一份数据的。
public SaleTicket(String name) {
super(name);
}
@Override
public void run() {
while(true){
//同步代码块
synchronized ("锁") {
if(num>0){
System.out.println(Thread.currentThread().getName()+"售出了第"+num+"号票");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
num--;
}else{
System.out.println("售罄了..");
break;
}
}
}
}
}
public class Demo4 {
public static void main(String[] args) {
//创建三个线程对象,模拟三个窗口
SaleTicket thread1 = new SaleTicket("窗口1");
SaleTicket thread2 = new SaleTicket("窗口2");
SaleTicket thread3 = new SaleTicket("窗口3");
//开启线程售票
thread1.start();
thread2.start();
thread3.start();
}
}
方式二:同步函数(就是使用synchronized修饰的一个函数)
注意事项: 1.如果函数是一个非静态的同步函数,那么锁对象是this对象;
2.如果函数是静态的同步函数,那么锁对象是当前函数所属的类的字节码文件(class对象);
3.同步函数的锁对象是固定的,不能由自己指定。
class BankThread extends Thread{
static int count = 5000;
public BankThread(String name){
super(name);
}
@Override //
public synchronized void run() {
while(true){
synchronized ("锁") {
if(count>0){
System.out.println(Thread.currentThread().getName()+"取走了1000块,还剩余"+(count-1000)+"元");
count= count - 1000;
}else{
System.out.println("取光了...");
break;
}
}
}
}
public class Demo1 {
public static void main(String[] args) {
//创建两个线程对象
BankThread thread1 = new BankThread("老公");
BankThread thread2 = new BankThread("老婆");
//调用start方法开启线程取钱
thread1.start();
thread2.start();
}
}
单例模式与线程安全:
单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点,节省系统资源避免在每个调用的地方都新建不同对象。
详细参见:https://www.runoob.com/design-pattern/singleton-pattern.html
饿汉式:-----本身线程安全,无论之后是否用到在类加载的时候就已经进行实例化,如果类比较占内存之后又没用到就造成资源的浪费。
懒汉式:在需要的时候再实例化。最简单的写法是非线程安全的。
懒汉式怎么实现线程安全?
同步函数:(缺点效率很低)
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
双检锁/双重校验锁(DCL,即 double-checked locking):
public class lazySingleton{
private static volatile lazySingleton lazysingleton = null;
private Lazysingleton(){
}
public static LazySingleton getInstance(){
//判断实例是否为空,为空则实例化
if(lazySingleton == null){
Thread.sleep(1000);
synchronized(LazySingleton.class){
if(lazySingleton==null)
lazySingleton = new LazySingleton();
}
}
//否则直接返回
return lazySingleton;
}
public static void main(String[] args){
for(int i=0;i<10;i++){
new Thread(()->{
System.out.println(LazySingleton.getInstance());
}).start();
}
}
}
运行结果:保证了只有一个对象被创建,面试时经常问懒汉式的线程安全问题。
线程安全性问题成因:
1、多线程环境
2、多个线程操作同一共享资源
3、对共享资源进行了非原子性操作
避免:打破成因中任意一点。
未完,见java多线程学习记录(二)。。。。。。