线程2-线程同步
1、线程安全问题原因?
由于一个线程在操作一个共享数据时,未执行完毕的情况下,另外的线程参与进来,导致共享数据存在安全问题
2、如何解决线程安全问题?
一个线程操作完共享数据以后,其他线程才可以操作共享数据
3、java使用线程同步机制实现,线程安全
方式一:同步代码块
synchronized(Object obj){ //可以是任何一个对象,对象不能为线程私有
//操作共享数据的代码块
}
(1)共享数据
(2)同步监视器(上面的obj参数),对象锁,由一个对象充当,哪个类获取到对象锁,就执行同步代码
方式二:
(1)将需要同步的代码块,放到一个方法,对方法加同步
(2)同步方法,对象锁默认this
4、同步要注意的:
(1)synchronized修饰非静态方法,里面是当前类的对象
(2)synchronized静态方法的锁是当前类的字节码文件对象 类名.class
(3)多个线程必须用同一把对象锁,否则锁无效,非static锁定实列,static 锁定类
5、例1,继承方式实现多窗口卖票,非线程安全
package com.thread.test; /** * * 继承方式:模拟火车站售票 *存在线程安全问题 */ class Window extends Thread{ static int ticket = 100; static int k = 0 ; @Override public void run(){ while(true){ if(ticket > 0){ System.out.println(Thread.currentThread().getName()+"售票号码:"+ticket); k++; ticket --; }else{ System.out.println("All "+k); break; } } } } public class TestWindowExtends{ public static void main(String[] args) { Window w1 = new Window(); Window w2 = new Window(); Window w3 = new Window(); w1.setName("窗口1"); w2.setName("窗口2"); w3.setName("窗口3"); w1.start(); w2.start(); w3.start(); } }
例2,继承方式多窗口卖票,线程安全
package com.thread.test; /** * * 继承方式:模拟火车站售票 *存在线程安全问题 */ class Window3 extends Thread{ static int ticket = 100; static int k = 0 ; static Object obj = new Object(); @Override public void run(){ while(true){ synchronized(obj){//注意和实现implements方法对象锁的不同 if(ticket > 0){ System.out.println(Thread.currentThread().getName()+"售票号码:"+ticket); k++; ticket --; }else{ System.out.println("All "+k); break; } } } } } public class TestWindowExtends同步代码块{ public static void main(String[] args) { Window w1 = new Window(); Window w2 = new Window(); Window w3 = new Window(); w1.setName("窗口1"); w2.setName("窗口2"); w3.setName("窗口3"); w1.start(); w2.start(); w3.start(); } }
例3、实现Runnable接口实现多窗口卖票,线程安全,同步代码块
package com.thread.test; /** * 1、线程安全问题原因? * 由于一个线程在操作一个共享数据时,未执行完毕的情况下,另外的线程参与进来,导致共享数据存在安全问题 * 2、如何解决线程安全问题? * 一个线程操作完共享数据以后,其他线程才可以操作共享数据 * 3、java使用线程同步机制实现,线程安全 * 方式一:同步代码块 * synchronized(Object obj){ //可以是任何一个对象,对象不能为线程私有 * //操作共享数据的代码块 * } * (1)共享数据 * (2)同步监视器(上面的obj参数),对象锁,由一个对象充当,哪个类获取到对象锁,就执行同步代码 * 方式二: * (1)将需要同步的代码块,放到一个方法,对方法加同步 * (2)同步方法,对象锁默认this */ class Window2 implements Runnable{ int ticket = 100; int count = 0; @Override public void run() { //this表示当前对象,因为,在下面的main方法中,只创建了一次Windows2对象,所以,this代表的当前对象是三个线程共享的,对象锁唯一。所以可以用this //而在继承方式中,对象锁不能用this,因为,在main方法中Windows对象被创建了3次,this对应三个对象,对象不唯一,所以不能用this //多个线程用同一把对象锁 while(true){ synchronized(this){//不要写到while外面,while外面,就会只有线程1执行代码 if(ticket >0){ System.out.println("线程"+Thread.currentThread().getName()+"出售票号:"+ticket); ticket --; count ++; }else{ System.out.println("Aall "+count); break; } } } } } public class TestWindowImplements同步代码块 { public static void main(String[] args) { //继承方式只需创建一个Window对象 Window1 window = new Window1(); //三个线程操作同一个对象 Thread t1 = new Thread(window); t1.setName("t1"); Thread t2 = new Thread(window); t2.setName("t2"); Thread t3 = new Thread(window); t3.setName("t3"); t1.start(); t2.start(); t3.start(); } }
例4、实现Runnable接口实现多窗口卖票,线程安全,同步方法
package com.thread.test; class Window4 implements Runnable{ int ticket = 100; int count = 0; @Override public void run() { while(true){ sell(); } } //对象锁默认this public synchronized void sell(){ if(ticket >0){ System.out.println("线程"+Thread.currentThread().getName()+"出售票号:"+ticket); ticket --; count ++; } } } public class TestWindowImplements同步方法 { public static void main(String[] args) { //继承方式只需创建一个Window对象 Window4 window = new Window4(); //三个线程操作同一个对象 //由于三个线程会同时操作ticket这一变量,所以会出现同步问题,所以要加同步 Thread t1 = new Thread(window); t1.setName("t1"); Thread t2 = new Thread(window); t2.setName("t2"); Thread t3 = new Thread(window); t3.setName("t3"); t1.start(); t2.start(); t3.start(); } }
需要注意的事项都写在注释里