线程同步与锁定
由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突严重的这个问题,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;
}
}