------
Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
出现如下安全问题
同步的前提:
Q:当出现多线程访问时应该怎么解决
饿汉式没有安全问题,代码如下
六、死锁
多线程
一、多线程概述
进程:
是正在执行的程序。
是应用程序运行时的内存分配空间。
线程:
进程中的一个程序执行控制单元。
线程负责应用程序的执行顺序。
一个进程至少有一个进程。
多线程:
当一个进程中出现多个线程时,就称这个应用程序是多线程应用程序。
二、创建线程的方式
创建线程的第一种方式,继承Thread类
步骤:1、定义类继承Thread
2、复写Thread类中run方法//将自定义代码存放在run中让线程运行
3、调用线程start方法//开启线程并执行该线程的run方法
该方法的两个作用:启动线程,调用run方法
run方法仅仅是对象调用方法,而线程创建了并没有运行
发现每一次的运行结果都不同,为什么
因为多个线程都获取cpu的执行权。cpu执行到谁谁就执行
明确一点,在某一刻时,只能有一个程序在运行(多核除外)
cpu在做着高速的切换以达到看上去同时运行的效果。
为什么要覆盖run方法呢?
Thread类用于描述线程
该类就定义了一个功能,用于储备线程要运行的代码。该存储功能就是run方法
也就是说Thread类中的run方法,用于储存线程要运行的代码。
d.star();//开启线程,并执行该线程的run方法
d.run();//仅仅是对象调用方法,而线程创建了并没有运行
线程都有自己默认的名称
Thread-编号 //该编号从0开始
static Thread currentThread():获取当前线程对象
getName():获取线程名称
setName():设置线程名称
创建线程的第二种方式:实现Runnable接口(常用方式)
步骤:1、定义类实现Runnable接口
2、覆盖Runnable接口中的run方法//将线程要运行的代码存放在该run方法中
3、通过Thread类建立线程对象
4、将Runnable接口的子类对象作为实际参数传递给Thread类构造函数
5、调用Thread类的star方法开启线程并调用Runnable接口子类的run方法。
为什么要将Runnable接口的子类对象传递给Thread的构造函数?
因为自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程去指定,指定对象的run方法,就必须明确该方法所属对象。
实现方法和继承方法的不同:
实现方式的好处:避免了单继承的局限性,在定义线程时,建议使用实现方式。
继承Thread:线程代码存放在Thread子类run方法中
实现Runnable:线程代码存放在接口子类run方法中
三、线程的运行状态
四、多线程的安全问题
class Ticket implements Runnable//定义类,实现Runnable接口
{
private int tick = 1000;
public void run()//覆写run方法
{
while(true)
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
}
}
}
}
class TicketDemo2
{
public static void main(String[] args)
{
Ticket t = new Ticket();//通过Thread建立线程对象
Thread t1 = new Thread(t);//将Runnable接口的子类对象作为实参传递给Thread类构造函数
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();//调用star方法
t2.start();
t3.start();
t4.start();
}
}
问题的原因:
当多条语句当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,
另一个线程参与进来执行。导致共享数据的错误。
另一个线程参与进来执行。导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
Java对于多线程的安全问题提供了专业的解决方式。
就是同步代码块。
synchronized(对象)
{
需要被同步的代码
}
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
Java对于多线程的安全问题提供了专业的解决方式。
就是同步代码块。
synchronized(对象)
{
需要被同步的代码
}
同步的前提:
1、必须要有两个或者两个以上的线程
2、必须要多个线程使用同一个锁
必须保证同步中只有一个锁在运行。
同步的好处:解决了多线程的安全问题。
同步的弊端:多个线程需要判断锁,较为消耗资源。
同步函数用的锁是
this
如果同步函数被static修饰,这时的同步函数用的是什么锁?
静态进入内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
静态的同步方法使用的锁是该方法所在类的字节码文件对象。类名.class
五、多线程中的单例设计模式
考点问题:
Q:写一个延时加载的单例模式
A:懒汉式
Q:饿汉式和懒汉式的区别
A:懒汉式是一种延迟加载的方式
Q:当出现多线程访问时应该怎么解决
A:加同步解决安全问题
Q:效率高么
A:不高
Q:怎么解决?
A:通过双重判断的形式解决
懒汉式代码如下
class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance()
{
if(s==null)
{
synchronized(Single.class)//锁是字节码文件对象
{
if(s==null)
s = new Single();
}
}
return s;
}
}
饿汉式没有安全问题,代码如下
class Single
{
private static final Single s = new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}
六、死锁
死锁就是同步中嵌套同步,而锁却不同。导致程序卡住不能继续执行。
代码示例:
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag = flag;
}
public void run()
{
if(flag)
{
while(true)
{
synchronized(MyLock.locka)//a锁
{
System.out.println("if locka");
synchronized(MyLock.lockb)//b锁
{
System.out.println("if lockb");
}
}
}
}
else
{
while(true)
{
synchronized(MyLock.lockb)//b锁
{
System.out.println("else lockb");
synchronized(MyLock.locka)//a锁
{
System.out.println("else locka");
}
}
}
}
}
}
class MyLock//创建两个不同的锁
{
static Object locka=new Object();
static Object lockb=new Object();
}
class DeadLock
{
public static void main(String[] args)
{
//创建并启动两个进程
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}
运行结果: