Java-多线程详解(二)
2019-08-19 本文已影响0人
一只洁_
线程的同步
1.线程同步问题的引出
下面模拟一个简单的卖票程序,两个线程,卖10张票
public class MyClass {
public static void main(String[] args){
Ticket ticket1 = new Ticket("线程A");
Thread t1 = new Thread(ticket1);
Ticket ticket2 = new Ticket("线程B");
Thread t2 = new Thread(ticket2);
t1.start();
t2.start();
}
}
//用于卖票的任务
class Ticket implements Runnable{
//定义所有车票的数量
public static int num = 10;
String name;
public Ticket(String name){
this.name = name;
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
//判断有没有票
if (num > 0) {
System.out.println(name + "出票:" + num--);
}
}
}
}
运行结果:
线程B出票:10
线程A出票:9
线程B出票:8
线程A出票:7
线程A出票:5
线程A出票:4
线程A出票:3
线程B出票:6
线程B出票:1
线程A出票:2
这时我们发现,操作的结果A、B两个线程余票没有同步。那么,到底是如何造成这种不同步的呢?
卖票的过程分为两个步骤:
第一步:判断是否还有剩余的票数;
第二步:票数减一
2.线程同步问题的解决
(1)同步代码块,通过synchronized关键词解决。同步代码块格式:
synchronized(同步对象){
需要同步的代码
}
在进行同步的操作时必须设置一个要同步的对象,任意对象我们用Object设置
class Ticket implements Runnable{
//定义所有车票的数量
public static int num = 10;
String name;
public Ticket(String name){
this.name = name;
}
Object object = new Object();
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
synchronized (object){
//判断有没有票
if (num > 0) {
System.out.println(name + "出票:" + num--);
}
}
}
}
}
发现结果还是不正确,这里我们需要主要,任何一个对象都有自己的一把锁,如果多个线程操作同一个代码块,并且需要同步,那么必须操作同一个对象/同一个对象的同一把锁,于是我们
static final Object object = new Object();
这时候我们再运行:
线程A出票:10
线程B出票:9
线程B出票:8
线程B出票:7
线程B出票:6
线程B出票:5
线程B出票:4
线程B出票:3
线程B出票:2
线程B出票:1
运行多次会发现,AB没有交替出票,所以我们要用一个方法使两个线程交替执行
synchronized (object){
//判断有没有票
if (num > 0) {
try {
//唤醒同步监听器监听其他线程
object.notify();
//让当前线程等待
object.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(name + "出票:" + num--);
}
}
这样执行结果:
线程A出票:10
线程B出票:9
线程A出票:8
线程B出票:7
线程A出票:6
线程B出票:5
线程A出票:4
线程B出票:3
线程A出票:2
线程B出票:1
(2)同步方法,使用synchronized关键字将一个方法声明成同步方法。定义格式:
synchronized 方法返回值 方法名称(参数列表){ }
具体如下:
//用于卖票的任务
class Ticket implements Runnable {
//定义所有车票的数量
public static int num = 10;
String name;
public Ticket(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
this.sale();
}
}
public synchronized void sale(){
//判断有没有票
if (num > 0) {
System.out.println(name + "出票:" + num--);
}
}
}
但其实这样是错误的,我们定义的synchronized方法 其实相当于
synchronzied(this){}
但是在执行类里面 我new了两个线程出来,this就不知道到底指哪一个,因为我定义的票数不多,看不出问题,所以建议自己尝试的时候把票数换成100,错误就会很明显了,因为要让程序正确,我们应该这样做
public class MyClass {
public static void main(String[] args){
Ticket ticket = new Ticket();
new Thread(ticket,"线程A").start();
new Thread(ticket,"线程B").start();
}
}
//用于卖票的任务
class Ticket implements Runnable {
//定义所有车票的数量
public static int num = 10;
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
this.sale();
}
}
public synchronized void sale(){
//判断有没有票
if (num > 0) {
System.out.println(Thread.currentThread().getName()+ "出票:" + num--);
}
}
}
注意:同步方法必须确保多个对象调用的同步方法是操作的同一个对象
(3)通过锁(Lock)解决
public class MyClass {
public static void main(String[] args){
Ticket ticket1 = new Ticket("线程A");
Thread t1 = new Thread(ticket1);
Ticket ticket2 = new Ticket("线程B");
Thread t2 = new Thread(ticket2);
t1.start();
t2.start();
}
}
class Ticket implements Runnable{
//定义所有车票的数量
public static int num = 10;
String name;
public Ticket(String name){
this.name = name;
}
static ReentrantLock reentrantLock = new ReentrantLock();
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
reentrantLock.lock();
//判断有没有票
if (num > 0) {
System.out.println(name + "出票:" + num--);
}
reentrantLock.unlock();
}
}
}
运行结果:
线程A出票:10
线程A出票:9
线程A出票:8
线程B出票:7
线程B出票:6
线程B出票:5
线程B出票:4
线程B出票:3
线程B出票:2
线程B出票:1