package study.thread;
public class ThreadSafeImpl implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (true){
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("售票员"+Thread.currentThread().getName()+"正在卖第"+ticket+"张票……");
ticket--;
}else {
break;
}
}
}
}
package study.thread;
public class ThreadSafeTest {
public static void main(String[] args) {
ThreadSafeImpl threadSafe = new ThreadSafeImpl();
//多线程干一件事
new Thread(threadSafe).start();
new Thread(threadSafe).start();
new Thread(threadSafe).start();
}
}
(发现票卖重了,而且卖了不存在的票,这就有问题了!)
售票员Thread-2正在卖第100张票……
售票员Thread-0正在卖第100张票……
售票员Thread-1正在卖第100张票……
...
售票员Thread-2正在卖第1张票……
售票员Thread-0正在卖第1张票……
售票员Thread-1正在卖第-1张票……
①同步代码块;
②同步方法;
③锁机制;
synchronized(锁对象){
可能出现线程安全问题的代码(从访问到共享数据的代码开始)
}
前面的线程开始执行后回去拿取堆内存中的锁对象,后面的线程开始执行后再去拿锁对象就拿不到了,所以无法继续执行,需要等待前面的线程执行完毕,归还锁对象,后面的线程才能拿到锁对象,继续执行;
①通过代码块中的锁对象,可以使用任意的对象;
②但必须保证多个线程使用的所对象是同一个;
③所对象的作用:把同步代码块锁住,只让一个线程在同步代码块中执行;
加入同步代码块后的多线程类:
package study.thread;
public class ThreadSafeImpl implements Runnable {
private int ticket = 100;
private final Object object = new Object();
@Override
public void run() {
while (true){
synchronized (object){
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("售票员"+Thread.currentThread().getName()+"正在卖第"+ticket+"张票……");
ticket--;
}else {
break;
}
}
}
}
}
测试类:
package study.thread;
public class ThreadSafeTest {
public static void main(String[] args) {
ThreadSafeImpl threadSafe = new ThreadSafeImpl();
//多线程干一件事
new Thread(threadSafe).start();
new Thread(threadSafe).start();
new Thread(threadSafe).start();
}
}
运行结果(截取部分):
售票员Thread-0正在卖第100张票……
售票员Thread-0正在卖第99张票……
售票员Thread-2正在卖第98张票……
售票员Thread-1正在卖第97张票……
售票员Thread-1正在卖第96张票……
售票员Thread-1正在卖第95张票……
①把访问了共享数据的代码抽取出来,放到一个方法中;
②在方法上添加synchronized修饰符;
访问修饰符 synchronized 返回值类型 方法名(参数列表){
//方法体
}
package study.thread;
public class ThreadSafeImpl implements Runnable {
private int ticket = 100;
@Override
public void run() {
printMessage();
}
private synchronized void printMessage(){
while (true){
if(ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("售票员"+Thread.currentThread().getName()+"正在卖第"+ticket+"张票……");
ticket--;
}else {
break;
}
}
}
}
但经测试发现,此方法一个会出现一个线程将票卖完的情况,我自己猜测也许是因为while循环放进了锁住的方法中。
package study.thread;
public class ThreadSafeImpl implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (true){
printMessage();
if(ticket==0){
break;
}
}
}
private synchronized void printMessage(){
if(ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("售票员"+Thread.currentThread().getName()+"正在卖第"+ticket+"张票……");
ticket--;
}
}
}
这样写确实避免了上述情况。
同步方法也会把方法内部的代码锁住,只让一个线程执行,实际上所的对象是new RunnableImpl(),也就是this(自身)
静态同步方法就是在一般的同步方法synchronized前加上static;
此时的所对象不是this(本身),而是本类的class属性-->class文件对象(反射)
Lock接口实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作;
①在成员位置创建一个;
②在有可能出现安全问题的代码前,调用获取锁的方法(闭锁);
③在有可能出现安全问题的代码后,调用释放锁的方法(开锁);
package study.thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadSafeImpl implements Runnable {
private int ticket = 100;
//1、在成员位置创建一个
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true){
//2、在有可能出现安全问题的代码前,调用获取锁的方法(闭锁)
lock.lock();
if(ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("售票员"+Thread.currentThread().getName()+"正在卖第"+ticket+"张票……");
ticket--;
}else {
break;
}
//3、在有可能出现安全问题的代码后,调用释放锁的方法(开锁)
lock.unlock();
}
}
}
package study.thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadSafeImpl implements Runnable {
private int ticket = 100;
//1、在成员位置创建一个
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true){
//2、在有可能出现安全问题的代码前,调用获取锁的方法(闭锁)
lock.lock();
if(ticket>0){
try {
Thread.sleep(10);
System.out.println("售票员"+Thread.currentThread().getName()+"正在卖第"+ticket+"张票……");
ticket--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//3、在有可能出现安全问题的代码后,调用释放锁的方法(开锁)
lock.unlock();
}
}else {
break;
}
}
}
}