注意:锁一般放在数据容器内
1,方法一:同步代码块:
1,格式:synchronized
synchronized (要锁的对象){
操作共享数据的代码;
}
2,作用:
3,原理:
每次只能一个线程获取锁进入,执行完毕以后自动解锁,其他线程才可以进来执行。
4,锁对象的要求:
原则上:锁对象必须是同一个对象。
语法上:任意对象都可以作为锁。
5,使用范例:(实际,就是给代码块内的代码加锁)
没锁的线程是无法执行代码块内的代码的;只有上锁被标记的线程才能进入;
//Window类:
public class Window extends Thread {
//设置共享的变量
private static int tickerNumber = 100;
***************************************************
//一般开发中会专门使用一个对象作为锁;
//并且要定义为共享对象;
//创建锁对象
private static Object obj = new Object();
//设置线程名称,并指向父类Thread的对应构造器;
public Window(String name) {
super(name);
}
@Override
public void run() {
while (true) {
******************************************
//使用同步代码块,锁住对象;解决线程安全问题
//注意不要把循环锁上,不然只执行一个对象;
synchronized (obj) {
if (tickerNumber > 0) {
try {
Thread.sleep(100);
}
//捕获sleep异常,这里的代码不需要异常处理代码
catch (InterruptedException e) {
}
tickerNumber--;
System.out.println(getName() + "剩余:" + tickerNumber);
} else {
break;
}
}
//让带锁的对象,休眠一会,避免一个线程对象一直抢锁;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//main方法类:
public class Text06 {
public static void main(String[] args) {
//创建对象时进行设置线程名称:
Window window1 = new Window("window1");
Window window2 = new Window("window2");
Window window3 = new Window("window3");
//以线程对象启动对应线程
window1.start();
window2.start();
window3.start();
}
}
2,方式二:同步方法:
1,格式:synchronized void
//普通方法:(实例方法)
//使用this作为锁;
public synchronized void 方法名() {
// 操作共享资源的代码
}
//静态方法:
//使用 类名.class 作为锁;
public static synchronized void 方法名() {
// 操作共享资源的代码
}
2,作用:
3,原理:(同同步代码块)
每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行。
4,底层原理:
1. 同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法。
1. 如果方法是**实例方法**:同步方法默认用**this**作为的锁对象。
1. 如果方法是**静态方法**:同步方法默认用**类名.class**作为的锁对象。
5,使用范例:
//Window类:
public class Window extends Thread {
private static int tickNumbers = 100;
public Window(String name) {
super(name);
}
@Override
public void run() {
//循环卖票:
while (true) {
//调用同步方法;(静态的)
sales();
//设置线程对象的延迟值,避免一个对象一直抢锁;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tickNumbers == 0) {
break;
}
}
}
public static synchronized void sales() {
// System.out.println(Window.class);
//静态对象使用 类名.class 作为对象;
if (tickNumbers > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
tickNumbers--;
//注意:这里的获取线程名称要先获取当前线程对象,因为是静态的,会有多个线程对象,如不先获得,系统无法识别出线程名
System.out.println(Thread.currentThread().getName() + "\t剩余:" + tickNumbers);
}
}
}
//main方法类:
public class Text07 {
public static void main(String[] args) {
//创建线程对象并设置线程名;
Window window1 = new Window("window1");
Window window2 = new Window("window2");
Window window3 = new Window("window3");
window1.start();
window2.start();
window3.start();
}
}
3,方式三:Lock锁:
1,Lock的API :ReentrantLock()
2,Lock的介绍:
1. u为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock,更加灵活、方便。
1. Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作。
1. Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来构建Lock锁对象。
3,格式:(lock)
lock.lock();
try {
操作共享资源代码
} finally {
lock.unlock();
}
4,使用范例:
//main:
public class Text01 {
public static void main(String[] args) {
MyLock myLock1 = new MyLock();
MyLock myLock2 = new MyLock();
myLock1.start();
myLock2.start();
}
}
//mylock类:
public class MyLock extends Thread{
//创建lock
private static Lock lock=new ReentrantLock();
@Override
public void run() {
try {
//获得锁:
lock.lock();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放锁:
//记得在finally里面释放,否则进程一直被一个线程对象拿着锁执行;
lock.unlock();
}
}
}