java多线程学习记录(一)

**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多线程学习记录(二)。。。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值