进程是一个正在执行的程序
每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
Java VM 启动的时候会有一个进程java.exe
一个进程中至少有一个线程负责Java程序的运行,而且这个线程运行的代码存放在main方法中,该线程称之为主线程
扩展:其实更细节说明虚拟机,虚拟机不止启动一个线程,还有负责垃圾回收的线程。
第一种创建线程的步骤:
1.创建类继承Thread类或者实现Runable接口
2.复写Run方法
3.调用线程的start方法
该方法有两个作用:启动线程 ,调用run方法
示例如下:假设有4个售票窗口同时进行售票,每一个售票窗口可以理解为一个线程,四个窗口同时售票
也就是四个线程同时启动,而假定票数是10张,这样....每个窗口也就是线程都在运行时产生四个对象,会
有40张票,即每个对象产生10张票,一个线程,总共40张票4个线程,为了解决这个问题,可以考虑将
Ticket类中的num设置为static使四个对象共享这10张票。
输出如下:Thread-1售票9
Thread-1售票6
Thread-1售票5
Thread-1售票4
Thread-1售票3
Thread-1售票2
Thread-1售票1
Thread-1售票0
Thread-0售票7
Thread-3售票8
--------------------------------可以观察到四个线程共享tiket_num这个数据,但是静态的对象属性生命周期过长,而且需要建立四个对象 所以可以考虑用实现Runnable接口的
方式来实现。
为什么要覆盖Run方法?
Thread类用于描写线程
该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法
也就是说Thread类中的Run方法是用于存储线程要运行的代码
star方法和run方法的区别:
start--开启线程并执行该线程的run方法
run--仅仅是对象调用方法。而线程创建了,并没有执行 也就是只执行run方法内的代码 但是没有多线程
static Thread currentThread();获取当前线程对象
getName();获取线程名称
设置线程名称:setName或者构造函数
注意:每一个变量在线程当中都有独立的一份
创建线程的第二种方式:实现Runnable接口
步骤:
1.定义类实现Runable接口
2.覆盖Runable接口中的run方法
将线程要运行的代码放在该run方法中
3.通过Thread类建立线程对象
4.将Runable接口的子类对象作为实际参数传递给Thread类构造函数
为什么要将Runnable接口的子类对象传递给Thread的构造函数
因为,自定义的run方法所属的对象是实现Runnable接口的子类对象
所以要让线程去执行指定对象的run方法。就必须明确该run方法所属的对象
5.调用Thread类的start方法开启线程并调用Runable接口子类的run方法
售票示例如下:
输出如下:Thread-0售票10
Thread-2售票8
Thread-1售票9
Thread-1售票5
Thread-1售票4
Thread-1售票3
Thread-1售票2
Thread-1售票1
Thread-0售票6
Thread-2售票7
实现方式和继承方式有什么区别?
实现方式的好处:避免了单继承的局限性。在定义线程时,建议使用实现方式。
继承Thread:线程代码存放在Thread子类run方法中。
实现Runnable,线程代码存放在接口子类的run方法中。
多线程的安全隐患:(需要多加注意)
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没
有执行完,另一个线程参与进来执行。导致了共享数据的错误。
解决办法:对于多条操作共享数据的语句,只能让一个线程都执行完,其他的线程才能
参与执行。
Java对于多线程的安全问题提供了专业的解决方案,就是同步代码块。
synchronized(对象){
需要被同步的代码
}
如何找到问题 找到该同步哪些代码块?
1.明确哪些代码是需要多线程运行代码。
2.明确共享数据。
3.明确多线程运行代码中的哪些语句是操作共享数据的。
同步函数---同步函数用的是哪一个锁?
---同步函数用的锁就是this,函数需要被对象调用,那么函数都有一个所属的对象的引用。就是this.
所以同步函数使用的锁是this.
验证示例:通过以下的程序来验证。需求为 --通过两个线程来买票一个线程在同步代码块中,另一
个线程在同步函数中。都在执行买票动作。
代码如下:
输出结果如下:
-------------------------------------
同步代码块进程---Thread-0-----sale:10
同步函数进程---Thread-1-----sale:8
同步代码块进程---Thread-0-----sale:9
同步代码块进程---Thread-0-----sale:7
同步函数进程---Thread-1-----sale:7
同步函数进程---Thread-1-----sale:6
同步代码块进程---Thread-0-----sale:5
同步代码块进程---Thread-0-----sale:4
同步函数进程---Thread-1-----sale:3
同步函数进程---Thread-1-----sale:2
同步代码块进程---Thread-0-----sale:1------------可以观察输出有序且线程Thread1和Thread0交替执行
那么上面的例子验证了同步函数的锁是this 那么静态同步函数的锁是什么呢?
--首先不是this,因为在static 中没有this.静态进内存是,内存中没有本类对象,但是一定有该类对应
的字节码文件对象。--->也就是类名.class 该对象类型是Class 即在静态函数中同步锁为类名.class
示例代码如下:
输出如下:
-------------------------------------
同步代码块进程---Thread-0-----sale:10
+++++++++++++++++++
同步代码块进程---Thread-0-----sale:9
同步代码块进程---Thread-0-----sale:8
同步函数进程---Thread-1-----sale:7
同步函数进程---Thread-1-----sale:6
同步函数进程---Thread-1-----sale:5
同步函数进程---Thread-1-----sale:4
同步函数进程---Thread-1-----sale:3
同步函数进程---Thread-1-----sale:2
同步函数进程---Thread-1-----sale:1
----------------------------------------小结:静态的同步方法:使用的锁是该方法所在类的字节码
文件对象。类名.class
多线程的单例设计模式
1懒汉式
====================================================================================================
死锁的问题 用以下的示例来说明死锁的问题
===========================================================================================
线程间通信的安全问题:
线程A对资源R进行加操作线程B对资源R进行减操作 如何同步 如何同步时安全操作 示例代码如下:
经过测试 输出有序 说明线程间通信的安全问题解决
输出如下:
mikeman
mikeman
mikeman
王语嫣女女
王语嫣女女
王语嫣女女
==========================================================================================
等待唤醒机制
---接上个例子 要求 男女输出交替进行 也就是 写--男--输出--男---写--女---输出---女
资源类如下:
输出结果如下:
mike。。。。man
周芷若。。。。女女
mike。。。。man
周芷若。。。。女女
mike。。。。man
周芷若。。。。女女
mike。。。。man
周芷若。。。。女女
mike。。。。man
周芷若。。。。女女
mike。。。。man-----------结果有序 而且交替输出
每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
Java VM 启动的时候会有一个进程java.exe
一个进程中至少有一个线程负责Java程序的运行,而且这个线程运行的代码存放在main方法中,该线程称之为主线程
扩展:其实更细节说明虚拟机,虚拟机不止启动一个线程,还有负责垃圾回收的线程。
第一种创建线程的步骤:
1.创建类继承Thread类或者实现Runable接口
2.复写Run方法
3.调用线程的start方法
该方法有两个作用:启动线程 ,调用run方法
示例如下:假设有4个售票窗口同时进行售票,每一个售票窗口可以理解为一个线程,四个窗口同时售票
也就是四个线程同时启动,而假定票数是10张,这样....每个窗口也就是线程都在运行时产生四个对象,会
有40张票,即每个对象产生10张票,一个线程,总共40张票4个线程,为了解决这个问题,可以考虑将
Ticket类中的num设置为static使四个对象共享这10张票。
Ticket类中的num设置为static使四个对象共享这10张票。 //Ticke类示例代码如下: public class Ticket extends Thread{//实现线程需继承Thread类 private static int ticket_num=10;//静态共享数据假定为10张票 @Override public void run() {//复写run方法 while(ticket_num>0) { if(ticket_num>0) { System.out.println(Thread.currentThread().getName()+"售票"+--ticket_num); //Thread为类名,currentThread()为静态方法,可以由类名调用,作用是返回当前线程对象,getName(),拿到当前线程的名字 } } } } //TestMain示例代码如下: public class TestMain { public static void main(String[] args) { Ticket t1=new Ticket();//创建四个Thread类的子对象 Ticket t2=new Ticket(); Ticket t3=new Ticket(); Ticket t4=new Ticket(); t1.start();t2.start();t3.start();t4.start();//启动这四个对象的线程 } }
输出如下:Thread-1售票9
Thread-1售票6
Thread-1售票5
Thread-1售票4
Thread-1售票3
Thread-1售票2
Thread-1售票1
Thread-1售票0
Thread-0售票7
Thread-3售票8
--------------------------------可以观察到四个线程共享tiket_num这个数据,但是静态的对象属性生命周期过长,而且需要建立四个对象 所以可以考虑用实现Runnable接口的
方式来实现。
为什么要覆盖Run方法?
Thread类用于描写线程
该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法
也就是说Thread类中的Run方法是用于存储线程要运行的代码
star方法和run方法的区别:
start--开启线程并执行该线程的run方法
run--仅仅是对象调用方法。而线程创建了,并没有执行 也就是只执行run方法内的代码 但是没有多线程
static Thread currentThread();获取当前线程对象
getName();获取线程名称
设置线程名称:setName或者构造函数
注意:每一个变量在线程当中都有独立的一份
创建线程的第二种方式:实现Runnable接口
步骤:
1.定义类实现Runable接口
2.覆盖Runable接口中的run方法
将线程要运行的代码放在该run方法中
3.通过Thread类建立线程对象
4.将Runable接口的子类对象作为实际参数传递给Thread类构造函数
为什么要将Runnable接口的子类对象传递给Thread的构造函数
因为,自定义的run方法所属的对象是实现Runnable接口的子类对象
所以要让线程去执行指定对象的run方法。就必须明确该run方法所属的对象
5.调用Thread类的start方法开启线程并调用Runable接口子类的run方法
售票示例如下:
public class Ticket implements Runnable{ private String name;//售票窗口名字 private int num;//票数 Ticket(String name,int num) { this.name=name;this.num=num; } public void run() {//复写run方法 while(this.num>0) { System.out.println(Thread.currentThread().getName()+"售票"+this.num--); } } } //TestMain程序代码如下: public class TestMain { public static void main(String[] args) { Ticket t1=new Ticket("售票窗口",10);//创造实现Runnable接口的子类对象 new Thread(t1).start();//开启3个线程 new Thread(t1).start(); new Thread(t1).start(); } }
输出如下:Thread-0售票10
Thread-2售票8
Thread-1售票9
Thread-1售票5
Thread-1售票4
Thread-1售票3
Thread-1售票2
Thread-1售票1
Thread-0售票6
Thread-2售票7
实现方式和继承方式有什么区别?
实现方式的好处:避免了单继承的局限性。在定义线程时,建议使用实现方式。
继承Thread:线程代码存放在Thread子类run方法中。
实现Runnable,线程代码存放在接口子类的run方法中。
多线程的安全隐患:(需要多加注意)
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没
有执行完,另一个线程参与进来执行。导致了共享数据的错误。
解决办法:对于多条操作共享数据的语句,只能让一个线程都执行完,其他的线程才能
参与执行。
Java对于多线程的安全问题提供了专业的解决方案,就是同步代码块。
synchronized(对象){
需要被同步的代码
}
如何找到问题 找到该同步哪些代码块?
1.明确哪些代码是需要多线程运行代码。
2.明确共享数据。
3.明确多线程运行代码中的哪些语句是操作共享数据的。
同步函数---同步函数用的是哪一个锁?
---同步函数用的锁就是this,函数需要被对象调用,那么函数都有一个所属的对象的引用。就是this.
所以同步函数使用的锁是this.
验证示例:通过以下的程序来验证。需求为 --通过两个线程来买票一个线程在同步代码块中,另一
个线程在同步函数中。都在执行买票动作。
代码如下:
/** * 售票类 有两个线程 同步代码块和同步函数 注意同步函数的锁为this而同步代码块的锁要达到 * 安全的目的则必须与同步函数的锁相同 也就是this * @author bing * */ public class Ticket implements Runnable{ private static int tickt=10; Object obj = new Object(); boolean flag=true; public void run()//同步代码块 { if(flag) { while(true) { //synchronized (obj)//注意这里的锁是obj,这样与同步函数用的不是一个锁,所以会产生数据异常,比如售票时卖到0号票.. //synchronized (this)//这里用this锁,与同步函数用同一个锁 { if(tickt>0) { try{Thread.sleep(10);}catch(Exception e){} System.out.println("同步代码块进程---"+Thread.currentThread().getName() +"-----sale:"+tickt--); } } } }else { while(true) { show(); } } } public static synchronized void show()//同步函数 { if(tickt>0) { try{Thread.sleep(10);}catch(Exception e){} System.out.println("同步函数进程---"+Thread.currentThread().getName() +"-----sale:"+tickt--); } } } //测试程序代码如下: /** * 验证线程锁,同步代码块和同步函数的控制通过flag旗帜来控制 * 注意主线程需要sleep一段时间 * @author bing * */ public class TestMain { public static void main(String[] args) throws InterruptedException { Ticket aTicket=new Ticket(); Thread t1=new Thread(aTicket); Thread t2=new Thread(aTicket); t1.start();//线程t1开始启动,启动的时间短,非常有可能被main主线程抢走执行权,所以让主线程休息一会 Thread.currentThread().sleep(10); aTicket.flag=false;System.out.println("-------------------------------------"); t2.start(); } }
输出结果如下:
-------------------------------------
同步代码块进程---Thread-0-----sale:10
同步函数进程---Thread-1-----sale:8
同步代码块进程---Thread-0-----sale:9
同步代码块进程---Thread-0-----sale:7
同步函数进程---Thread-1-----sale:7
同步函数进程---Thread-1-----sale:6
同步代码块进程---Thread-0-----sale:5
同步代码块进程---Thread-0-----sale:4
同步函数进程---Thread-1-----sale:3
同步函数进程---Thread-1-----sale:2
同步代码块进程---Thread-0-----sale:1------------可以观察输出有序且线程Thread1和Thread0交替执行
那么上面的例子验证了同步函数的锁是this 那么静态同步函数的锁是什么呢?
--首先不是this,因为在static 中没有this.静态进内存是,内存中没有本类对象,但是一定有该类对应
的字节码文件对象。--->也就是类名.class 该对象类型是Class 即在静态函数中同步锁为类名.class
示例代码如下:
package DemoSynchronized_Static; /** * 售票类 有两个线程 同步代码块和同步函数 注意同步函数的锁为this而同步代码块的锁要达到 * 安全的目的则必须与同步函数的锁相同 也就是this * @author bing * */ public class Ticket implements Runnable{ private static int tickt=10; Object obj = new Object(); boolean flag=true; public void run()//同步代码块 { if(flag) { while(true) { //synchronized (obj)//注意这里的锁是obj,这样与同步函数用的不是一个锁,所以会产生数据异常,比如售票时卖到0号票.. synchronized (Ticket.class)//这里用的是所在类的字节码文件对象,即类名.calss { if(tickt>0) { try{Thread.sleep(100);}catch(Exception e){} System.out.println("同步代码块进程---"+Thread.currentThread().getName() +"-----sale:"+tickt--); } } } }else { while(true) { show();//调用同步函数 } } } public static synchronized void show()//同步函数 { if(tickt>0) { try{Thread.sleep(10);}catch(Exception e){} System.out.println("同步函数进程---"+Thread.currentThread().getName() +"-----sale:"+tickt--); } } } //测试程序代码如下: /** * 验证线程锁,同步代码块和同步函数的控制通过flag旗帜来控制 * 注意主线程需要sleep一段时间 * @author bing * */ public class TestMain { public static void main(String[] args) throws InterruptedException { Ticket aTicket=new Ticket(); Thread t1=new Thread(aTicket); Thread t2=new Thread(aTicket); t1.start();//线程t1开始启动,启动的时间短,非常有可能被main主线程抢走执行权,所以让主线程休息一会 Thread.currentThread().sleep(100); aTicket.flag=false;System.out.println("-------------------------------------"); t2.start(); System.out.println("+++++++++++++++++++"); } }
输出如下:
-------------------------------------
同步代码块进程---Thread-0-----sale:10
+++++++++++++++++++
同步代码块进程---Thread-0-----sale:9
同步代码块进程---Thread-0-----sale:8
同步函数进程---Thread-1-----sale:7
同步函数进程---Thread-1-----sale:6
同步函数进程---Thread-1-----sale:5
同步函数进程---Thread-1-----sale:4
同步函数进程---Thread-1-----sale:3
同步函数进程---Thread-1-----sale:2
同步函数进程---Thread-1-----sale:1
----------------------------------------小结:静态的同步方法:使用的锁是该方法所在类的字节码
文件对象。类名.class
多线程的单例设计模式
1懒汉式
package Single_Design_Pattern_Multithreading; /** * 懒汉式单例模式 * 本类描述怎么在懒汉单例模式中运用多线程同步锁 * 方法一:可以直接将getInstance()方法声明为synchronized的 但是这样做 * 会浪费资源 每次调用getInstance()方法时都要判断锁,所以可以按以下的方式解决 * 也就是方法二--两次判断,延时加载 * * @author bing * */ public class LazyMan { private static LazyMan lm=null; private LazyMan(){} public static LazyMan getInstance() {/**这里的处理方式为延迟加载 比如有线程A 进入 进入aaa判断 再进入bbb判断这时线程B启动 由aaa判断后 到 bbb此时 有线程锁的存在 线程B没有访问权限 再次回到线程A 线程A继续执行到 ccc这时判断后建立对象lm 线程A解锁 这时线程B再执行 判断ccc结果对象lm已经 存在了 所以不再执行。线程C启动 判断aaa时就lm已经存在了 所以也不会再继续 这样 就避免了对 线程锁的重复判断 节省资源---这种方式叫做两次判断,延时加载 */ if(lm==null)//aaa { synchronized (LazyMan.class)//bbb { if(lm==null)//cccc { lm=new LazyMan(); } } } return lm; } } //饿汉式 package Single_Design_Pattern_Multithreading; /** * 饿汉式单例设计模式,不存在共享数据异常的问题 所以不用锁 * @author bing * */ public class HungryMan_Single { private static final HungryMan_Single hs=new HungryMan_Single(); private HungryMan_Single(){} public static HungryMan_Single getInstance() { return hs; } }
====================================================================================================
死锁的问题 用以下的示例来说明死锁的问题
package Single_Design_Pattern_Multithreading; /** * 懒汉式单例模式 * 本类描述怎么在懒汉单例模式中运用多线程同步锁 * 方法一:可以直接将getInstance()方法声明为synchronized的 但是这样做 * 会浪费资源 每次调用getInstance()方法时都要判断锁,所以可以按以下的方式解决 * 也就是方法二--两次判断,延时加载 * * @author bing * */ public class LazyMan { private static LazyMan lm=null; private LazyMan(){} public static LazyMan getInstance() {/**这里的处理方式为延迟加载 比如有线程A 进入 进入aaa判断 再进入bbb判断这时线程B启动 由aaa判断后 到 bbb此时 有线程锁的存在 线程B没有访问权限 再次回到线程A 线程A继续执行到 ccc这时判断后建立对象lm 线程A解锁 这时线程B再执行 判断ccc结果对象lm已经 存在了 所以不再执行。线程C启动 判断aaa时就lm已经存在了 所以也不会再继续 这样 就避免了对 线程锁的重复判断 节省资源---这种方式叫做两次判断,延时加载 */ if(lm==null)//aaa { synchronized (LazyMan.class)//bbb { if(lm==null)//cccc { lm=new LazyMan(); } } } return lm; } } //饿汉式 package Single_Design_Pattern_Multithreading; /** * 饿汉式单例设计模式,不存在共享数据异常的问题 所以不用锁 * @author bing * */ public class HungryMan_Single { private static final HungryMan_Single hs=new HungryMan_Single(); private HungryMan_Single(){} public static HungryMan_Single getInstance() { return hs; } } ==================================================================================================== 死锁的问题 用以下的示例来说明死锁的问题 package DemoSynchronized; public class Ticket implements Runnable{ private int tickt=100; Object obj = new Object(); public boolean flag=true; public void run()//同步代码块 { if(flag) { while(true) { synchronized (obj)//这里是同步代码块中有同步函数,注意锁是不同的 { show(); } } }else { while(true) { show(); } } } public synchronized void show()//同步函数这里的锁是this { //注意这里是同步函数里面有同步代码块而且两者的锁是不同的 synchronized(obj)//这里的锁是obj { if(tickt>0) { try{Thread.sleep(10);}catch(Exception e){} System.out.println("同步函数进程---"+Thread.currentThread().getName() +"-----sale:"+tickt--); } } } } //测试程序代码如下: package DeadLock; import DemoSynchronized.Ticket; /** * 死锁: * 同步中嵌套同步 * @author bing * */ public class TestMain { public static void main(String[] args) throws InterruptedException { Ticket aTicket=new Ticket(); Thread t1=new Thread(aTicket); Thread t2=new Thread(aTicket); t1.start();//线程t1开始启动,启动的时间短,非常有可能被main主线程抢走执行权,所以让主线程休息一会 Thread.currentThread().sleep(16); aTicket.flag=false;System.out.println("-------------------------------------"); t2.start(); } } //输出结果:输出过程中卡住 因为两个锁互相争夺
===========================================================================================
线程间通信的安全问题:
线程A对资源R进行加操作线程B对资源R进行减操作 如何同步 如何同步时安全操作 示例代码如下:
package DemoSynchronized; public class Ticket implements Runnable{ private int tickt=100; Object obj = new Object(); public boolean flag=true; public void run()//同步代码块 { if(flag) { while(true) { synchronized (obj)//这里是同步代码块中有同步函数,注意锁是不同的 { show(); } } }else { while(true) { show(); } } } public synchronized void show()//同步函数这里的锁是this { //注意这里是同步函数里面有同步代码块而且两者的锁是不同的 synchronized(obj)//这里的锁是obj { if(tickt>0) { try{Thread.sleep(10);}catch(Exception e){} System.out.println("同步函数进程---"+Thread.currentThread().getName() +"-----sale:"+tickt--); } } } } //测试程序代码如下: package DeadLock; import DemoSynchronized.Ticket; /** * 死锁: * 同步中嵌套同步 * @author bing * */ public class TestMain { public static void main(String[] args) throws InterruptedException { Ticket aTicket=new Ticket(); Thread t1=new Thread(aTicket); Thread t2=new Thread(aTicket); t1.start();//线程t1开始启动,启动的时间短,非常有可能被main主线程抢走执行权,所以让主线程休息一会 Thread.currentThread().sleep(16); aTicket.flag=false;System.out.println("-------------------------------------"); t2.start(); } } //输出结果:输出过程中卡住 因为两个锁互相争夺 =========================================================================================== 线程间通信的安全问题: 线程A对资源R进行加操作线程B对资源R进行减操作 如何同步 如何同步时安全操作 示例代码如下: package InOut_Demo; /** * 线程间通信的示例代码 * 线程间通信,其实就是多个线程在操作同一个资源,但是操作的动作是不同的 * @author bing * */ public class TestMain { public static void main(String[] args) { Res r =new Res(); Input in=new Input(r); OutPut ou = new OutPut(r); Thread t1=new Thread(in); Thread t2=new Thread(ou); t1.start(); t2.start(); } } //资源R代码如下 package InOut_Demo; /** * 资源Res,Input和OutPut的操作对象 * @author bing * */ public class Res { private String name; private String sex; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } } //写操作代码如下 package InOut_Demo; /** * 对资源R进行写操作 * @author bing * */ public class Input implements Runnable{ private Res r; Input(Res r) {this.r=r;} public void run() { int x=0; while(true) { synchronized (r) { if(x==0){r.setName("mike");r.setSex("man");} if(x==1){r.setName("王语嫣");r.setSex("女女");} } x=(x+1)%2; } } } //输出操作代码如下: package InOut_Demo; /** * 输出资源R的内容 * @author bing * */ public class OutPut implements Runnable{ private Res r; public OutPut(Res r){this.r=r;} public void run() { while(true) { synchronized(r){System.out.println(r.getName()+r.getSex());} } } } //测试程序代码如下 package InOut_Demo; /** * 线程间通信的示例代码 * 线程间通信,其实就是多个线程在操作同一个资源,但是操作的动作是不同的 * @author bing * */ public class TestMain { public static void main(String[] args) { Res r =new Res(); Input in=new Input(r); OutPut ou = new OutPut(r); Thread t1=new Thread(in); Thread t2=new Thread(ou); t1.start(); t2.start(); } }
经过测试 输出有序 说明线程间通信的安全问题解决
输出如下:
mikeman
mikeman
mikeman
王语嫣女女
王语嫣女女
王语嫣女女
==========================================================================================
等待唤醒机制
---接上个例子 要求 男女输出交替进行 也就是 写--男--输出--男---写--女---输出---女
资源类如下:
public class Res { public String name; public String sex; public boolean flag=false;//旗帜变量用来控制是写资源还是输出资源 } //写方法类如下: public class Input implements Runnable{ public Res r; private int x=0; Input(Res r){this.r=r;} public void run() { while(true) { synchronized(r) { //如果旗帜为真,则等待 等待需要与锁一致 if(r.flag){try{r.wait();}catch(Exception e){System.out.println("Input wait 异常");}} //如果旗帜为假 则写入资源 if(x==0){r.name="mike";r.sex="man";} else{r.name="周芷若";r.sex="女女";} x=(x+1)%2; //写完资源后 资源设置为真 r.flag = true; r.notify(); //唤醒,需要与锁一致 } } } } //输出资源类如下: package Wait_WakeUp_right; public class OutPut implements Runnable{ private Res r; OutPut(Res r){this.r=r;} public void run() { while(true) { synchronized(r) { if(!r.flag)//如果资源为假 则等待 { try{r.wait();}catch(Exception e){System.out.println("output wait 出错");} } //如果资源为真 则输出资源 System.out.println(r.name+"。。。。"+r.sex); r.flag = false;//输出后 资源设置为假 r.notify();//唤醒 } } } } //测试程序如下: public class TestMain { /**wait; * notify; * notifyALl; 都在同步中使用,因为要对持有监视器(锁)的线程操作。 * 所以都要使用在同步中,因为只有同步才有锁。 * 为什么这些操作线程的方法要定义在object类中? * 因为这些方法在操作同步中线程时,都必须要标识他们所操作的线程持有的锁 * 只有同一个锁上的被等待线程可以被同一个锁上的线程notify唤醒,不可以对不同 * 锁中的线程进行唤醒 * * 也即是说等待和唤醒必须是同一把锁。 * 而锁可以是任意对象,可以被任意对象调用的方法就定义在Object中 * * @param args */ public static void main(String[] args) { Res r=new Res(); Input in=new Input(r); OutPut ou=new OutPut(r); Thread t1=new Thread(in); Thread t2=new Thread(ou); t1.start(); t2.start(); } }
输出结果如下:
mike。。。。man
周芷若。。。。女女
mike。。。。man
周芷若。。。。女女
mike。。。。man
周芷若。。。。女女
mike。。。。man
周芷若。。。。女女
mike。。。。man
周芷若。。。。女女
mike。。。。man-----------结果有序 而且交替输出
相关推荐
这篇学习笔记将深入探讨Java多线程的核心概念、实现方式以及相关工具的使用。 一、多线程基础 1. 线程与进程:在操作系统中,进程是资源分配的基本单位,而线程是程序执行的基本单位。每个进程至少有一个主线程,...
### Java多线程学习笔记 #### 一、线程的基本概念 在计算机科学中,**线程**(Thread)是程序执行流的最小单位。一个标准的程序只能做一件事情,而通过多线程技术,可以让程序同时处理多个任务。在Java中,线程是...
【UNIX多线程学习笔记】 在UNIX操作系统中,多线程是一种重要的编程模型,它允许多个执行流在单个进程中并发运行。多线程带来了许多优势,包括提高应用程序响应速度,充分利用多CPU系统的资源,以及优化程序结构,...
这份"C++多线程学习笔记1"涵盖了基础到进阶的多线程概念,旨在帮助初学者快速掌握这一关键技能。 首先,C++11引入了对多线程的支持,引入了`<thread>`库,使得创建和管理线程变得简单。创建一个新的线程可以使用`...
java基础:多线程学习笔记
多线程 学习笔记.md
java学习笔记2(多线程)java学习笔记2(多线程)
本篇学习笔记主要涵盖了线程基础、threading模块的使用以及线程同步控制。 首先,线程是操作系统分配CPU执行时间的基本单位,一个进程可以包含多个线程。在Python3中,线程的状态主要包括新建、就绪、运行、死亡、...
这篇文档和对应的源代码 博文链接:https://interper56-sohu-com.iteye.com/blog/172303
在计算机科学中,多线程是一种编程模型,允许一个应用程序同时执行多个任务。这提高了系统的效率,特别是对于处理大量并发操作的情况。以下是一些关于多线程的重要知识点,特别是关于在Linux和Windows环境下创建和...
在多线程编程中,进程和线程是两个核心概念。进程是操作系统资源分配的基本单位,每个独立执行的程序都对应一个进程。而线程则是程序执行的最小单元,是进程内部的一条执行路径。多线程是指在一个应用程序中存在多个...
在这个学习笔记中,主要讨论了Java中的线程同步机制,包括volatile关键字、synchronized以及Lock接口,特别是ReentrantLock的使用。 首先,对于线程1和线程2的疑惑,调试(debug)模式并不能改变线程的执行顺序。...
Java多线程是Java编程中的核心概念,它允许并发执行多个任务,提高程序的执行效率。以下是关于Java多线程的详细知识点: 1. **创建线程** - **继承Thread类**:创建一个新的类,该类继承自Thread类,并重写run()...
Java多线程学习笔记之自定义线程池 本篇文章主要介绍了Java多线程学习笔记之自定义线程池,通过深入了解ThreadPoolExecutor这个核心类,我们可以自定义线程池,满足不同的线程池需求。 Java多线程学习笔记之自定义...
Java 线程学习笔记 Java 线程创建有两种方法: 1. 继承 Thread 类,重写 run 方法:通过继承 Thread 类并重写 run 方法来创建线程,这种方法可以使线程具有自己的执行逻辑。 2. 实现 Runnable 接口:通过实现 ...
Java并发编程学习笔记,研究JAVA并发多线程编程的一本教程,使用并发技术可以开发出并行算法,充分利用多处理器的计算能力,避免硬件资源浪费。目前,在JAVA并发编程方面的论述系统且内容详实的技术资料不太多,Java...