交替输出两个线程,并显示执行次数

wait/notify 机制

一个线程调用Object的wait()方法,使其线程被阻塞;

另一线程调用Object的notify()/notifyAll()方法,wiat()阻塞的线程继续执行。

方法一:通过继承runnable实现

Main函数中执行

 public static void main(String[] args) throws InterruptedException
    {

        MyThreadRunnable threadRunnable = new MyThreadRunnable();
        Thread t1 = new Thread(threadRunnable);
        t1.setName("线程1");
        Thread t2 = new Thread(threadRunnable);
        t2.setName("线程2");
        t1.start();
        t2.start();
    }

MyThreadRunnable 方法执行内容

public class MyThreadRunnable implements Runnable
{
    int mVal = 0;
    private Object Lock = new Object();

    public void run()
    {
        synchronized (Lock)
        {
            for (; mVal < 100; mVal++)
            {
                try
                {
                    Lock.notifyAll();
                    Lock.wait();
                    System.out.println("MyThreadRunnable " + Thread.currentThread().getName() + ":" + mVal);
                } catch (InterruptedException e)
                {
                    throw new RuntimeException(e);
                }
            }
            Lock.notifyAll();
        }
    }
}

运行结果如下

通过Runnable接口创建线程的步骤如下:

1、定义实现Runnable接口的类,并实现该类中的run()方法。

2、建立一个Thread对象,并将实现的Runnable接口的类的对象作为参数传入Thread类的构造方法。

  1. 通过Thread类中的start()方法启动线程,并运行。
方法二:通过继承Thread类进行实现

main方法中创建两个线程

 public static void main(String[] args) throws InterruptedException
    {
        MyThread5 thread9=new MyThread5();
        thread9.setName("线程09");
        MyThread5 thread10=new MyThread5();
        thread10.setName("线程10");
        thread9.start();
        thread10.start();

    }

在MyThread5中继承Thread 类

public class MyThread5 extends Thread
{
    private static Object Lock = new Object();
    private static int sTimes = 0;


    @Override
    public void run()
    {
        while (true)
        {
            if(sTimes==101)
            {
                break;
            }
            synchronized (Lock)
            {
                System.out.println(Thread.currentThread().getName() + " : " + sTimes);
                sTimes++;
                Lock.notify();
                try
                {
                    Lock.wait();

                } catch (InterruptedException e)
                {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

  • 继承Thread类实现多线程:实现起来简单,而且要获取当前线程,无需调用Thread.currentThread()方法,直接使用this即可获取当前线程
    • 线程类已经继承Thread类了,就不能再继承其他类
    • 多个线程不能共享同一份资源
wait/notify总结:

wait方法

在执行wait()方法前,当前线程必须已获得对象锁。调用它时会阻塞当前线程,进入等待状态,在当前wait()处暂停线程。

wait()方法执行后,会立即释放获得的对象锁。

notify 方法

在执行notify方法前,当前线程也必须已获得线程锁。

调用notify()方法后,会通知一个执行了wait()方法的阻塞等待线程,使该等待线程重新获得取到对象锁,然后继续执行wait()后面的代码。

与wait()方法不同,执行notify()后,不会立即释放对象锁,而需要执行完synchronized的代码块或方法才会释放锁,所以接收通知的线程也不会立即获得锁,也需要等待执行notify()方法的线程释放锁后再获取锁。

notifyAll()

通知所有等待状态的线程,用notifyAll唤醒所有线程。

Thread类中其他实现方法

方法三:通过synchronized控制方法来实现

public static void main(String[] args) throws InterruptedException
    {

        MyThreadStatic1 thread7 = new MyThreadStatic1();
        thread7.setName("线程7");
        MyThreadStatic1 thread8 = new MyThreadStatic1();
        thread8.setName("线程8");
        thread7.start();
        thread8.start();
    }

MyThreadStaitic方法继承Thread类

public class MyThreadStatic1 extends Thread
{
    private static int sVal = 0;

    public static synchronized void addSVal()
    {
        sVal++;
    }

    @Override
    public void run()
    {

        while (sVal < 100)
        {

            if (sVal % 2 == 0)
            {
                if (Thread.currentThread().getName() == "线程7")
                {
                    System.out.println(Thread.currentThread().getName() + ":" + (sVal));
                    addSVal();
                } else
                {
                    sleep();
                }
            } else
            {
                if (Thread.currentThread().getName() == "线程8")
                {
                    System.out.println(Thread.currentThread().getName() + ":" + (sVal));
                    addSVal();
                } else
                {
                    sleep();
                }

            }
        }
    }

    private void sleep()
    {

        try
        {
            Thread.sleep(0);
        } catch (Exception e)
        {
            throw new RuntimeException(e);
        }
    }
}

这种方法主要通过控制addSVal函数,来控制变量进而控制两个线程的输出顺序。

方法四:通过Lock锁来实现

通常,锁提供对共享资源的独占访问:一次只能有一个线程可以获取锁,并且对共享资源的所有访问都要求首先获取锁。 但是,一些锁可能允许并发访问共享资源,如ReadWriteLock的读写锁。 

Lock接口基本的方法:

static Lock lock=new ReetrantLock();

public static void addNumber(){    lock.lock();//加锁

    try{

        number++;

    }finally{

        lock.unlock();//释放锁

    }

}

Main函数中的代码

 public static void main(String[] args) throws InterruptedException
    {

        MyThreadStatic2 thread7 = new MyThreadStatic2();
        thread7.setName("线程7");
        MyThreadStatic2 thread8 = new MyThreadStatic2();
        thread8.setName("线程8");
        thread7.start();

        thread8.start();    }

MyThreadStatic2函数中的主要代码

import java.util.concurrent.locks.ReentrantLock;

public class MyThreadStatic2 extends Thread
{
    private static final ReentrantLock mLock = new ReentrantLock();
    private static int sVal = 0;

    @Override
    public void run()
    {
        while (sVal < 100)
        {
            if (sVal % 2 == 0)
            {
                if (Thread.currentThread().getName() == "线程7")
                {

                    System.out.println(Thread.currentThread().getName() + " : " + sVal);
                    addSVal();

                } else
                {
                    sleep();
                }
            } else
            {
                if (Thread.currentThread().getName() == "线程8")
                {
                    System.out.println(Thread.currentThread().getName() + " : " + sVal);
                    addSVal();
                }else
                {
                    sleep();
                }
            }
        }
    }

    private void addSVal()
    {
        mLock.lock();
        sVal++;
        mLock.unlock();
    }

    private void sleep()
    {

        try
        {
            Thread.sleep(0);
        } catch (Exception e)
        {
            throw new RuntimeException(e);
        }
    }
}

方法五:通过AtomicInterger来实现

  public static void main(String[] args) throws InterruptedException
    {

        MyThreadStatic3 thread7 = new MyThreadStatic3();
        thread7.setName("线程7");
        MyThreadStatic3 thread8 = new MyThreadStatic3();
        thread8.setName("线程8");
        thread7.start();

        thread8.start();
    }

MyThreadStatic3中的内容

import java.util.concurrent.atomic.AtomicInteger;

public class MyThreadStatic3 extends Thread
{
    private static final AtomicInteger sVal=new AtomicInteger();

    @Override
    public void run()
    {
        while (sVal.get() < 100)
        {

            if (sVal.get() % 2 == 0)
            {
                if (Thread.currentThread().getName() == "线程7")
                {
                    System.out.println(Thread.currentThread().getName() + ":" + (sVal));
                    sVal();
                } else
                {
                    sleep();
                }
            } else
            {
                if (Thread.currentThread().getName() == "线程8")
                {
                    System.out.println(Thread.currentThread().getName() + ":" + (sVal));
                    sVal();
                } else
                {
                    sleep();
                }

            }
        }
    }
    private void sleep()
    {

        try
        {
            Thread.sleep(0);
        } catch (Exception e)
        {
            throw new RuntimeException(e);
        }
    }
    private void sVal()
    {
        sVal.set(sVal.get()+1);
    }
}

总结:

synchronized使用

Java多线程——synchronized使用详解_synchronized用法-CSDN博客

  1. synchronized修饰代码块

修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象。

结论:

当多个并发线程(thread1和thread2)访问同一个对象(这个很重要)(ThreadSyn)中的synchronized代码块时,在同一时刻只能有一个线程得到执行,其他线程受阻塞,必须等待当前线程执行完这个代码块以后才能执行该代码块。

当多个线程访问 不同对象 的同步代码块,线程访问各自同步代码块,线程不会阻塞,互不干扰。

指定要给某个对象加锁

当有一个明确的对象作为锁时,

public void method(someObject obj)
{
//obj 锁定的对象
    {
        //todo
    }
}

2 synchronized修饰一个方法

Synchronized修饰一个方法很简单,就是在方法的前面加synchronized。

public synchronized void method(){
    //todo
};

写法二:

public void method()
{
   synchronized(this) {
      // todo
   }
}

注意事项
  1. synchronized关键字不能继承。

虽然可以使用synchronized来定义方法,但synchronized并不属于方法定义的一部分,因此,synchronized关键字不能被继承。如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以。当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了。

  1. 在定义接口方法时不能使用synchronized关键字。
  2. 构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。

3. synchronized修饰一个静态的方法

Synchronized也可修饰一个静态方法,

public synchronized static void method() {
   // todo
}

静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象。

总结:

  1. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;

如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。

  1. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
  2. 实现同步是要很大的系统开销代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

  • 30
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值