精华内容
下载资源
问答
  • 本文主要介绍java中线程同步的几种常用方式。CountDownLatch从字面上理解,CountDownLatch一个同步等待的锁,根据官方的注释可以看出这其实一个同步执行工具类。先看一下官方注释的前两段/*** A ...

    本文主要是介绍java中线程同步的几种常用方式。

    CountDownLatch

    从字面上理解,CountDownLatch是一个同步等待的锁,根据官方的注释可以看出这其实是一个同步执行工具类。

    先看一下官方注释的前两段

    /**

    * A synchronization aid that allows one or more threads to wait until

    * a set of operations being performed in other threads completes.

    *

    *

    A {@code CountDownLatch} is initialized with a given count.

    * The {@link #await await} methods block until the current count reaches

    * zero due to invocations of the {@link #countDown} method, after which

    * all waiting threads are released and any subsequent invocations of

    * {@link #await await} return immediately. This is a one-shot phenomenon

    * -- the count cannot be reset. If you need a version that resets the

    * count, consider using a {@link CyclicBarrier}.

    翻译一下就是:

    /**

    * CountDownLatch是一个,允许一个或多个线程,

    * 等待其他线程中执行的一组操作完成的,同步辅助工具。

    *

    * CountDownLatch用给定的计数进行初始化。

    * 当线程点用await方法后被阻塞,直到当前计数由于其他线程调用countDown()方法而达到零,

    * 此后所有等待线程被放,并且任何后续调用await立即返回。

    * 这是一次性的操作,计数无法重置。

    * 如果您需要重置计数的版本,请考虑使用CyclicBarrier。

    * /

    解释的很清楚,不在赘述,接着看一下官方提供的伪代码案例

    官方案例一

    class Driver { // ...

    void main() throws InterruptedException {

    CountDownLatch startSignal = new CountDownLatch(1);

    CountDownLatch doneSignal = new CountDownLatch(N);

    for (int i = 0; i < N; ++i) // create and start threads

    new Thread(new Worker(startSignal, doneSignal)).start();

    doSomethingElse(); // don't let run yet

    startSignal.countDown(); // let all threads proceed

    doSomethingElse();

    doneSignal.await(); // wait for all to finish

    }

    }

    class Worker implements Runnable {

    private final CountDownLatch startSignal;

    private final CountDownLatch doneSignal;

    Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {

    this.startSignal = startSignal;

    this.doneSignal = doneSignal;

    }

    public void run() {

    try {

    startSignal.await();

    doWork();

    doneSignal.countDown();

    } catch (InterruptedException ex) {} // return;

    }

    void doWork() { ... }

    }

    这个案例很直白的说明了,CountDownLatch可以让多个线程同时初始化完成后等待,直到主线程要求他们开始执行为止,并且当主线程调用await()之后阻塞直到所有的线程调用countDown()将计数减为0,主线程再次唤醒执行后序操作。

    当然这样还有一些其他的注意点,譬如子线程被中断或者子线程的耗时操作很长导致主线程一直阻塞等问题。

    官方案例二

    class Driver2 { // ...

    void main() throws InterruptedException {

    CountDownLatch doneSignal = new CountDownLatch(N);

    Executor e = ...

    for (int i = 0; i < N; ++i) // create and start threads

    e.execute(new WorkerRunnable(doneSignal, i));

    doneSignal.await(); // wait for all to finish

    }

    }

    class WorkerRunnable implements Runnable {

    private final CountDownLatch doneSignal;

    private final int i;

    WorkerRunnable(CountDownLatch doneSignal, int i) {

    this.doneSignal = doneSignal;

    this.i = i;

    }

    public void run() {

    try {

    doWork(i);

    doneSignal.countDown();

    } catch (InterruptedException ex) {} // return;

    }

    void doWork() { ... }

    }

    }

    这个案例是说,当一个问题需要被分成n份进行处理时,将他们用线程池来执行,并让主线程等待。当然官方注释里还说了,如果需要反复用这种形式来执行一些问题时可以考虑使用CyclicBarrier来代替CountDownLatch,因为CountDownLatch是一次性的计数器无法重置。

    CyclicBarrier

    字面意思:可循环使用的栅栏。主要的作用也是让指定个数的线程到达目标位置后进入等到状态,等所有的线程都到到目标位置后同时开始执行。

    构造方法有2个

    CyclicBarrier(int parties),其中parties指等待的线程数目,当await线程数达到parties时,线程同时开始执行。

    CyclicBarrier(int parties, Runnable barrierAction),第二个参数指所有线程达到后执行的操作。

    通过第二个构造方法也可以实现CountDownLatch功能,当然这不是CyclicBarrier的目的

    再来看一下到达目标位置时的等待方法,有2个重载方法

    await(),这个没什么可说的,到达指定位置后等待

    await(long timeout, TimeUnit unit),这个指到到指定位置后等待一段时间,如果超时则继续执行后序操作。

    现在来看2个例子说明一下使用CyclicBarrier可能出现的问题

    CyclicBarrier例一

    public class CyclicBarrierTest {

    public static void main(String[] args) {

    try {

    final int Num = 5;

    CyclicBarrier cyclicBarrier = new CyclicBarrier(Num);

    for (int i = 0; i < Num - 1; i++) {

    new Thread(new RunnableOne(cyclicBarrier)).start();

    }

    Thread thread = new Thread(new RunnableTwo(cyclicBarrier));

    thread.start();

    Thread.sleep(2000);

    thread.interrupt();

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    private static class RunnableOne implements Runnable {

    CyclicBarrier mCyclicBarrier;

    RunnableOne(CyclicBarrier cyclicBarrier) {

    mCyclicBarrier = cyclicBarrier;

    }

    @Override

    public void run() {

    try {

    System.out.println("wait in barrier");

    mCyclicBarrier.await();

    System.out.println("finish");

    } catch (InterruptedException | BrokenBarrierException e) {

    e.printStackTrace();

    }

    }

    }

    private static class RunnableTwo implements Runnable {

    CyclicBarrier mCyclicBarrier;

    RunnableTwo(CyclicBarrier cyclicBarrier) {

    mCyclicBarrier = cyclicBarrier;

    }

    @Override

    public void run() {

    try {

    System.out.println("wait in barrier");

    Thread.sleep(5000);

    mCyclicBarrier.await();

    System.out.println("finish");

    } catch (InterruptedException | BrokenBarrierException e) {

    e.printStackTrace();

    }

    }

    }

    }

    打印结果如下:

    wait in barrier

    wait in barrier

    wait in barrier

    wait in barrier

    wait in barrier

    java.lang.InterruptedException: sleep interrupted

    at java.lang.Thread.sleep(Native Method)

    at com.jxx.myjavatest.CyclicBarrierTest$RunnableTwo.run(CyclicBarrierTest.java:65)

    at java.lang.Thread.run(Thread.java:748)

    这个例子的意图也很简单,启动4个RunnableOne,随后启动1个RunnableTwo,在所有线程都await()之前其中一个线程被中断了,因为没有都await()成功,其他4个线程就一直阻塞。

    这就提醒我们,要在抛出异常后及时处理,至少也要让其他线程能正常执行下去。

    CyclicBarrier例二

    public class CyclicBarrierTest {

    public static void main(String[] args) {

    final int Num = 5;

    CyclicBarrier cyclicBarrier = new CyclicBarrier(Num);

    for (int i = 0; i < Num - 1; i++) {

    new Thread(new RunnableOne(cyclicBarrier)).start();

    }

    Thread thread = new Thread(new RunnableTwo(cyclicBarrier));

    thread.start();

    }

    private static class RunnableOne implements Runnable {

    CyclicBarrier mCyclicBarrier;

    RunnableOne(CyclicBarrier cyclicBarrier) {

    mCyclicBarrier = cyclicBarrier;

    }

    @Override

    public void run() {

    try {

    System.out.println("wait in barrier");

    Thread.sleep(5000);

    mCyclicBarrier.await();

    System.out.println("finish");

    } catch (InterruptedException | BrokenBarrierException e) {

    e.printStackTrace();

    }

    }

    }

    private static class RunnableTwo implements Runnable {

    CyclicBarrier mCyclicBarrier;

    RunnableTwo(CyclicBarrier cyclicBarrier) {

    mCyclicBarrier = cyclicBarrier;

    }

    @Override

    public void run() {

    try {

    System.out.println("wait in barrier");

    mCyclicBarrier.await(2000, TimeUnit.MILLISECONDS);

    System.out.println("finish");

    } catch (InterruptedException | BrokenBarrierException | TimeoutException e) {

    e.printStackTrace();

    }

    }

    }

    }

    打印如下:

    wait in barrier

    wait in barrier

    wait in barrier

    wait in barrier

    wait in barrier

    java.util.concurrent.TimeoutException

    at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:257)

    at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)

    at com.jxx.myjavatest.CyclicBarrierTest$RunnableTwo.run(CyclicBarrierTest.java:61)

    at java.lang.Thread.run(Thread.java:748)

    java.util.concurrent.BrokenBarrierException

    at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)

    at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)

    at com.jxx.myjavatest.CyclicBarrierTest$RunnableOne.run(CyclicBarrierTest.java:40)

    at java.lang.Thread.run(Thread.java:748)

    java.util.concurrent.BrokenBarrierException

    at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)

    at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)

    at com.jxx.myjavatest.CyclicBarrierTest$RunnableOne.run(CyclicBarrierTest.java:40)

    at java.lang.Thread.run(Thread.java:748)

    java.util.concurrent.BrokenBarrierException

    at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)

    at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)

    at com.jxx.myjavatest.CyclicBarrierTest$RunnableOne.run(CyclicBarrierTest.java:40)

    at java.lang.Thread.run(Thread.java:748)

    java.util.concurrent.BrokenBarrierException

    at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)

    at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)

    at com.jxx.myjavatest.CyclicBarrierTest$RunnableOne.run(CyclicBarrierTest.java:40)

    at java.lang.Thread.run(Thread.java:748)

    这里模拟了一个await()超时的异常,可以看到在抛出异常后需要我们自己处理后期的事物。同时某一个线程抛出超时异常后,其他线程再次到达会抛出BrokenBarrierException异常,防止继续等待。

    Semaphore

    其实Semaphore不该放到这里讲,因为Semaphore类似于Lock的存在,是对资源或者线程的一种控制,但是这篇博文主要讲了线程的等待唤起,信号量放这里讲问题也不大。

    官方的说法是信号量通常用来限制线程的数量,而不是控制访问一些(物理或逻辑)资源。用法也非常简单,使用前先acquire()获取许可,在获取许可过程中,是线程是被阻塞的,使用完毕release()许可即可。这点类似于Lock,不同的是Semaphore的acquire()可以被允许多次。

    Semaphore有两个构造方法,可以指定Semaphore获取是公平的还是非公平的,默认是非公平

    看这里,举个栗子:

    public class SemaphoreTest {

    public static void main(String[] args) {

    CountDownLatch startLatch = new CountDownLatch(1);

    Semaphore semaphore = new Semaphore(3);

    for (int i = 0; i < 10; i++) {

    new Thread(new MyRunnable(startLatch, semaphore)).start();

    }

    startLatch.countDown();

    }

    private static class MyRunnable implements Runnable {

    final CountDownLatch mCountDownLatch;

    final Semaphore mSemaphore;

    MyRunnable(CountDownLatch countDownLatch, Semaphore semaphore) {

    mCountDownLatch = countDownLatch;

    mSemaphore = semaphore;

    }

    @Override

    public void run() {

    try {

    mCountDownLatch.await();

    mSemaphore.acquire();

    System.out.println(Thread.currentThread().getName() + " acquire success");

    Thread.sleep(2000);

    } catch (InterruptedException e) {

    e.printStackTrace();

    } finally {

    mSemaphore.release();

    }

    }

    }

    }

    打印如下:

    Thread-0 acquire success

    Thread-1 acquire success

    Thread-9 acquire success

    Thread-3 acquire success

    Thread-2 acquire success

    Thread-4 acquire success

    Thread-6 acquire success

    Thread-7 acquire success

    Thread-5 acquire success

    Thread-8 acquire success

    可以看出这是默认的非公平锁的情况,再来看一下公平锁的情况

    public class SemaphoreTest {

    public static void main(String[] args) {

    Semaphore semaphore = new Semaphore(3, true);

    for (int i = 0; i < 10; i++) {

    new Thread(new MyRunnable(semaphore)).start();

    }

    }

    private static class MyRunnable implements Runnable {

    final Semaphore mSemaphore;

    MyRunnable(Semaphore semaphore) {

    mSemaphore = semaphore;

    }

    @Override

    public void run() {

    try {

    mSemaphore.acquire();

    System.out.println(Thread.currentThread().getName() + " acquire success");

    Thread.sleep(2000);

    } catch (InterruptedException e) {

    e.printStackTrace();

    } finally {

    mSemaphore.release();

    }

    }

    }

    }

    打印如下

    Thread-0 acquire success

    Thread-1 acquire success

    Thread-2 acquire success

    Thread-3 acquire success

    Thread-4 acquire success

    Thread-5 acquire success

    Thread-6 acquire success

    Thread-7 acquire success

    Thread-8 acquire success

    Thread-9 acquire success

    当然这里肯定有读者想了,直接将Semaphore置为true公平锁的情况就好了,何必去掉CountDownLatch呢。

    这里需要注意下,虽然你Semaphore是公平,但是CountDownLatch到点之后唤起线程的顺序是随机的,并不一定就是线程入队的顺序唤起。

    线程的join()

    jion方法的作用是让主线程阻塞等待子线程完成,当然有几个前提条件,下面细说。

    join方法有三个重载的版本

    final void join(); //一直等待到join的线程执行完毕

    final synchronized void join(long millis); //等待指定时间后继续执行

    final synchronized void join(long millis, int nanos); 同上,时间处理了一下

    第一个和第三个最后其实调用的都是第二个重载方法,我们来看一下源码

    public final synchronized void join(long millis)

    throws InterruptedException {

    long base = System.currentTimeMillis();

    long now = 0;

    if (millis < 0) {

    throw new IllegalArgumentException("timeout value is negative");

    }

    if (millis == 0) {

    while (isAlive()) {

    wait(0);

    }

    } else {

    while (isAlive()) {

    long delay = millis - now;

    if (delay <= 0) {

    break;

    }

    wait(delay);

    now = System.currentTimeMillis() - base;

    }

    }

    }

    直接看最后的while循环,可以看到,调用这个方法,其实是调用Object提供的wait(long timeout)让主线程阻塞而已。有几个注意点

    子线程如果已经销毁,则直接跳过等待

    join(long millis) 是一个同步方位,意味着要想调用此方法需要先获取到子线程的实例对象锁

    来看一个例子,验证一下第二点:

    public class JoinTest {

    public static void main(String[] args) {

    final Thread thread = new Thread(new Runnable() {

    @Override

    public void run() {

    try {

    Thread.sleep(6000);

    System.out.println("4---" + System.currentTimeMillis());

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    });

    thread.start();

    try {

    System.out.println("1---" + System.currentTimeMillis());

    new Thread(new MyRunnable(thread)).start();

    System.out.println("2---" + System.currentTimeMillis());

    thread.join(2000);

    System.out.println("3---" + System.currentTimeMillis());

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    System.out.println("finish " + System.currentTimeMillis());

    }

    private static class MyRunnable implements Runnable {

    final Object mObject;

    MyRunnable(Object object) {

    mObject = object;

    }

    @Override

    public void run() {

    synchronized (mObject) {

    try {

    Thread.sleep(2000);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    }

    }

    }

    打印如下:

    1---1525529803445

    2---1525529803446

    3---1525529807449

    finish 1525529807449

    4---1525529809445

    可以很清晰的看到,打印完1之后立即打印了2,但是2和3之间打相差了4秒,原因就在join之前需要先获取thread的锁对象,但是需要MyRunnable释放锁之后才能执行。

    总结

    好了,又到总结的时间了。

    CountDownLatch相对于CyclicBarrier侧重点是,等待其他线程操作完成后主线程在继续后续的操作

    CyclicBarrier相对于CountDownLatch侧重点是,所有的线程操作完成后等待一起继续后续操作。

    CountDownLatch不能重置状态,CyclicBarrier可以重置后多次利用

    CountDownLatch和CyclicBarrier抛出异常后都需要妥善处理

    Semaphore于Lock类似,主要用于线程的访问控制,构造时可以指定是否是公平竞争

    thread.join()主要是让主线程等待子线程执行完毕,有个注意点就是join()执行之前需要获取到子线程的实例对象锁。

    展开全文
  • 关于java实现线程同步的方式有哪些当使用多个线程来访问同一个数据时,非常容易出现...什么是线程同步?当使用多个线程来访问同一个数据时,非常容易出现线程安全问题(比如多个线程都在操作同一数据导致数据不一致)...

    关于java实现线程同步的方式有哪些

    当使用多个线程来访问同一个数据时,非常容易出现线程安全问题,所以我们用同步机制来解决这些问题。那么,java实现线程同步的方式有哪些?下面百分网小编带大家一起来看看详细内容,希望对大家有所帮助!想了解更多相关信息请持续关注我们应届毕业生考试网!

    什么是线程同步?

    当使用多个线程来访问同一个数据时,非常容易出现线程安全问题(比如多个线程都在操作同一数据导致数据不一致),所以我们用同步机制来解决这些问题。

    实现同步机制有两个方法:

    1。同步代码块:

    synchronized(同一个数据){} 同一个数据:就是N条线程同时访问一个数据。

    2。同步方法:

    public synchronized 数据返回类型 方法名(){}

    就是使用 synchronized 来修饰某个方法,则该方法称为同步方法。对于同步方法而言,无需显示指定同步监视器,同步方法的同步监视器是 this 也就是该对象的本身(这里指的对象本身有点含糊,其实就是调用该同步方法的对象)通过使用同步方法,可非常方便的将某类变成线程安全的类,具有如下特征:

    1,该类的对象可以被多个线程安全的访问。

    2,每个线程调用该对象的任意方法之后,都将得到正确的结果。

    3,每个线程调用该对象的任意方法之后,该对象状态依然保持合理状态。

    注:synchronized关键字可以修饰方法,也可以修饰代码块,但不能修饰构造器,属性等。

    实现同步机制注意以下几点: 安全性高,性能低,在多线程用。性能高,安全性低,在单线程用。

    1,不要对线程安全类的所有方法都进行同步,只对那些会改变共享资源方法的进行同步。

    2,如果可变类有两种运行环境,当线程环境和多线程环境则应该为该可变类提供两种版本:线程安全版本和线程不安全版本(没有同步方法和同步块)。在单线程中环境中,使用线程不安全版本以保证性能,在多线程中使用线程安全版本.

    线程通讯:

    为什么要使用线程通讯?

    当使用synchronized 来修饰某个共享资源时(分同步代码块和同步方法两种情况),当某个线程获得共享资源的锁后就可以执行相应的代码段,直到该线程运行完该代码段后才释放对该 共享资源的锁,让其他线程有机会执行对该共享资源的修改。当某个线程占有某个共享资源的锁时,如果另外一个线程也想获得这把锁运行就需要使用wait() 和notify()/notifyAll()方法来进行线程通讯了。

    Java.lang.object 里的三个方法:wait() notify() notifyAll()

    wait方法导致当前线程等待,直到其他线程调用同步监视器的`notify方法或notifyAll方法来唤醒该线程。

    wait(mills)方法

    都是等待指定时间后自动苏醒,调用wait方法的当前线程会释放该同步监视器的锁定,可以不用notify或notifyAll方法把它唤醒。

    notify()

    唤醒在同步监视器上等待的单个线程,如果所有线程都在同步监视器上等待,则会选择唤醒其中一个线程,选择是任意性的,只有当前线程放弃对该同步监视器的锁定后,也就是使用wait方法后,才可以执行被唤醒的线程。

    notifyAll()方法

    唤醒在同步监视器上等待的所有的线程。只用当前线程放弃对该同步监视器的锁定后,才可以执行被唤醒的线程

    【关于java实现线程同步的方式有哪些】相关文章:

    展开全文
  • 当在程序启动两个或多个线程时,可能存在多个线程尝试访问相同资源的情况,并且最终由于...因此需要同步多个线程的操作,并确保只有一个线程可以在给定的时间点访问资源。它使用称为监视器的概念实现的。Java...

    当在程序中启动两个或多个线程时,可能存在多个线程尝试访问相同资源的情况,并且最终由于并发问题它们可能产生无法预料的结果。例如,如果多个线程尝试在同一个文件中写入,那么它们可能会破坏数据,因为其中一个线程可以覆盖数据,或者当一个线程同时打开同一个文件时,另一个线程可能正在关闭同一个文件。

    因此需要同步多个线程的操作,并确保只有一个线程可以在给定的时间点访问资源。它是使用称为监视器的概念实现的。Java中的每个对象都与一个监视器相关联,一个线程可以锁定或解锁。一次只有一个线程可以锁定监视器。

    Java编程语言提供了一种非常方便的方法,即使用synchronized块创建线程并同步其任务。在此块中保留共享资源。以下是synchronized声明的一般形式 -

    语法

    synchronized(objectidentifier) {

    // Access shared variables and other shared resources

    }

    这里,objectidentifier是对一个对象的引用,该对象的锁与synchronized语句所代表的监视器相关联。下面有两个示例,在示例中使用两个不同的线程打印计数器。当线程不同步时,它们打印的计数器值不是按顺序排列的,但是当通过置于synchronized()块内部打印计数器时,它会为两个线程按顺序打印计数器。

    1. 没有同步的多线程示例

    这是一个简单的例子,可能会或可能不会按顺序打印计数器值,每次运行它时,它会根据线程的CPU可用性产生不同的结果。

    示例

    class PrintDemo {

    public void printCount() {

    try {

    for(int i = 5; i > 0; i--) {

    System.out.println("Counter --- " + i );

    }

    } catch (Exception e) {

    System.out.println("Thread interrupted.");

    }

    }

    }

    class ThreadDemo extends Thread {

    private Thread t;

    private String threadName;

    PrintDemo PD;

    ThreadDemo( String name, PrintDemo pd) {

    threadName = name;

    PD = pd;

    }

    public void run() {

    PD.printCount();

    System.out.println("Thread " + threadName + " exiting.");

    }

    public void start () {

    System.out.println("Starting " + threadName );

    if (t == null) {

    t = new Thread (this, threadName);

    t.start ();

    }

    }

    }

    public class TestThread {

    public static void main(String args[]) {

    PrintDemo PD = new PrintDemo();

    ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );

    ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );

    T1.start();

    T2.start();

    // wait for threads to end

    try {

    T1.join();

    T2.join();

    } catch ( Exception e) {

    System.out.println("Interrupted");

    }

    }

    }

    执行上面示例代码,得到以下结果 -

    Starting Thread - 1

    Starting Thread - 2

    Counter --- 5

    Counter --- 4

    Counter --- 3

    Counter --- 5

    Counter --- 2

    Counter --- 1

    Counter --- 4

    Thread Thread - 1 exiting.

    Counter --- 3

    Counter --- 2

    Counter --- 1

    Thread Thread - 2 exiting.

    2. 具有同步的多线程示例

    下面是按顺序打印计数器值的示例,每次运行它时,都会产生相同的结果。

    示例

    class PrintDemo {

    public void printCount() {

    try {

    for(int i = 5; i > 0; i--) {

    System.out.println("Counter --- " + i );

    }

    } catch (Exception e) {

    System.out.println("Thread interrupted.");

    }

    }

    }

    class ThreadDemo extends Thread {

    private Thread t;

    private String threadName;

    PrintDemo PD;

    ThreadDemo( String name, PrintDemo pd) {

    threadName = name;

    PD = pd;

    }

    public void run() {

    synchronized(PD) {

    PD.printCount();

    }

    System.out.println("Thread " + threadName + " exiting.");

    }

    public void start () {

    System.out.println("Starting " + threadName );

    if (t == null) {

    t = new Thread (this, threadName);

    t.start ();

    }

    }

    }

    public class TestThread {

    public static void main(String args[]) {

    PrintDemo PD = new PrintDemo();

    ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );

    ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );

    T1.start();

    T2.start();

    // wait for threads to end

    try {

    T1.join();

    T2.join();

    } catch ( Exception e) {

    System.out.println("Interrupted");

    }

    }

    }

    执行上面示例代码,得到以下结果 -

    Starting Thread - 1

    Starting Thread - 2

    Counter --- 5

    Counter --- 4

    Counter --- 3

    Counter --- 2

    Counter --- 1

    Thread Thread - 1 exiting.

    Counter --- 5

    Counter --- 4

    Counter --- 3

    Counter --- 2

    Counter --- 1

    Thread Thread - 2 exiting.

    ¥ 我要打赏

    纠错/补充

    收藏

    加QQ群啦,易百教程官方技术学习群

    注意:建议每个人选自己的技术方向加群,同一个QQ最多限加 3 个群。

    展开全文
  • 好啦,下面来看线程对某个变量的操作步骤:1.从主内存复制数据到工作内存2.执行代码,对数据进行各种...这两个棋手相当于是同步中线程,观众相当于其它线程。棋手无法直接操作服务器的大屏幕的,他只能看到...

    好啦,下面来看线程对某个变量的操作步骤:

    1.从主内存中复制数据到工作内存

    2.执行代码,对数据进行各种操作和计算

    3.把操作后的变量值重新写回主内存中

    现在举个例子,设想两个棋手要通过两个终端显示器(Working Memory)对奕,而观众要通过服务器大屏幕(Main Memory )观看他们的比赛过程。这两个棋手相当于是同步中的线程,观众相当于其它线程。棋手是无法直接操作服务器的大屏幕的,他只能看到自己的终端显示器,只能先从服务器上将当前结果先复制到自己的终端上(步骤1),然后在自己的终端上操作(步骤2),将操作的结果记录在终端上,然后在某一时刻同步到服务器上(步骤3)。他所能看到的结果就是从服务器上复制到自己的终端上的内容,而要想把自己操作后的结果让其他人看到必须同步到服务器上才行。至于什么时候同步,那要看终端和服务器的通信机制。

    回到这三个步骤,这个顺序是我们希望的,但是,JVM并不保证第1步和第3步会严格按照上述次序立即执行。因为根据java语言规范的规定,线程的工作内存和主存间的数据交换是松耦合的,什么时候需要刷新工作内存或者什么时候更新主存的内容,可以由具体的虚拟机实现自行决定。由于JVM可以对特征代码进行调优,也就改变了某些运行步骤的次序的颠倒,那么每次线程调用变量时是直接取自己的工作存储器中的值还是先从主存储器复制再取是没有保证的,任何一种情况都可能发生。同样的,线程改变变量的值之后,是否马上写回到主存储器上也是不可保证的,也许马上写,也许过一段时间再写。那么,在多线程的应用场景下就会出现问题了,多个线程同时访问同一个代码块,很有可能某个线程已经改变了某变量的值,当然现在的改变仅仅是局限于工作内存中的改变,此时JVM并不能保证将改变后的值立马写到主内存中去,也就意味着有可能其他线程不能立马得到改变后的值,依然在旧的变量上进行各种操作和运算,最终导致不可预料的结果。

    这可如何是好呢?还好有synchronized和volatile:

    1.多个线程共有的字段应该用synchronized或volatile来保护.

    2.synchronized负责线程间的互斥.即同一时候只有一个线程可以执行synchronized中的代码.

    synchronized还有另外一个方面的作用:在线程进入synchronized块之前,会把工作存内存中的所有内容映射到主内存上,然后把工作内存清空再从主存储器上拷贝最新的值。而在线程退出synchronized块时,同样会把工作内存中的值映射到主内存,不过此时并不会清空工作内存。这样一来就可以强制其按照上面的顺序运行,以保证线程在执行完代码块后,工作内存中的值和主内存中的值是一致的,保证了数据的一致性!

    3.volatile负责线程中的变量与主存储区同步.但不负责每个线程之间的同步.

    volatile的含义是:线程在试图读取一个volatile变量时,会从主内存区中读取最新的值。现在很清楚了吧。

    展开全文
  • 数据同步就是指在同一时间,只能由一个线程来访问被同步的类变量,当前线程访问完这些变量后,其他线程才能继续访问,下面看一下为什么要进行数据同步Java中的变量分为两类:局部变量和类变量。局部变量指在方法内...
  • 线程安全性什么是线程安全性《Java Concurrency In Practice》一书的作者 Brian Goetz 是这样描述“线程安全”的:“当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行...
  • 问题的引出在java语言,我们常常会用到多线程相关的操作,但是多线程操作可能会出现一些问题。现在给定一个抢票的多线程代码class MyThread implementsRunnable{int a = 10;//票数@Overridepublic voidrun(){...
  • java中线程同步,线程让步,线程休眠的区别和联系是什么 线程的本质还是一个运行中的类,
  • Java中的变量分为两类:局部变量和类变量。局部变量指在方法内定义的变量,如在run方法中定义的变量。对于这些变量来说,并不存在线程之间共享的问题。因此,它们不需要进行数据同步。类变量在类中定义的变量,...
  • Java线程同步是保证多线程状态下安全访问竞争资源的一种编程手段,但线程的同步在Java多线程编程算是一个比较难的难点,很多开发者甚至都不知道什么是竞争资源,也不知道时候需要进行线程同步,当然这是没有明确...
  • 无状态对象一定是线程安全的。如果我们在无状态的对象增加一个状态时,会出现什么情况呢?假设我们按照以下方式在servlet增加一个"命中计数器"来管理请求数量:在servlet增加一个long类型的...
  • 线程同步的概念处理多线程问题时,多个线程同时访问同一个对象,并且一个线程还想修改这个对象。这时候,我们就需要用到“线程同步”。线程同步其实就是一种等待机制,多个线程需要同时访问同一个对象,则...
  • Java认证:Java线程同步方法。线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源、什么时候需要考虑同步,怎么同步等等问题,当然,这些问题没有很明确的答案,但有些原则问题需要考虑,是否有...
  • 首先,线程同步,就意味着线程是安全的,在JDK1.1版本,所有的集合都是线程安全的。但是在1.2及以后的版本就出现了一些线程不安全的结合,为什么版本升级反而会出现一些线程不安全的集合呢?因为线程不安全的...
  • 线程同步Java线程编程的难点,往往开发者搞不清楚什么是竞争资源、什么时候需要考虑同步,怎么同步等等问题,当然,这些问题没有很明确的答案,但有些原则问题需要考虑,是否有竞争资源被同时改动的问题?...
  • Exchanger主要解决什么问题? 对比SynchronousQueue,为什么说Exchanger可被视为 SynchronousQueue 的双向形式? Exchanger在不同的JDK版本实现有什么差别?...什么是伪共享,Exchanger如何体现的? Ex.
  • 同步有几种实java中多线程的实现方法有两种:1.直接继承thread类;2.实现runnable接口;同步的实现方法有五种:1.同步方法;2.同步代码块;3.使用特殊域变量(volatile)实现线程同步;4.使用重入锁实现线程同步;5....
  • Java中线程同步问题

    2019-05-06 21:34:11
    在引出线程同步问题之前首先要了解什么是同步? 所谓的同步指的是所有的线程对于同一个资源的访问上的时序性。 2.Synchronized关键字 在java中使用synchronized关键字来实现线程同步的问题【即加锁操作】 3....
  • Java中线程同步

    2019-02-22 02:26:58
    什么需要线程同步线程同步的问题在这样的背景下才衍生的: 内存一个硬件,CPU计算速度比内存快很多,程序计算的问题都由CPU来完成的,但不是每次计算,CPU都和内存进行数据交互,CPU会先把一些数据写在缓存...
  • java:线程:线程同步

    2018-10-26 21:25:00
    这时该变量多个线程共享的,使用这种同步机制需要很细致的分析在什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放该对象的索等等。 所有这些都因为多个线程共享了该资源造成的。...
  • Java线程四:线程同步

    2020-12-08 15:55:28
    由前面几篇文章可知:线程什么时候运行不确定的,即: ●各个线程是通过竞争CPU的时间而获得运行机会的; ●各线程什么时候获得CPU时间,占用多久,不可预测的; ●一个正在运行的线程什么地方被暂停不...
  • 线程同步Java线程编程的难点,往往开发者搞不清楚什么是竞争资源、什么时候需要考虑同步,怎么同步等等问题,当然,这些问题没有很明确的答案,但有些原则问题需要考虑,是否有竞争资源被同时改动的问题?...
  • 面试题:线程同步有几种方法(百度面试题)面试题:线程安全解释一下(大疆面试题)为什么线程同步?当使用多个线程要同时访问一个变量或对象时,如果这些线程既有读又有写操作时,就会导致变量值或对象的状态出现...
  • Java中,synchronized关键字用来控制线程同步的,就是在多线程的环境下,控制synchronized代码段不被多个线程同时执行。synchronized既可以加在一段代码上,也可以加在方法上。关键,不要认为给方法或者代码段...
  • Java中,synchronized关键字用来控制线程同步的,就是在多线程的环境下,控制synchronized代码段不被多个线程同时执行。synchronized既可以加在一段代码上,也可以加在方法上。关键,不要认为给方法或者代码段...
  • Java中线程同步技术

    2015-07-09 17:08:23
    Java中线程同步有几种方式,synchronized关键字和wait(),notify()下面就来对这两种方式展开描述一下: synchronized关键字可以用于方法和代码块,若用于方法,默认对当前实例加锁。而用于代码块时可以指定加锁...
  • Java中的多线程同步

    千次阅读 2016-04-24 11:38:06
    一、进程与线程  进程可并发执行的程序在一个数据集上的一次执行过程,它...二、为什么要引入进程与线程  要探索这个问题答案之前,需要先了解并发执行。并发执行为了增强计算机系统的处理能力和提高资源利用率,

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,941
精华内容 1,576
关键字:

java中什么是线程同步

java 订阅