Java多线程基础之线程同步与锁定
线程同步与锁定
由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突严重的这个问题,Java语言提供了专门的机制以解决这类冲突,有效避免了同一个数据对象被多个线程同时访问,由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们针对方法提出一整套机制,这套机制就是sychronized关键字;
sychronized:
HashTable 和StringBuffer底部均有使用 同步代码或同步块;
* 一:同步块(sychronized(this|.class|引用类型)
package Tread; public class Demo { public static void main(String[] args) { Web12306 wb = new Web12306(); Thread t1 = new Thread(wb, "黄牛"); Thread t2 = new Thread(wb, "黄牛2"); Thread t3 = new Thread(wb, "工程师"); t1.start(); t2.start(); t3.start(); } } class Web12306 implements Runnable { int num = 10; private boolean flag = true; @Override public void run() { while (true) { test1(); } } // 线程不安全 public void test1() { if(num<=0) { flag=false; return; } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--+"票"); } // 同步方法 public synchronized void test2() { if(num<=0) { flag=false; return; } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--+"票"); } //锁定this对象 public void test2() { synchronized (this) { if (num <= 0) { flag = false; return; } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "抢到了" + num-- + "票"); } } }
/ 锁定范围不正确与锁定资源不正确,均会造成线程不安全,错误代码实例:
public void test2() { if (num <= 0) { flag = false; return; } synchronized (this) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "抢到了" + num-- + "票"); } } public void test2() { synchronized ((Integer)num) { if (num <= 0) { flag = false; return; } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "抢到了" + num-- + "票"); } }
锁定.class对象
单例设计模式
单例设计模式: 确保一个类只有一个对象
* 懒汉式
* 1.构造器私有化,避免外部直接创建对象
* 2.声明一个私有的静态变量
* 3.创建一个对外公共的静态方法访问该变量,如果该变量没有对象,创建该对象
package Tread; //单例设计模式:确保一个类只有一个对象 public class SynDemo { public static void main(String[] args) { Jvmthread jvm1 = new Jvmthread(100); Jvmthread jvm2 = new Jvmthread(500); new Thread(jvm1).start(); new Thread(jvm2).start(); } } class Jvmthread implements Runnable { private long time; public Jvmthread(long time) { super(); this.time = time; } @Override public void run() { System.out.println(Thread.currentThread().getName() + "------>" + Jvm.getInstance(time)); } } /** * 单例设计模式: 确保一个类只有一个对象 * 懒汉式 * 1.构造器私有化,避免外部直接创建对象 * 2.声明一个私有的静态变量 * 3.创建一个对外公共的静态方法访问该变量,如果该变量没有对象,创建该对象 */ class Jvm { private static Jvm instance = null; private Jvm() { } // 两种方法,一种锁住方法,一种锁住.class文件,注意:静态方法不能存在this对象,所以不能锁住this public static Jvm getInstance(long time) { if (null == instance) { try { Thread.sleep(time); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } instance = new Jvm(); } return instance; } // 提高效率:双重检查 已经有对象,其他线程不用再等待锁 public static Jvm getInstance2(long time) { if (null == instance) { synchronized (Jvm.class) { if (null == instance) { try { Thread.sleep(time); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } instance = new Jvm(); } } } return instance; } }
饿汉式
* 1.构造器私有化,避免外部直接创建对象
* 2.声明一个私有的静态变量,同时创建该对象
* 3.创建一个对外公共的静态方法访问该变量,如果该变量没有对象,创建该对象
package Tread; /***** * 饿汉式 * 1.构造器私有化,避免外部直接创建对象 * 2.声明一个私有的静态变量,同时创建该对象 * 3.创建一个对外公共的静态方法访问该变量,如果该变量没有对象,创建该对象 */ public class MyJvM { private static MyJvM instance=new MyJvM(); private MyJvM () { } public static MyJvM getInstance(long time) { return instance; } } /**优化写法 * 类在使用的时候才加载,延缓加载时间 * */ class MyJvM3 { private static class Jvmholder { private static MyJvM3 instance = new MyJvM3(); } private MyJvM3() { } public static MyJvM3 getInstance() { return Jvmholder.instance; } }