1、 多线程中的概念:
a. 进程:正在进行中的程序(直译),其实就是该应用程序在内存中分配的空间。
b. 线程:是在进程中负责程序执行的路径。负责程序执行的。
在进程中至少有一个线程在执行。
2、什么是多线程?
当有多部分代码需要同时运行时,就需要开辟多条执行路径来完成。这时该程序就是多线程程序。
多线程解决了:让多部门门代码同时运行的问题
3、JVM中的多线程了解
jvm中也一样是多线程程序,只要有一个程序负责程序执行,又有一个线程负责着垃圾回收,这个就是同时进行的。
结论:每一个线程都有自己的运行代码,这个称之为线程的任务。
对于每一个线程便于识别都有自己的名称,比如负责主函数执行程序代码的线程,称之为 主线程,main thread。
主线程运行的代码都定义在主函数中。
负责回收垃圾的线程:垃圾回收线程。
发现运行结果不一致:多线程的随机性构成的。因为cpu的快速切换的原因。
4、 调用run和调用start的区别?
class Demo extends Thread
{
private String name;
Demo(String name)
{
this.name = name;
}
public void run()
{
for(int x=0; x<10; x++)
{
System.out.println("name...."+x+"....."+name);
}
}
}
class ThreadDemo
{
public static void main(String[] args)
{
//创建线程对象。
Demo d1 = new Demo("小强");
Demo d2 = new Demo("旺财");
//开启线程。让线程运行起来。
d1.start();
d2.start();
d1.run();
d2.run();
}
}
区别:start():开启线程的同时,调用run()方法!run():只是在调用run方法,没有开启线程!
5、第一种创建线程的方法:继承Thread类,并复写run方法。
每一个线程都应该有自己的任务,而且任务都会定义在指定的位置上。
主线程的任务都定义在main方法中。
自定义线程的任务都定义在了run方法中。
Thread t = new Thread();
t.start();//这种开启只能调用Thread类中自己的run方法。而该run方法中并未定义自定义的内容。
我还需要创建线程,还要让线程执行自定义的任务。
所以可以复写run方法。前提必须是继承Thread类。
而继承了Thread后,该子类对象就是线程对象。
6、第二种创建线程的方法:
1.实现Runnable接口。
2.覆盖run方法。
3.通过Thread类创建线程对象。
4.将Runnable接口的子类对象作为实参传递给Thread类中的构造函数。
5.调用start方法开启线程,并运行Runnable接口子类的run方法。
第二种实现Runnable接口创建线程的思想:
将线程任务和线程对象进行解耦,将线程任务单独封装成对象。另,实现Runnable接口可以避免单继承的局限性。
所以建议创建多线程,都是用实现Runnable接口方法。
任务对象实现规则,线程对象在使用规则。
7、 线程安全问题:
原因: 1.多线程在同时处理共享数据;
2.线程任务中有多条代码在操作共享数据。
安全问题成因就是:一个线程在通过多条操作共享数据的过程中,其他线程参与了共享数据的操作,导致了数据的错误。
想要知道你的多线程程序有没有安全问题:只要看线程任务中是否有多条代码在处理共享数据。
解决:一个线程在通过多条语句操作共享数据的过程中,不允许其他线程参与运算。
Java中提供了同步代码块进行引起安全问题的代码封装。(同步)
8、 同步:
格式:
synchronized(对象)
{
//需要被同步的代码;
}
同步:
好处:解决了多线程的安全问题;
弊端:降低了多线程的效率。
同步的前提:
1.至少有两个线程在同步中;
2.必须保证同步使用的是一个锁;
9、 同步的第二种表现形式:
同步函数。
问题:同步函数使用的锁是神什么?this
10、 同步函数和同步代码块的区别?
同步函数使用的固定锁this
同步代码块使用的锁是可以指定的
11、 静态同步函数锁是什么?
就是所在类的 类名.class 字节码文件对象。
12、 单例设计模式:懒汉式中加双重判断既提高了效率,有增加了安全。
13、 同步的另一个弊端:
容易引发死锁。
开发时尽量避免同步嵌套的情况。
死锁演示:
/*
写一个多线程嵌套的死锁演示,死锁就是当两个锁同时开启后,相互争夺锁对象而导致的;
*/
class Demo implements Runnable
{
private boolean flag;
Demo(boolean flag)
{
this.flag=flag;
}
public void run()
{
while(true)
{
if(flag)
{
synchronized(MyLock.LOCKA)//LOCKA锁
{
System.out.println("if locka");
synchronized(MyLock.LOCKB)//LOCKA锁中嵌套的LOCKB锁
{
System.out.println("if lockb");
}
}
}
else
{
synchronized(MyLock.LOCKB)//LOCKB锁
{
System.out.println("else lockb");
synchronized(MyLock.LOCKA)//LOCKB锁中嵌套的LOCKA锁
{
System.out.println("else locka");
}
}
}
}
}
}
class MyLock//定义两个锁对象
{
public static final Object LOCKA=new Object();
public static final Object LOCKB=new Object();
}
class DeadLockDemo
{
public static void main(String[] args)
{
/*
创建两个线程任务,开启两个线程
*/
Demo d1=new Demo(true);
Demo d2=new Demo(false);
Thread t1=new Thread(d1);
Thread t2=new Thread(d2);
t1.start();
t2.start();
}
}
14、 多线程间通信:多个线程处理同一资源,但是处理动作却不同。
wait(); 可以让当前线程处于等待,这时的线程被临时存储到了线程池中 ;
notify();唤醒线程池中任意一个等待的线程。
notifyAll();唤醒线程池中所有的等待线程。
这些方法在使用时,必须定义在同步中,必须被所属同步的锁调用。
15、 单生产者、单消费者:等待唤醒机制。
16、 多生产者,多消费者:
17、 sleep和wait的区别?
sleep必须指定时间,wait可以指定可以不指定。
sleep和wait都可以让线程处于冻结状态,释放执行权
持有锁的线程执行sleep,,不释放锁,持有锁的线程执行到wait释放锁。
sleep到时间会自动醒,wait没有指定时间,只能被其他线程通过notify唤醒。
------------------------------------------------------------------------
Lock lock=new ReentrantLock();创建互斥锁
await();等待
signal();唤醒
signalAll();唤醒所有