多线程
一、线程概述
进程:是一个正在执行的程序,每一个进程都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元;
线程*:就是进程中的一个独立的控制单元,线程在控制进程的执行。
一个进程至少有一个线程;
二、创建自定义线程
1、如何在自定义代码中自定义一个线程呢?
(1)、创建线程的第一种方法:继承Thread类
步骤:
① 、定义类继承Thread类:classDemo extends Thread{}
② 、复写Thread类中的run方法:public void run(){} 目的:将自定义的 代码存储在run方法,让线程运行。
③ 、调用线程的start方法:Demod=new Demo(); d.start();作用:启动线程、调用Run方法。
多线程的一个特性:随机性。
(2)、创建线程的第二种方式:实现Runnable接口
步骤:
① 定义类实现Runnable接口:class Demoimplements Runnable{}
② 覆盖Runnable接口中的run方法:publicvoid run( ){ }
③ ‘建立子类对象:Demo t=new Demo( );
④ 通过Thread类建立线程对象,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数:Thread t1=new Thread(t); Thread t2=new Thread(t);…….
为什么将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数?
因为自定义的Run方法所属的对象是Runnable接口的子类对象。所以要让线程去运行指定对象的run方法,就必须明确该run方法所属对象。
⑤ 调用Thread类的start方法开启线程并调用Runnable接口子类的run方法:t1.start(); t2.start();…..
2、两种多线程方式(实现方式和继承方式)的区别?
实现Runnable方式:线程代码存在接口的子类的run方法中;好处:避免了单继承的局限性。开发建议使用实现方式。
继承Thread方式:线程代码存放Thread子类的run方法中。
三、练习创建两个线程与主线程交替运行(继承方式)
class Test extends Thread
{
Test(String name)
{
Super(name);
}
public void run()
{
for(int x=0;x<10;x++)
System.out.println(Thread.currentThread().getName()+“run=”+x);
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Test t1=new Test(“one”);
Test t2=new Test(“two”);
T1.start();
T2.start();
for(int x=0;x<10;x++)
{
System.out.println(“main=”+x);
}
}
}
线程都有自己默认的名称:Thread-编号从0开始。
Thread.currentThread().getName()是标准通用调用方法,此处可由this代替。
其中Thread.currentThread()是获取当前线程对象、getName()是获取线程名称。
四、简单的卖票程序,多个窗口同时卖票(实现方式),以及多线程的安全问题
class Test implements Runnable
{
Test (String name)
{
super(name);
}
private int num=100;
public void run()
{
while(true)
{
synchronized(obj)
{
if(num>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+”sale--”+num--);
}
}
}
}
}
class TicketDemo
{
public static void main(String[] args)
{
Test t=new Test();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t21.start();
t3.start();
t4.start();
}
}
1、 通过分析发现,在没有设置synchronized()时,或打印出0,-1,-2等错票
多线程的运行出现的安全问题。
问题的原因:
当多条语句在操作同一线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来,导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与进来。
2、 Java对于多线程的安全问题提供了专业的解决方式,就是:同步代码块
同步代码块格式:synchronized(对象){需要被同步的代码}
如本例,obj对象就如同锁,持有锁的线程可以在同步中执行,没有持有锁的线程即使获得cpu的执行权也进不来,因为没有锁。(火车上厕所的例子)
同步的前提:1、必须要有两个或两个以上的线程;2、必须是多个线程使用同一个锁。
必须保证同步中只能有一个线程在运行。
好处:解决了多线程的安全问题
弊端:多线程需要判断锁,较为消耗资源。
五、同步函数的创建使用
例题:需求:银行有一个金库,有两个储户分别存300元分三次存每次100元
class Cus implements Runnable
{
private Bank b=new Bank();
public viod run()
{
for(int x;x<3;x++)
b.add(100);
}
}
class Bank
{
private int sum;
public synchronized void add(int n) //把同步代码块放在函数上,实现同步函数
{
sum=sum+n;
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+”sum=”+sum);
}
}
class BankDemo
{
public static void main(String[] args)
{
Cus c=new Cus();
Thread c1=new Thread(c);
Thread c2=new Thread(c);
c1.start();
c2.start();
}
}
1、 目的:改程序是否有安全问题,如果有如何解决?
如何找到问题?
1、 明确哪些代码是多线程代码
2、 明确共享数据
3、 明确多线程代码中哪些语句是操作共享数据的
2、 同步函数用的是哪一个锁?
函数需要被对象调用,那么函数有一个所属对象引用,就是this。
3、 同步函数被静态static修饰后,同步函数的锁是什么?
静态进内存时,内存中没有本类对象,但是一定有该类对应字节码文件对象
类名.class 该对象的类型是class
因此,静态同步方法时,使用的锁是该方法所在类的字节码文件对象类名.class
class Ticket implements Thread
{
private static int tick=100;
boolean flag=true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(Ticket.class)//同步函数被静态修饰后,传入的对象是类名.class
{
if(tick>0)
{
try{Thread,.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+” coad:”+tick--);
}
}
}
}
else
while(true)
show();
}
public static synchronized void show()
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+”show:”+tick--);
}
}
}
class StaticTicketDemo
{
public static void main(String[] args)
{
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
try{Thread,sleep(10);}catch(Exception e){}
t.flag=false;
t2.start();
}
}
六、单例设计两种模式中懒汉式在同步代码快中的应用
前瞻:
饿汉式:class Single
{
private static Single s=new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}
*懒汉式:
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 Test implements Runnable
{
private Boolean flag;
Test(Boolean flag)
{
this.flag=flag;
}
public void run()
{
if(flag)
{
while(true)
{
synchronized(MyLoka)
{
System.out.println(“if locka”);
synchronized(MyLockb)
System.out.println(“if lockb”);
}
}
}
else
{
while(true)
{
synchronized(MyLockb)
{
System.out.println(“else lockb”);
synronized(MyLocka)
System.out.println(“else locka”);
}
}
}
}
}
class MyLock
{
static Object locka=new Object();
static Object lockb=new Object();
}
class DeadLock
{
public stativ void main(String[] args)
{
Thread t1=new Thread(new Test(true));
Thread t2=new Thread(new Test(flase));
t1.start();
t2.start();
}
}