Java基础——多线程

12、多线程

进程

进程是一个正在执行的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径或者叫一个控制单元

线程:

就是进程中一个独立的控制单元,线程在控制着进程的执行,一个进程中至少有一个线程

Java VM启动的时候会有一个进程java.exe,,该进程至少有一个线程负责java程序的执行。而且这个线程运行的代码在main方法中,该线程称为主线程,其实更细节说明jvmjvm启动停止不止一个线程,还有负责垃圾回收机制的线程

创建线程,继承Thread

1、定义类继承Thread

2、复写Thread中的run方法

3、调用线程start方法,该方法有两种作用(启动线程,调用run方法)

package days4;

class Demo extends Thread

{

public void run()

{

for(int i = 0;i<60;i++)

{

System.out.println("thread"+i);

}

}

}

public class Thr1

{

   public static void main(String[]args)

   {

Demo d=new Demo();

     d.start();

     for(int i=0;i<60;i++)

     {

      System.out.println("main"+i);

     }

   }

}

    发现运行结果每次都不同,因为两个线程都在获取CPU的执行权(CPU执行到谁,谁就执行,在某一个时刻只能有一个程序在运行,多核除外),CPU在做着快速的切换,已达到看上去是同时运行的效果。

我们可以形象的把多线程的运行,形容为互相抢夺CPU资源或执行权

这就是多线程一个特性,随机性,谁抢到谁执行

线程运行状态

获取线程对象及名称

Current  Thread()获取当前线程对象

对象。getName()获取线程名称

简单的买票示例

package days4;

class Ticket extends Thread

{

  private int tick=20;

  public void run()

  {

  while(tick>0)

  {

  System.out.println(Ticket.currentThread().getName()+"sale"+tick--);

  }

  }

}

public class Ticket1 

{

public static void main(String[]args)

{

Ticket t1=new Ticket();

Ticket t2=new Ticket();

Ticket t3=new Ticket();

Ticket t4=new Ticket();

t1.start();

t2.start();

t3.start();

t4.start();

}

}

简单的卖票程序(代码改进)

实现Runable接口

1、定义类实现Runnable接口

2、覆盖Runnable接口中的run方法(将线程要运行的代码存放在run方法中)

3、通过Thread类建立线程对象

4、Runnable接口的子类对象作为实际参数传递给Thread类的构造函数

5、调用Thread类的start方法开启线程,并调用Runnable接口子类的run方法

为什么要将Runnable接口的子类对象传递给Thread的构造函数?

因为自定义的run方法所属的对象是Runnable接口的子类对象,所以要让线程执行指定的run方法,就必须明确run方法所属对象

实现方式和继承方式的区别

好处在于实现方式避免了单继承的局限性。在定义线程时建议实现方式使用实现方式

继承Thred:线程代码存放在Thread子类run方法中

实现Runnable:线程代码存放在接口子类的run方法中

package days4;

class Ticket extends Thread

{

  private int tick=20;

  public void run()

  {

  while(tick>0)

  {

  System.out.println(Ticket.currentThread().getName()+"sale"+tick--);

  }

  }

}

public class Ticket1 

{

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();

}

}

多线程的安全问题

问题在于当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据错误

解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程可以不参与执行

Java对于多线程的安全问题提供了专业的解决方式——同步代码块

Synchronized(对象)

{

   需要被同步执行的代码块

   

}

对象如同锁,持有锁的线程可以在同步中执行。没有持有锁的线程,即使获取CPU的执行权也进不去,因为没有锁

同步的前提:

1、必须要有两个或者两个以上的线程

2、必须多个线程使用同一个锁

3、必须保证同步中只有一个线程在运行

    

好处:解决了多线程的安全问题

弊端:解决了问题但是消耗了资源,多个线程需要判断锁

class Tickets implements Runnable

{

  private int tick=100;

  private Object obj=new Object();

  public void run()

  {

while(true)

{  synchronized(obj)

 { if(tick>0)

  {

  System.out.println(Ticket.currentThread().getName()+"sale"+tick--);

  }

 }

}

 }

}

public class Ticket2 

{

public static void main(String[]args)

{

Tickets t=new Tickets();

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();

}

}

多线程同步函数

   练习代码

需求:

银行有一个金库。

有两个储户分别存300员,每次存100,存3次。

目的:该程序是否有安全问题,如果有,如何解决?

如何找问题:

1,明确哪些代码是多线程运行代码。

2,明确共享数据。

3,明确多线程运行代码中哪些语句是操作共享数据的。

package days4;

class Ban

{

private int sum=0;

public synchronized void add(int n)

{

sum=sum+n;

System.out.println("sum="+sum);

}

}

class Cus  implements Runnable 

{

Ban ba;

Cus()

{

 ba=new Ban();

}

    

public void run() 

{

for(int i=0;i<3;i++)

{

   ba.add(100);

}

}

}

public class Bank 

{

public static void main(String[]args)

{

Cus t=new Cus();

Thread t1=new Thread(t);

Thread t2=new Thread(t);

t1.start();

t2.start();

}

}

同步函数——锁

函数需要被对象调用,那么函数都有一个所属对象调用——this,所以同步函数调用的锁是this

package days4;

class Ticket5 implements Runnable

{

private  int tick = 100;

Object obj = new Object();

boolean flag = true;

public  void run()

{

if(flag)

{

while(true)

{

synchronized(this)

{

if(tick>0)

{

try{Thread.sleep(10);}catch(Exception e){}

System.out.println(Thread.currentThread().getName()+"....code : "tick--);

}

}

}

}

else

while(true)

show();

}

public synchronized void show()//this

{

if(tick>0)

{

try{Thread.sleep(10);}catch(Exception e){}

System.out.println(Thread.currentThread().getName()+"....show.... : "tick--);

}

}

}

class  This

{

public static void main(String[] args) 

{

Ticket5 t = new Ticket5();

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

通过验证,发现不再是this,因为静态方法中不可以定义this,静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象

类名。Class是锁,该对象类型是class

练习代码:

package days4;

class Ticket6 implements Runnable

{

private static  int tick = 100;

//Object obj = new Object();

boolean flag = true;

public  void run()

{

if(flag)

{

while(true)

{

synchronized(Ticket.class)

{

if(tick>0)

{

try{Thread.sleep(10);}catch(Exception e){}

System.out.println(Thread.currentThread().getName()+"....code : "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  Static

{

public static void main(String[] args) 

{

Ticket6 t = new Ticket6();

Thread t1 = new Thread(t);

Thread t2 = new Thread(t);

t1.start();

try{Thread.sleep(10);}catch(Exception e){}

t.flag = false;

t2.start();

}

}

多线程——死锁

练习代码:

死锁:

package days4;

class Ticket7 implements Runnable

{

private  int tick = 100;

Object obj = new Object();

boolean flag = true;

public  void run()

{

if(flag)

{

while(true)

{

synchronized(obj)

{

show();

}

}

}

else

while(true)

show();

}

public synchronized void show()//this

{

synchronized(obj)

{

if(tick>0)

{

try{Thread.sleep(10);}catch(Exception e){}

System.out.println(Thread.currentThread().getName()+"....code : "tick--);

}

}

}

}

class  Dead

{

public static void main(String[] args) 

{

Ticket7 t = new Ticket7();

Thread t1 = new Thread(t);

Thread t2 = new Thread(t);

t1.start();

try{Thread.sleep(10);}catch(Exception e){}

t.flag = false;

t2.start();

}

}

死锁练习:

package days4;

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(Thread.currentThread().getName()+"...if locka ");

synchronized(MyLock.lockb)

{

System.out.println(Thread.currentThread().getName()+"..if lockb");

}

}

}

}

else

{

while(true)

{

synchronized(MyLock.lockb)

{

System.out.println(Thread.currentThread().getName()+"..else lockb");

synchronized(MyLock.locka)

{

System.out.println(Thread.currentThread().getName()+".....else locka");

}

}

}

}

}

}

class MyLock

{

static Object locka = new Object();

static Object lockb = new Object();

}

class  Dead2

{

public static void main(String[] args) 

{

Thread t1 = new Thread(new Test(true));

Thread t2 = new Thread(new Test(false));

t1.start();

t2.start();

}

}

线程间通信

其实就是多个线程在操作一个资源在操作同一个资源,但是操作的动作不同

等待唤醒机制

Wait

Notify

Notifyall( )

为什么这些操作线程方法要定义在Object类中

     因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。

练习代码:

package days5;

class Res

{

String name;

boolean flag=false;

int age;

int x=20;

}

class Thr1 implements Runnable

{

private Res r;

Thr1(Res r)

{

this.r=r;

}

 

public void run() 

{

  while(true&&r.x>0)

   {

   synchronized(r)

   {

 if(r.flag)

 {

 try{r.wait();}

 catch(Exception e)

 {

 

 }

 }

  if(r.x%2==0)

 { r.age=5;

   r.name="Tom";

 }

 else

 {

 r.age=10;

 r.name="Anna";

 }

  r.x--;

      r.flag=true;

      r.notify();

   }

   }

}

}

class Thr2 implements Runnable

{

   private Res r;

   Thr2(Res r)

   {

   this.r=r;

   }

public void run() 

{

while(true&&r.x>0)

{

synchronized(r)

{

if(!r.flag)

{

try{r.wait();}

catch(Exception e)

{   }

}

System.out.println("name"+r.name+"age"+r.age);

r.flag=false;

r.notify();

}

}

}

}

public class Wait 

{

public static void main(String args[])

{

Res r2=new Res();

Thr1 th1=new Thr1(r2);

Thr2 th2=new Thr2(r2);

Thread t1=new Thread(th1);

Thread t2=new Thread(th2);

t1.start();

t2.start();

}   

}

package days5;

class Res2

{

private String name;

private String sex;

private boolean flag = false;

public synchronized void set(String name,String sex)

{

if(flag)

try{this.wait();}catch(Exception e){}

this.name = name;

this.sex = sex;

flag = true;

this.notify();

}

public synchronized void out()

{

if(!flag)

try{this.wait();}catch(Exception e){}

System.out.println(name+"........"+sex);

flag = false;

this.notify();

}

}

class Input implements Runnable

{

private Res2 r ;

Input(Res2 r)

{

this.r = r;

}

public void run()

{

int x = 0;

while(true)

{

if(x==0)

r.set("mike","man");

else

r.set("丽丽","女女女女女");

x = (x+1)%2;

}

}

}

class Output implements Runnable

{

private Res2 r ;

Output(Res2 r)

{

this.r = r;

}

public void run()

{

while(true)

{

r.out();

}

}

}

class  Wait2

{

public static void main(String[] args) 

{

Res2 r = new Res2();

new Thread(new Input(r)).start();

new Thread(new Output(r)).start();

}

}

package days5;

class Res3

{

private String name;

private int age;

boolean flag=false;

int count=20;

public synchronized void Set()

{

    if(flag)

    {

       try{

        this.wait();

       }

       catch(Exception e)

       {

        

       }

    }

if(count%2==0)

{

name="S1";

age=5;

}

else

{

name="S2";

age=10;

}

flag=true;

this.notifyAll();

}

    public synchronized void Out()

    {

     if(!flag)

     {

     try

     {

     this.wait();

     }

     catch(Exception e)

     {

    

     }

     }

     System.out.println(Thread.currentThread().getName()+"name"+name+"age"+age+"%%%%%"+count--);

     flag=false;

     this.notifyAll();

    }

}

class  Input2 implements Runnable

{

    private Res3 r;

    Input2(Res3 r)

    {

     this.r=r;

    }

public void run() 

{

     while(true&&r.count>0)

     {

      r.Set();

     }

}

}

class Output2 implements Runnable

{

private Res3 r;

Output2(Res3 r)

{

this.r=r;

}

public void run()

{

while(true&&r.count>0)

{

r.Out();

}

}

}

public class InputOutput 

{

   public static void main(String []args)

   {

   Res3 re=new Res3();

   Input2 in=new Input2(re);

   Output2 out=new Output2(re);

   Thread t1=new Thread(in);

   Thread t2=new Thread(in);

   Thread t3=new Thread(out);

   Thread t4=new Thread(out);

   t1.start();

   t2.start();

   t3.start();

   t4.start();

   }

}

对于多个生产者和消费者。

为什么要定义while判断标记。

原因:让被唤醒的线程再一次判断标记。

为什么定义notifyAll

因为需要唤醒对方线程。

因为只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。

    JDK1.5 中提供了多线程升级解决方案。将同步Synchronized替换成现实Lock操作。

Object中的waitnotify notifyAll,替换了Condition对象。该对象可以Lock锁 进行获取。该示例中,实现了本方只唤醒对方操作。

Lock替代了Synchronized

Condition:替代了Object wait notify notifyAll

package days5;

import java.util.concurrent.locks.*;

class Resource

{

private String name;

private int count = 1;

private boolean flag = false;

//  t1    t2

private Lock lock = new ReentrantLock();

private Condition condition_pro = lock.newCondition();

private Condition condition_con = lock.newCondition();

public  void set(String name)throws InterruptedException

{

lock.lock();

try

{

while(flag)

condition_pro.await();//t1,t2

this.name = name+"--"+count++;

System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);

flag = true;

condition_con.signal();

}

finally

{

lock.unlock();//释放锁的动作一定要执行。

}

}

//  t3   t4  

public  void out()throws InterruptedException

{

lock.lock();

try

{

while(!flag)

condition_con.await();

System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);

flag = false;

condition_pro.signal();

}

finally

{

lock.unlock();

}

}

}

class Producer implements Runnable

{

private Resource res;

Producer(Resource res)

{

this.res = res;

}

public void run()

{

while(true)

{

try

{

res.set("+商品+");

}

catch (InterruptedException e)

{

}

}

}

}

class Consumer implements Runnable

{

private Resource res;

Consumer(Resource res)

{

this.res = res;

}

public void run()

{

while(true)

{

try

{

res.out();

}

catch (InterruptedException e)

{

}

}

}

}

class InputOutput2 

{

public static void main(String[] args) 

{

Resource r = new Resource();

Producer pro = new Producer(r);

Consumer con = new Consumer(r);

Thread t1 = new Thread(pro);

Thread t2 = new Thread(pro);

Thread t3 = new Thread(con);

Thread t4 = new Thread(con);

t1.start();

t2.start();

t3.start();

t4.start();

}

}

停止线程

1、定义循环结束标记

因为线程运行的代码一般都是循环,只要控制循环即可

2、使用interrupt(中断)方法

该方法是结束进程的冻结状态,使线程回到运行状态中来

如何停止线程?

Run方法结束,开启线程运行,运行代码通常都是循环结构,只要控制循环,就可以让run方法结束,也就是循环结束,线程结束

特殊情况:当线程处于冻结状态,就不会读取标记,那么线程就不会结束。当没有指定方式让冻结的线程恢复到运行状态时,这时对冻结状态进行清除,强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束

Thread类中提供该方法:interrupt()

Join方法

A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行join可以用来临时加入线程执行。

练习代码:

package days5;

class Demo implements Runnable

{

public void run()

{

for(int x=0; x<20; x++)

{

System.out.println(Thread.currentThread().toString()+"....."+x);

Thread.yield();

}

}

}

class  Join

{

public static void main(String[] args) throws Exception

{

Demo d = new Demo();

Thread t1 = new Thread(d);

Thread t2 = new Thread(d);

t1.start();

//t1.setPriority(Thread.MAX_PRIORITY);

t2.start();

//t1.join();

for(int x=0; x<80; x++)

{

//System.out.println("main....."+x);

}

System.out.println("over");

}

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值