创建线程:
通过对java.lang包的查找,继承Thread类可以创建线程
1,建立类继承Thread类
2,复写Thread中的ran方法。
3,调用线程的start()方法,该方法的作用是,启动线程,调用ran方法。
public class Test {
public static void main(String args[])
{
Demo a= new Demo();
a.start();
for(int x=0;x<=100;x++)
System.out.println("hello word");
}
}
class Demo extends Thread
{
public void ran()
{
for(int x=0;x<=100;x++)
System.out.println("Demo ran");
}
}
两个输出会交替出现。
发现运行结果每次都不同
因为多个线程都在获取cpu的使用权,cpu执行到谁,谁就运行,在某一时刻,只能有一个程序在运行(多核除外)cpu在做着快速切换,已达到看上去是在同时运行。
多线程特性:随机性,谁抢到谁执行,至于执行多长时间,cpu说的算。
为什么要覆盖ran方法
thread类用于描述线程。
该类定义了用于存储要运行的代码,这些代码就存储在ran方法中。
ran() 和 start() 区别
只写ran()线程没有被创建,编程了单线程程序。会顺序执行,输出不是随机的。
线程运行过程中的状态:
线程对象的名称:
每个线程都有默认名称。
Thread-编号 //该编号从0开始
线程名称可以提供 线程.getName() 方法获得
currentThread() :获取当前运行的线程对象
getName():获取线程名称
买票程序举例:
public class Test
{
public static void main(String args [])
{
Ticket t1 = new Ticket();
Ticket t2 = new Ticket();
Ticket t3 = new Ticket();
Ticket t4 = new Ticket();
Ticket t5 = new Ticket();
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
class Ticket extends Thread
{
private static int tick=100; //静态,所有对象公用一个票数。
public void run()
{
if(tick>0)
System.out.println(currentThread().getName()+"sale:"+tick--);
}
}
第二种创建进程的方法
实现runable接口
步骤:
1,定义类,实现runnable接口
2,覆盖runnable中的run()方法
3,通过Thread;类建立线程对象
4,将Runnable接口对象作为实际参数传递给Thread类的构造函数
5,调用Thread类的start方法开启线程并调用Runnable子类中的run方法
实现方式和前一种方法的区别:
实现方式好处在于:避免了单继承的局限性。
在定义线程时,建议使用实现方式。
继承Thread类:线程代码存放在Thread子类对象run方法中
实现Runnable:线程代码存放在接口的子类对象的run中
在此例中ticket也不用static了,因为只建立了一个对象
睡眠:
try
{Thread.sleep(10);}
catch(Exception e)
{
}
public class Test
{
public static void main(String args [])
{
Ticket t = new Ticket();
Thread t1 =new Thread(t);
Thread t2 =new Thread(t);
Thread t3 =new Thread(t);
Thread t4 =new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Ticket implements Runnable
{
private int tick = 100;
public void run()
{
while(true)
if(tick>0)
{
System.out.println("sale"+tick--);
}
}
}
安全问题:
以下代码会打印 0,-1,-2号票
产生问题原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,另一个线程1参与进来执行,导致共享数据出现错误。
解决办法:对多条操作共享数据的语句,只能让让一个线程都执行完,在执行过程中,其他线程不可以参与执行。
java对于多线程的安全问题提供了专业的解决方式,就是同步代码块。会保证该代码块内的代码被全部执行再切换线程。
格式:
synchronized(对象)
{
需要被同步的代码
}
public class Test
{
public static void main(String args [])
{
Ticket t = new Ticket();
Thread t1 =new Thread(t);
Thread t2 =new Thread(t);
Thread t3 =new Thread(t);
Thread t4 =new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Ticket implements Runnable
{
private int tick = 100;
public void run()
{
while(true)
if(tick>0)
{
try
{Thread.sleep(10);}
catch(Exception e)
{
}
System.out.println("sale"+tick--);
}
}
}
修改安全问题后的代码
public class Test
{
public static void main(String args [])
{
Ticket t = new Ticket();
Thread t1 =new Thread(t);
Thread t2 =new Thread(t);
Thread t3 =new Thread(t);
Thread t4 =new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Ticket implements Runnable
{
Object obj= new Object();
private int tick = 100;
public void run()
{
while(true)
{
synchronized(obj)
{
if(tick>0)
{
try
{Thread.sleep(10);}
catch(Exception e)
{
}
System.out.println("sale"+tick--);
}
}
}
}
}
如何找到安全问题/同步的前提:
1,明确哪些代码是多线程运行的代码。
2,明确共享数据。
3,明确多线程代码中哪些语句是操作共享数据的。
同步函数:
同步有两种,1代码块,2同步函数
用synchronized修饰函数即可。
同步代码块使用的锁时obj
同步函数的锁:
同步函数使用的所时this 也就是调用该函数的实体。
如果同步函数被静态修饰后,使用的锁是该类对应的class,就是类名点class 。 Ticket.class
懒汉式
死锁:
两个锁:
同步中嵌套同步,锁却不同,恰好停止在某位置,会造成死锁。
要避免死锁。
public class Test
{
public static void main(String args [])
{
Thread t1 = new Thread(new Deadlock(true));
Thread t2 = new Thread(new Deadlock(false));
t1.start();
t2.start();
}
}
class Deadlock implements Runnable
{
private boolean flag;
Deadlock(boolean flag)
{
this.flag = flag;
}
public void run()
{
if(flag)
{
while(true)
{
synchronized(Mylock.locka)
{
System.out.println("if lock a");
synchronized(Mylock.lockb)
{
System.out.println("if lockb");
}
}
}
}
else
{
while(true)
{
synchronized(Mylock.lockb)
{
System.out.println("else lockb");
synchronized(Mylock.locka)
{
System.out.println("else locka");
}
}
}
}
}
}
class Mylock
{
static Object locka = new Object();
static Object lockb = new Object();
}