我们在Java的 使用中会遇到很多多线程使用的场所,现在我就简单介绍一下相关的学习与 面试。
多线程是指在一个进程中至少有两个或两个以上的线程,它们每个都可以独立的控制单元,它们控制着进程的 执行。在程序中有两种创建线程的方式:
一种方法是将类声明为 Thread
的子类。该子类应重写 Thread
类的 run
方法。另一种方法是声明实现Runnable
接口的类,该类然后实现run
方法。然后可以分配该类的实例,在创建 Thread
时作为一个参数来传递并启动。
继承方式和实现方式有什么区别呢?
继承Thread类:线程代码 存放在Thread子类的run方法中,实现Runnable接口:线程代码存放在接口的子类的run方法中。一般我们使用Runnable实现方式,因为它避免了单继承的局限性。
多线程的使用同时带来了安全问题,因为当多条语句在操作同一个线程共享数据时,一个线程多多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,会导致共享数据的错误。解决的办法:对多条操作共享数据的语句,只能让一个线程执行完,在执行过程中,其他线程不可以参与执行。Java对于多线程的安全提供了专业的解决方案,就是同步代码块。如下:
synchronized(对象)
{
//需要被同步的代码块
}
对象如同锁,持有锁的 线程可以在同步中执行,没有持有锁的线程即使获取了cpu的执行权,也进不去,因为没有获取锁。
同步的前提:1.必须要有两个或两个以上的线程; 2. 必须是多个线程使用同一个锁。必须保证同步 中只有一个线程在运行。
下面举例说明,我们都知道 火车票 卖票系统,卖票点共用同一个系统 ,他们的 资源是相同的,但是是多个窗口同时售票,所以 我们可以通过以下代码模拟实现:
class ThreadDemo4
{
public static void main(String[] args)
{
Ticket1 t=new Ticket1();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
//Thread t3=new Thread(t);
//Thread t4=new Thread(t);
t1.start();
try{Thread.sleep(10);} catch(Exception e){}
t.flag=false;
t2.start();
//t3.start();
//t4.start();
}
}
class Ticket1 implements Runnable
{
private int time=100;
//Object obj=new Object();
boolean flag=true;
public void run()
{
if (flag)
{
while(true)
{
synchronized(this) //synchronized(obj) synchronized(Ticket1.class)
{
if (time>0)
{
try {Thread.sleep(10);} catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"...sale...:"+time--);
}
}
}
}
else
{
while(true)
show();
}
}
public synchronized void show() //public static synchronized void show()
{
if (time>0)
{
try {Thread.sleep(10);} catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"...show...."+time--);
}
}
}
此段代码因为加入了同步代码块,所以在输出的过程中不会输出票为0或者负数的情况,假如将同步代码块,移除的话,此种情况就有可能发生。
同时除了同步代码块,还有同步函数以及静态的同步方法。同步函数使用的锁是this,静态的同步方法使用的锁是该方法所属类的字节码文件对象,即类名.class。下面举例说明:
class ThreadDemo5
{
public static void main(String[] args)
{
Ticket2 t=new Ticket2();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
//Thread t3=new Thread(t);
//Thread t4=new Thread(t);
t1.start();
try{Thread.sleep(10);} catch(Exception e){}
t.flag=false;
t2.start();
//t3.start();
//t4.start();
}
}
class Ticket2 implements Runnable
{
private static int time=100;
//Object obj=new Object();
static boolean flag=true;
public void run()
{
if (flag)
{
while(true)
{
synchronized(Ticket2.class) //synchronized(this) //synchronized(obj)
{
if (time>0)
{
try {Thread.sleep(10);} catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"...sale...:"+time--);
}
}
}
}
else
{
while(true)
show();
}
}
public static synchronized void show() //public synchronized void show()
{
if (time>0)
{
try {Thread.sleep(10);} catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"...show...."+time--);
}
}
}
大家注意到上述使用了同步函数以及静态的同步方法,且静态的同步方法使用到的synchronized包含的对象是该方法run所属类的字节码文件
Ticket2.class。
多线程学习中还有一个关键问题,就是死锁的问题。大家一定要学会处理。