如何在自定义代码中定义一个线程?
通过对API查找,创建一个线程方法一是继承自thread类。
具体步骤:
1、继承thread类
2、复写run ()方法
3、调用start()方法,此方法有两个作用,具体如下 :
每次运行结果都不同因为每个线程都在获取CPU使用权,单核情况下在某一时刻只 有一个线程在运行。谁抢到谁执行,所以结果是随机的。
为什么覆盖run方法?
thread类用于描述线程,该类定义了一个功能用于存储线程要运行的代码。该存储功能就是run()方法。
即run()方法用于存储线程中要运行的代码。
线程的几种状态:
常用方法:
static Thread currentThread();返回当前执行线程对象。
getName() 返回线程名称
setName(String name) 设置线程名称。(或者用构造函数也可以)
Thread类本身也实现了Runnable.接口
用这种方法避免了单继承的局限性。
1、创建一个类实现Runnable接口。
2、重写run方法。
3、通过Thread 类建立线程对象。
4、将Runnable接口的子类作为参数传给线程对象,使用Thread类的构造函数。
5、调用Thread类的start()方法开启线程并调用Runnable接口子类的run()方法。
继承和实现这两种方式建立线程有什么区别呢?
线程代码存在位置不同。
Thread继承:线程代码存在于子类的run方法中。
Runnable实现: 线程代码 存在于接口子类的run 方法中。
为什么要设置Runnable接口?
java只支持单继承,如果一个类中有一部分代码需要被多线程执行,但是它又已经有了父类,这时就要用到接口来解决这个问题。接口的好处之一就是扩展了类的功能。
多线程的安全问题。
下面是一个卖票的小程序。
/**
卖票程序,4个线程卖100张票。
*/
//extends Thread
class Ticket implements Runnable
{
private int tick = 100;
public void run()//这里不能抛出,因为它是一个接口,其父类没有抛出异常,它也不能,只能try
{
while(true)
{
if(tick>0)
{
try{Thread.sleep(10); }//让正在执行的线程睡10秒。
catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....seals " +tick--);
}
}
}
}
class TickTest
{
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();
}
}
由于 多个线程操作同一个变量,会导致一些问题,一个线程对多条语句只执行了一部分另一个线程参与进来 ,导致同享数据出问题。
解决方法:
同步代码块:对象就好比一个锁,持有锁的线程才可以运行,没持有锁的进程即使有CPU执行权也无法执行。
synchronized(对象)
{
需要同步的代码(哪里操作共享数据哪里就是)
}
修改之后的代码:
/**
卖票程序,4个线程卖100张票。
*/
//extends Thread
class Ticket implements Runnable
{
private int tick = 100;
Object obj = new Object();
public void run()
{
while(true)
{
synchronized(obj)
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....seals " +tick--);
}
}
}
}
}
class TickTest
{
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();
}
}
同步的前提:
1、至少两个以上的线程。
2、用的是同一个锁,保证同步中只有一个线程在执行。
优点:解决了多线程安全问题
缺点:多个线程都需要判断锁的状态,比较消耗资源。
同步代码块与函数都是封装了一部分代码,那他们的区别是? ——同步代码块可以同步。
所以有了新的关键字:只需要把synchronized 作为关键字放在函数声明中即可,也不需要定义对象了。因为函数都是被对象调用的,都有个引用对象,this ,这次就是这个对象在调用。
但加了static 后的函数由于 没有了this,所以静态同步函数的锁 是??
静态进内存时,还没有本类创建的对象 ,但是有一个该类对应的字节码文件【类名.class】,该对象的类型是class。
静态同步方法使用的锁是该方法所在类的字节码文件对象(类名.class)
下例:
/**
需求:有两个客户向银行存钱,都存300,存3次每次100。
*/
class Bank
{
private int sum;
// Object obj = new Object();
public synchronized void add(int n)
{
//synchronized(obj)
// {
sum=sum+n;
try{Thread.sleep(10);}
catch(Exception e){}
System.out.println("sum="+sum);
//}
}
}
class Cus implements Runnable
{
private Bank b = new Bank();
public void run()
{
for(int i=0;i<3;i++)
{
b.add(100);
}
}
}
class BankTest
{
public static void main(String[] args)
{
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
带有synchronized函数称为同步函数。但是放关键字的位置很重要,比如上面的卖票程序需要改成这个样子:
/**
卖票程序,4个线程卖100张票。
*/
//extends Thread
class Ticket implements Runnable
{
private int tick = 100;
//如果在这里加关键字,那么只有一个线程执行这个程序,其它进不去。结果是一个线程卖100张票。
public void run()
{
while(true)
{
show();
}
}
//必须仅让涉及问题的代码封装成函数
public synchronized void show()
{
if(tick>0)
{
try{Thread.sleep(10); }
catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....seals " +tick--);
}
}
}
class TickTest
{
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 Single
{
private static final Single s = new Single();
private Single();
public static void getInstance()
{
return s;
}
}
懒汉式(延迟加载)
class Single
{
private static Singel s=null;
private Single(){}
public static synchronized void getInstence()
{
if (s==null)
s=new Single();
return s;
}
}
*/
class Single
{
private static Singel s=null;
private Single(){}
public static void getInstence()
{
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(Mylock.locka)
{
System.out.println("if loaka");
synchronized(Mylock.lockb)
{
System.out.println("if loakb");
}
}
}
}
else
{
while(true)
{
synchronized(Mylock.lockb)
{
System.out.println("else loakb");
synchronized(Mylock.locka)
{
System.out.println("else loaka");
}
}
}
}
}
}
class Mylock
{
static Object locka = new Object();
static Object lockb = new Object();
}
class Death2
{
public static void main(String[] args)
{
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}
一种报错是:无效线程异常——多次开启一个线程。 start就是让线程从初始到运行状态。已经在运行状态的线程就不能再运行了
这里一般用复写Runnable接口的方法。
下面这段代码有两个点值得学习:
1 由于input 和output 必须操作的是同一个对象,而用单例设计模式又太麻烦,这里用了一个传对象的方式。在input和output内自己定义了一个对象,我们只需要在外边定义一个对象然后传进来就搞定了。
2这里没用flag=falser 的方式 而是用了一个没见过的x=0 x=(x+1)%2