多线程相关知识:
并行: 两个或者两个以上的事件,同一时刻发生
并发: 两个或者两个以上的事件,同一时间段发生
进程: 进入到内存中的一个程序, 好比打开一个360安全卫士
线程: 是进程的一个执行单位,负责当前进程中程序的执行,一个进程至少有一个线程,一个进程可以有多个线程。
创建线程的方法:
public class Run extends Thread{
/**
* 创建多线程方法: (一) 通过继承Thread类 然后重写方法run
* 具体操作: ①继承Threa类并 重写run() 【run()主要写你自己的应用程序】
* ②创建Thread子类的实例(也就是创建线程对象)
* ③调用线程对象的方法 start(), 多线程开始运行
*/
public void run() {
for (int i=0;i<100;i++) {
try {
sleep(1000); //这个sleep() 会报错 要抛异常
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("这个是第"+i+"个数字了");
}
}
public static void main(String[] args) {
Run run = new Run();
run.start();
for (int i=0;i<100;i++) {
System.out.println("我这个是一个线程,进行了"+i+"秒");
}
}
}
public class Run extends Thread{
/**
* 创建多线程方法: (二) 通过实现接口 Runnable 然后重写run,
* 具体操作:①创建类实现Runnable接口,重写run()
* ②创建一个Runnable接口实现类的对象
* ③创建Thread类对象 ,构造方法中传递Runnable接口中的实现类对象
* ④调用Thread对象的stra() 开始调用多线程
*/
/**
* 一个run()就相当于一个线程
*/
@Override
public void run() {
for (int i=0;i<100;i++) {
try {
sleep(1000); //这个sleep() 会报错 要抛异常
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("这个是第"+i+"个数字了");
}
}
public static void main(String[] args) {
Run run = new Run();
run.start();
for (int i=0;i<100;i++) {
System.out.println("我这个是一个线程,进行了"+i+"秒");
}
}
说到线程问题就会牵扯到线程安全问题
线程安全问题的产生:数据共享的时候就会产生线程安全。(就好比现在腾讯出来的一个在线文档编辑,多个人可以同时通过在线编辑来一起操作别人上传文件,大家都有增删改查的权力, 但是当俩个人同时去修改这个文件的时候,最后是应该按照谁修改的样子确定最终的样式呢?)
写一个最经典的一个案例: 车站卖票模式,车票的数量是固定的但是柜台是N个。N个柜台同时操作这固定的车票 是不是每一个柜台售卖出去的票数就必须立马减去,并且没张票的编码售卖出去之后就不能二次销售了。 没一张票都是独立的,下面我们就讲解下这个精典案例
解决方案:解决方案有三种:
第一种方式: 实现用同步代码块方法 解决安全问题
//首先想一下售卖的票数是不是固定的假如就是100张,每一张票的编号就是1 , 2 以此类推到100 每次一
//个柜台售卖掉一张门票就是少了一个编码,所以我们就可以把票作为定量
private int tick = 100;
public void run() {
while(true){
//写一个同步代码块
synchronized (object){
try {
Thread.sleep(100);
//卖票操作
System.out.println(Thread.currentThread().getName()+"正在卖第"+tick+"张票");
tick--;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
第一种方式: 实现用同步代码块方法 解决安全问题**
//第二种方式 同步方法 使用一个方法在修饰符前面加上一个synchronized 然后把可能出问题的代码放到方法里面 然后调用方法
try {
if (tick>0){
Thread.sleep(1000);
//卖票操作
System.out.println(Thread.currentThread().getName()+"正在卖第"+tick+"张票");
tick--;
}else
{
System.out.println("结束");
System.exit(3);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
第三种方式: Lock锁**
/**
* 第三种方法: Lock锁
* 成员变量位置先实例化一个锁接口 Lock借口
* 然后在 出问题的前面加上一个锁 , 之后在结束的时候 解锁
*/
Lock l = new ReentrantLock(); //定义一个锁
while (true){
l.lock();
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"售卖第"+tick+"张");
tick--;
l.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
以上的三种方式必须是通过接口的方式去解决的, 不可以通过继承方式去使用这三个方法
线程状态(面试的时候会经常提):
①New 线程刚被创建,未启动
②Runnable 可运行Jvm里的状态(可能启动,取决于程序)
③Blocked 阻塞
④Wait 无限等待,一个线程在等另外一个线程唤醒
⑤Timedwaiting 计时状态
⑥Teminated 被终止,因为run()结束,正常退出而死亡