参考:https://blog.csdn.net/u012768459/article/details/80926112
一、进程和线程
进程:是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
比如:QQ、微信就是两个进程
线程:是操作系统能够进行运算调度的最小单位
比如:一个mian方法就是一个主线程
一个进程通常包含多个线程,比如:微信一边放歌,一边看剧。
并发和并行
你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。
并发:你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。
并行:你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。
并发的关键是你有处理多个任务的能力,不一定要同时。
并行的关键是你有同时处理多个任务的能力。
所以我认为它们最关键的点就是:是否是『同时』。
并发就是同步的串行,一个任务执行完执行下一个任务;
同步: 多个任务情况下,一个任务A执行结束,才可以执行另一个任务B。只存在一个线程。
异步: 多个任务情况下,一个任务A正在执行,同时可以执行另一个任务B。任务B不用等待任务A结束才执行。存在多条线程。
并行,在用同一个时刻执行多个线程;
二、多线程
实现多线的两种方式:继承java.lang.Thread类,实现java.lang.Runnable接口
原理:在单cpu上,cpu给多个线程分配执行时间段。
(一)、线程生命周期
- 新创建
- 就绪状态(可运行)
- 运行状态
- 等待状态
- 休眠状态
- 阻塞状态
- 死亡状态
(二)、线程休眠
Thread.sleep(毫秒数);//
try {
Thread.sleep(1000);//毫秒数
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
(三)、线程加入
如果存在一个A线程,现在需要加入B线程,并要求线程B执行完了才能再去执行A。
join(): B.join(); //在A线程的run()方法里使用
(四)、线程优先级
级别高的先执行概率大
低优先级:1~4,其中类变量Thread.MIN_PRORITY最低,数值为1;
默认优先级:如果一个线程没有指定优先级,默认优先级为5,由类变量Thread.NORM_PRORITY表示;
高优先级:6~10,类变量Thread.MAX_PRORITY最高,数值为10。
设置线程优先级
thread3.setPriority(MAX_PRIORITY);
(四)、线程礼让
yield(): 使具有至少同级别的优先级的线程有进入可执行状态的机会。
(五)、线程同步
保证线程安全性
(1)、synchronized 关键字:
1、同步块
synchronized (obj){
}
2、同步方法
public synchronized void medthod (){
}
(2)、加锁:
private Lock lock = new ReentrantLock();//获取一个锁对象
lock.lock(); //加锁
lock.unlock(); //解锁
三、实现runnable接口实现多线程
- 建立Runnable对象(实现类的实例化对象)
- 使用参数Runnable对象的构造方法创建Thread实例
- 调用start()方法启动线程
1、带synchorized的售票
public class SyncDemo implements Runnable{
private int ticket = 50;
public static void main(String[] args) {
SyncDemo demo = new SyncDemo();
Thread t1 =new Thread(demo); //必须传一个demo实例,才会有输出
t1.setName("1号窗口");
t1.start();
Thread t2 =new Thread(demo);
t2.setName("2号窗口");
t2.start();
Thread t3 =new Thread(demo);
t3.setName("3号窗口");
t3.start();
}
@Override
public void run() {
while (ticket > 0){
sellTicket();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* synchronized关键字:同步方法
* 当一个线程启动并满足条件ticket>0,继而执行同步方法,此时,其它线程不能执行该方法,
* 一直等到该线程执行完了,其他线程才能执行该同步方法
* 缺陷:while处,可能有多个线程满足条件,继而去执行sellTicket()方法,其中一个线程获取同步方法锁
* 具备执行权限,直到此线程执行完,此时,tickets可能=0,但,其他线程可能之前满足tickets>0条件,
* 只是一直等待执行同步方法权限,这时正好获取到同步方法锁,继而去执行同步方法内的代码,导致tickets=0,或=-1
*
*
*/
public synchronized void sellTicket(){ //同步代码块
System.out.println(Thread.currentThread().getName() + "卖了第" + ticket + "张票");
ticket--;
}
2、带lock的售票
package sunyard.fuza.thread.thread1;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TicketDemo {
public static void main(String[] args) {
Demo demo = new Demo();
Thread t1 = new Thread(demo,"1");
t1.setName("1号窗口:");
Thread t2 = new Thread(demo,"2");
t2.setName("2号窗口:");
Thread t3 = new Thread(demo,"3号窗口:");//第二个参数相当于给线程一个名称
//t3.setName("3号窗口:");
t1.start();
t2.start();
t3.start();
}
}
class Demo implements Runnable{
private int ticket = 50;
private Lock lock = new ReentrantLock();//获取一个锁对象
@Override
public void run() {
while(ticket > 0){
seelticket();
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
/*
* 如果程序在执行try语句时,发生异常,继而去执行finally内的解锁语句,也会导致,多个窗口售卖同一张票
*
*/
public void seelticket(){
lock.lock(); //加锁
try{
if(ticket>0){
System.out.println(Thread.currentThread().getName() + "卖了第" + ticket + "张票!");
ticket--;
}
}finally{
lock.unlock(); //解锁
}
}
}