标签:
多线程
进程:一个正在执行中的程序,每一个进行执行,都有一个执行顺序,该顺序就是一个执行路径,或者加一个执行单元
线程:就是进程中的一个独立的执行路径,一个进程中至少有一个线程。
java vm启动的时候会有一个进程java.exe。
该进程中至少一个线程负责执行java程序的执行,
而且这个线程运行的代码存在于main方法中。
该线程称之为主线程。
扩展:其中更细节说明java vm启动不止一个线程,还有赋值垃圾回收机制的线程。
创建线程的实现步骤:
方法一
1.定义一个类继承Thread类
2.覆写Thread类中的run方法
3.在主函数中直接创建Thread的子类
4.运行start调用线程
方法二
1.定义一个类实现Runnable接口
2.在此类中写入需要多线程运用的代码
3.将 Runnable 接口的子类对象作为实际参数传递给 Thread 类的构造函数。
4.运行start调用线程
以下代码简单演示创建线程的两种方式
class Demo1 extends Thread { //第一种方式继承Thread类 private String name; Demo1(String name){ this.name = name; } public void run() { //覆写run方法 for (int i=1;i<10 ;i++ ){ //循环9次打印当前线程名字 System.out.println(name+"...."+i+Thread.currentThread().getName()); //Thread.currentThread().getName() 获取 当前 运行中 线程的名字 } } } class Demo2 implements Runnable { //第二种方式,实现Runnable接口 private String name; Demo2(String name){ this.name = name; } public void run(){ //覆写run方法 for (int i=1;i<10 ;i++ ){ //循环9次打印当前线程名字 System.out.println(name+"...."+i+Thread.currentThread().getName()); //Thread.currentThread().getName() 获取 当前 运行中 线程的名字 } } } public class AThreadDemo { public static void main(String[] args) { /* 创建线程的目的是为了开启一条新的执行路径,去运行指定的代码和其他代码实现同时运行。 而运行的 指定代码就是这个执行路径的任物。 jvm创建的主线程的任物都定义在主函数中。 而自定义的新城用于描述线程,线程是需要任物的,所以Thread类也对任物的描述。 这个任务就通过Thread类中的run方法来体现,也就是run方法就是封装自定义线程运行任务的函数。 run方法中定义的就是线程要运行的任物代码。 */ Demo1 d1 = new Demo1("d1"); Demo2 d2 = new Demo2("d2"); Thread t1 = new Thread(d1); Thread t2 = new Thread(d2); t1.start(); //开启多线程,调用run方法 t2.start(); //t1.run(); //t2.run(); //直接调用run方法,没有线程的随机性. } }
运行结果
由运行结果可知,d1线程和d2线程交替运行,两个线程均创建成功,并在主线程中调用正常。
线程安全
线程安全问题产生的原因:
1.多个线程在操作共享的数据。
2.操作共享数据的线程代码有多条。
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算就会导致线程安全问题的产生。
问题解决思路:
将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程不能参与运算。
必须当前线程吧这些代码都执行完毕后,其他线程才可以产于运算。
java中用同步代码块或同步函数解决该问题。
同步代码块的格式:
synchronized(对象) //此处对象为任意对象,可直接用Objct
{
需要被同步的代码
}
同步函数就是用synchronized修饰函数
注意:定义同步函数必须注意需要同步的代码是哪些。
同步的前提:同步中必须有多个线程,并使用同一个锁(即使用的同一个对象)
如果同步函数被静态修饰后,使用的锁是什么?
通过验证,发现不是this,因为静态方法不可以定义this
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
类名.class 该类对象的类型是Class。
静态的同步方法,使用的锁是该方法所在类的字节码所在对象。
多线程练习
需求:有20张票,需要在4个窗口卖完
编程注意:必须保证各个线程是共用的一个num数据
以下代码演示同步代码块解决安全问题的方法
步骤:
1:定义一个Thread类,包含了对卖票的代码
2:在定义Thread类时,将票数num定义为静态变量,从而保证不同线程运行时是对相同的100张票进行调用。
3:在主函数中定义4个线程对象,并用start方法开启线程。
//实现代码一:(应用同步代码块) class Ticket1 extends Thread{ private static int num = 20; //将num储存在方法区,由4个线程共用。 Object obj = new Object(); //对象必须定义在run外 public void run(){ while (num>0){ synchronized(obj){ //此处需要被同步的代码存在多条,必须定义同步代码块 if(num>0){ System.out.println(Thread.currentThread().getName()+"..."+num--); } } } } } public class BThreadTicket1{ public static void main(String[] args){ Ticket1 t1 = new Ticket1(); Ticket1 t2 = new Ticket1(); Ticket1 t3 = new Ticket1(); Ticket1 t4 = new Ticket1(); t1.start(); t2.start(); t3.start(); t4.start(); } }
运行结果
由运行结果可以看出,4个线程交替运行,出售这20张票。
第二种实现方式
步骤:
1:定义一个包含票数,及买票功能的对象Ticket2,并对其扩展Runnable接口,在其中覆写run方法。
2:在主函数中创建票对象
3:在主函数中创建4个线程对象,并将票对象作为它们的构造函数参数
4:运行start方法开启线程
class Ticket2 implements Runnable { private int num = 20; public void run() { while (num>0){ synchronized(Ticket2.class){ if(num>0){ try{Thread.sleep(10);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"..."+num--); } } } } //定义同步函数,函数需要被对象调用,那么函数都有一个所属对象引用,就是this //所以同步函数使用的锁是this } public class BThreadTicket2 { public static void main(String[] args){ //单独建立一个票的对象,4个线程均是对该票对象的调用 Ticket2 t2 = new Ticket2(); Thread th1 = new Thread(t2); Thread th2 = new Thread(t2); Thread th3 = new Thread(t2); Thread th4 = new Thread(t2); th1.start(); th2.start(); th3.start(); th4.start(); } }
运行结果:
由运行结果可以看出,4个线程交替运行,出售这20张票。
多线程应用
需求:
生产者生成15个产品 ,并且每生产一个产品,消费者就消费一个。
此代码用了新的同步方式,定义锁;并通过定义标记,使消费线程和成产线程交替运行。
import java.util.concurrent.locks.*; class Resource{ //定义商品 private String name; //定义成员变量 private int count = 1; private boolean flag = false; //定义标记 //Lock方法 private Lock lock = new ReentrantLock(); //定义锁 private Condition condition_pro = lock.newCondition(); private Condition condition_cou = lock.newCondition(); public void set(String name) throws InterruptedException //定义生产动作 { if (count<15){ lock.lock(); //获取锁 try{ while (flag) //注意此处用while是因为需要循环判断 condition_pro.await(); //使当前线程等待 this.name = name; //生成一个产品 System.out.println(Thread.currentThread().getName()+name+"生产商品"+(count++)+"----"); flag = true; //改变标记,是下次循环时能够让生产者等待 condition_cou.signal(); //唤醒另一个在等待的线程 } finally { lock.unlock(); //释放锁 } } } public void show() throws InterruptedException //定义消费动作 { if (count<15){ lock.lock(); //获取锁 try{ while (!flag) condition_cou.await(); //让当前线程等待 System.out.println(Thread.currentThread().getName()+"----消费商品"+name+(count-1)); flag = false; //消费一个对象,并改变标记 condition_pro.signal(); //唤醒另一个正在等待的线程 } finally { lock.unlock(); //释放锁 } } } } class Producter implements Runnable{ //定义生成者 private Resource r; Producter (Resource r){ //将商品传给生产者 this.r = r; } public void run(){ //覆写run方法 while (true){ try{ r.set("商品"); //调用生产动作,生产商品 } catch (InterruptedException e){ } } } } class Coustomor implements Runnable{ //定义消费者 private Resource r; Coustomor (Resource r){ //将商品传给消费者 this.r = r; } public void run(){ //覆写run方法 while (true){ try{ r.show(); //调用消费动作消费商品 } catch (InterruptedException e){ } } } } public class CThreadRe{ public static void main(String[] args) { Resource r = new Resource(); //定义商品对象 Producter pro = new Producter(r); //将商品传给生产者 Coustomor cou = new Coustomor(r); //将商品传给消费者 Thread t1 = new Thread(pro); //定义两个生产线程 Thread t2 = new Thread(pro); Thread t3 = new Thread(cou); //定义两个消费线程 Thread t4 = new Thread(cou); t1.start(); t2.start(); t3.start(); t4.start(); } }
运行结果
上面代码实际上是运用流多线程通讯的思想。
线程间通讯:
其实就是多个线程在操作同一个资源,但操作动作不同
实现核心:
多线程的等待唤醒机制
步骤:
1:设置flag标记变量
2: 在同步代码块中设置 锁名.wait() 而且锁名.wait()必须被 try{}catch{}(或是锁对象.await())
3: 在同步代码块中设置 锁名.notify().(或是锁对象.singal())
注意:设置wait() notify()时,运用flag变量做控制
标签:
原文地址:http://www.cnblogs.com/myblog-cl/p/4746319.html