Java 多线程共享变量

范例1:启动10个线程求连续自然数累加的和,使用线程本地变量ThreadLocal<Integer>

package com.contoso.demo1;

public class App {

    public static void main(String[] args) {
        // 启动10个线程求连续自然数累加的和
        for (int i = 1; i <= 10; i++) {
            Counter counter = new Counter(i * 10);
            Thread t = new Thread(counter, "线程" + i);
            t.start();
        }
    }

}
package com.contoso.demo1;

public class Counter implements Runnable {

    private Integer last;

    public Counter(Integer last) {
        this.last = last;
    }

    //线程本地变量:每个线程都创建了一个自己访问的内部副本变量,intLocal变量在各个线程中各自独立互不干扰
    ThreadLocal<Integer> intLocal = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };

    @Override
    public void run() {
        for (int i = 0; i <= last; i++) {
            intLocal.set(intLocal.get() + i);
        }
        System.out.println(Thread.currentThread().getName() + "--" + intLocal.get());
    }

}

例如:

1   +   2   +   3   +   4   +   5   +   6   +   7   +   8   +   9   +   10    =‬ 55

1   +   2   +   3   +   4   +   5   +   6   +   7   +   8   +   9   +   10   +   11   +   12   +   13   +   14   +   15   +   16   +   17   +   18   +   19   +   20 =‬ 210

1   +   2   +   3   +   4   +   5   +   6   +   7   +   8   +   9   +   10   +   11   +   12   +   13   +   14   +   15   +   16   +   17   +   18   +   19   +   20   +   21   +   22   +   23   +   24   +   25   +   26   +   27   +   28   +   29   +   30 =‬ 465

run:
线程2--210
线程3--465
线程1--55
线程5--1275
线程4--820
线程8--3240
线程7--2485
线程9--4095
线程10--5050
线程6--1830
BUILD SUCCESSFUL (total time: 2 seconds)

范例2:一共10张票由3个售票窗口出售,每个窗口就是一个线程,也就是3个线程一共要售出10张票

package com.contoso.demo2;

public class App {

    public static void main(String[] args) {
        Ticket ticket = new Ticket(10);
        Thread t1 = new Thread(ticket, "售票窗口1");
        Thread t2 = new Thread(ticket, "售票窗口2");
        Thread t3 = new Thread(ticket, "售票窗口3");
        t1.start();
        t2.start();
        t3.start();
    }

}
package com.contoso.demo2;

public class Ticket implements Runnable {

    private static int count;
    
    public Ticket(int count){
        this.count= count;
    }

    @Override
    public void run() {
        while (count > 0) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (this) { //加上同步代码块让卖票行为是同步的,当一个线程执行时,让其它线程等待
                if (count > 0) {
                    count--;
                    System.out.println(Thread.currentThread().getName() + "已卖出1张票,总票数还剩" + count + "张票");
                }
            }

        }

    }

}
run:
售票窗口1已卖出1张票,总票数还剩9张票
售票窗口3已卖出1张票,总票数还剩8张票
售票窗口2已卖出1张票,总票数还剩7张票
售票窗口1已卖出1张票,总票数还剩6张票
售票窗口3已卖出1张票,总票数还剩5张票
售票窗口2已卖出1张票,总票数还剩4张票
售票窗口1已卖出1张票,总票数还剩3张票
售票窗口3已卖出1张票,总票数还剩2张票
售票窗口2已卖出1张票,总票数还剩1张票
售票窗口1已卖出1张票,总票数还剩0张票
BUILD SUCCESSFUL (total time: 4 seconds)

范例3:101个线程求自然数累加和

package com.contoso;

public class MultiThreadSharedInt implements Runnable {

    private static Object obj = new Object();
    private static int sum = 0;

    private int intValue;

    public MultiThreadSharedInt(int intValue) {
        this.intValue = intValue;
    }

    @Override
    public void run() {
        synchronized (obj) {
            sum = sum + intValue;
            System.out.println(Thread.currentThread().getName() + " :" + sum);
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 101; i++) {
            new Thread(new MultiThreadSharedInt(i)).start();
        }
    }

}
run:
Thread-0 :0
Thread-2 :2
Thread-1 :3
Thread-4 :7
Thread-3 :10
Thread-5 :15
Thread-7 :22
Thread-8 :30
Thread-9 :39
Thread-10 :49
Thread-6 :55
Thread-12 :67
Thread-13 :80
Thread-11 :91
Thread-14 :105
Thread-15 :120
Thread-16 :136
Thread-17 :153
Thread-18 :171
Thread-19 :190
Thread-20 :210
Thread-22 :232
Thread-21 :253
Thread-25 :278
Thread-26 :304
Thread-27 :331
Thread-28 :359
Thread-29 :388
Thread-30 :418
Thread-24 :442
Thread-31 :473
Thread-32 :505
Thread-33 :538
Thread-34 :572
Thread-23 :595
Thread-35 :630
Thread-36 :666
Thread-37 :703
Thread-38 :741
Thread-39 :780
Thread-40 :820
Thread-41 :861
Thread-42 :903
Thread-51 :954
Thread-45 :999
Thread-43 :1042
Thread-46 :1088
Thread-47 :1135
Thread-49 :1184
Thread-61 :1245
Thread-50 :1295
Thread-48 :1343
Thread-56 :1399
Thread-55 :1454
Thread-57 :1511
Thread-58 :1569
Thread-59 :1628
Thread-62 :1690
Thread-54 :1744
Thread-53 :1797
Thread-44 :1841
Thread-52 :1893
Thread-60 :1953
Thread-63 :2016
Thread-64 :2080
Thread-65 :2145
Thread-66 :2211
Thread-67 :2278
Thread-68 :2346
Thread-69 :2415
Thread-70 :2485
Thread-72 :2557
Thread-71 :2628
Thread-74 :2702
Thread-76 :2778
Thread-79 :2857
Thread-77 :2934
Thread-75 :3009
Thread-78 :3087
Thread-80 :3167
Thread-81 :3248
Thread-83 :3331
Thread-86 :3417
Thread-84 :3501
Thread-85 :3586
Thread-87 :3673
Thread-90 :3763
Thread-89 :3852
Thread-91 :3943
Thread-93 :4036
Thread-97 :4133
Thread-95 :4228
Thread-96 :4324
Thread-99 :4423
Thread-98 :4521
Thread-100 :4621
Thread-73 :4694
Thread-82 :4776
Thread-94 :4870
Thread-88 :4958
Thread-92 :5050
BUILD SUCCESSFUL (total time: 0 seconds)

范例4:递增递减操作

package com.contoso;

public class App {

    public static void main(String[] args) {
        Printer printer = new Printer();
        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                public void run() {
                    printer.inc(); // 由子线程调用inc()方法,count变量不需要使用volatile修饰
                }
            }).start();
        }
        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {
                public void run() {
                    printer.dec();// 由子线程调用dec()方法,count变量不需要使用volatile修饰
                }
            }).start();
        }
    }

    static class Printer {

        private int count = 10; // 子线程共享私有堆栈变量,如果是使用主线程修改count变量的值,此变量必须使用volatile修饰

        public synchronized void inc() {
            count++;
            System.out.println(Thread.currentThread().getName() + " 加操作 count = " + count);
        }

        public synchronized void dec() {
            count--;
            System.out.println(Thread.currentThread().getName() + " 减操作 count = " + count);
        }
    }
}
run:
Thread-0 加操作 count = 11
Thread-1 加操作 count = 12
Thread-2 加操作 count = 13
Thread-4 加操作 count = 14
Thread-3 加操作 count = 15
Thread-5 减操作 count = 14
Thread-6 减操作 count = 13
BUILD SUCCESSFUL (total time: 0 seconds)

范例5:缓存自增多线程主键

package com.contoso;

import java.util.concurrent.atomic.AtomicLong;

public class App {

    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "    " + CachedID1.getNextID());
            }).start();
        }
        try {
            Thread.sleep(1000);
            System.err.println("\n");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                System.err.println(Thread.currentThread().getName() + "    " + CachedID2.getNextID());
            }).start();
        }
    }
}

class CachedID1 {

    private static volatile long nextID = 1;

    static synchronized long getNextID() {
        return nextID++;
    }
}

class CachedID2 {

    private static AtomicLong nextID = new AtomicLong(1);

    static long getNextID() {
        return nextID.getAndIncrement();
    }
}
run:
Thread-11    1
Thread-5    11
Thread-6    10
Thread-4    12
Thread-0    8
Thread-1    14
Thread-2    15
Thread-7    9
Thread-10    5
Thread-9    6
Thread-8    7
Thread-14    4
Thread-13    3
Thread-12    2
Thread-15    16
Thread-17    17
Thread-3    13
Thread-16    18
Thread-19    19
Thread-18    20


Thread-31    1
Thread-30    6
Thread-25    9
Thread-20    12
Thread-23    15
Thread-27    8
Thread-28    7
Thread-33    4
Thread-34    5
Thread-36    17
Thread-29    3
Thread-32    2
Thread-37    18
Thread-38    19
Thread-35    16
Thread-21    14
Thread-22    13
Thread-26    11
Thread-24    10
Thread-39    20
BUILD SUCCESSFUL (total time: 1 second)

范例6:

1)这是一个错误的小例子;

2)这是另外的一个错误的小例子;

3)最后才是正确的范例。

(1)这是一个错误的小例子:


package com.myth;

public class CounterBadSync1 implements Runnable{
    static CounterBadSync1 instance = new CounterBadSync1();
    static volatile int i = 0;  // volatile关键字能保证变量的有序行,变量的可见性,但无法保证变量的原子

    public  void increase() {
        i++;
    }

    @Override
    public void run() {
        for (int j = 0; j < 10000000; j++) {
            increase();
        }
    }

    public static void main(String[] args) {
        for (int k = 0; k < 20; k++) {
            i = 0;
            Thread t1 = new Thread(instance);
            Thread t2 = new Thread(instance);
            Thread t3 = new Thread(instance);
            Thread t4 = new Thread(instance);
            Thread t5 = new Thread(instance);
            t1.start();
            t2.start();
            t3.start();
            t4.start();
            t5.start();
            try {
                t1.join();
                t2.join();
                t3.join();
                t4.join();
                t5.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("i = " + i);
        }
    }
}

 如果程序能正确的被执行,打印出来的信息应该都是 i = 50000000,但是会打印输出如下错误的信息:

run:
i = 15250075
i = 15036833
i = 13782017
i = 14600152
i = 13297146
i = 14529569
i = 15578520
i = 13900965
i = 14065265
i = 14213863
i = 15111402
i = 12214092
i = 13549145
i = 17541327
i = 15118973
i = 14939221
i = 15826373
i = 15090387
i = 16516943
i = 13213272
BUILD SUCCESSFUL (total time: 24 seconds)

(2)这是另外的一个错误的小例子:

package com.myth;

public class CounterBadSync2 implements Runnable {

    static int i = 0;

    /*
    虽然在increase()方法中,申明了它是一个同步方法,
    执行这段代码的5个线程都指向了不同的Runnable实例,
    线程t1会进入同步方法前加锁自己的Runnable实例,
    线程t2会进入同步方法前也加锁自己的Runnable实例,
    线程t3会进入同步方法前也加锁自己的Runnable实例,
    线程t4会进入同步方法前也加锁自己的Runnable实例,
    线程t5会进入同步方法前也加锁自己的Runnable实例,
    它们使用的都是只属于自己的对象锁,是5把对象锁,而不是同一把锁
     */
    public synchronized void increase() {
        i++;
    }

    @Override
    public void run() {
        for (int j = 0; j < 10000000; j++) {
            increase();
        }
    }

    public static void main(String[] args) {
        for (int k = 0; k < 20; k++) {
            i = 0;
            // 这5个线程的Runnable实例new CounterBadSync2()并不是同一个对象
            // 这5个线程使用的是5把不同的锁,因此线程安全是无法保证的
            Thread t1 = new Thread(new CounterBadSync2());
            Thread t2 = new Thread(new CounterBadSync2());
            Thread t3 = new Thread(new CounterBadSync2());
            Thread t4 = new Thread(new CounterBadSync2());
            Thread t5 = new Thread(new CounterBadSync2());
            t1.start();
            t2.start();
            t3.start();
            t4.start();
            t5.start();
            try {
                t1.join();
                t2.join();
                t3.join();
                t4.join();
                t5.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("i = " + i);
        }
    }
}

如果程序能正确的被执行,打印出来的信息应该都是 i = 50000000,但是会打印输出如下错误的信息:

run:
i = 18402609
i = 13534286
i = 18492036
i = 15961159
i = 16038141
i = 14664662
i = 21591830
i = 21603842
i = 22323913
i = 20924102
i = 20440839
i = 20972306
i = 21910758
i = 22119042
i = 21172682
i = 20026029
i = 20389744
i = 20588357
i = 20269292
i = 20352488
BUILD SUCCESSFUL (total time: 4 seconds)

(3)最后才是正确的范例:

package com.myth;

public class CounterGoodSync3 implements Runnable {

    static CounterGoodSync3 instance = new CounterGoodSync3();
    static int i = 0;

    public synchronized void increase() {
        i++;
    }

    @Override
    public void run() {
        for (int j = 0; j < 10000000; j++) {
            increase();
        }
    }

    public static void main(String[] args) {
        for (int k = 0; k < 20; k++) {
            i = 0;
            // 这5个线程的Runnable实例instance是同一个对象,
            // 一个对象实例的同一把锁,被5个线程调用,变量i的原子性是可以保证的。
            Thread t1 = new Thread(instance);
            Thread t2 = new Thread(instance);
            Thread t3 = new Thread(instance);
            Thread t4 = new Thread(instance);
            Thread t5 = new Thread(instance);
            t1.start();
            t2.start();
            t3.start();
            t4.start();
            t5.start();
            try {
                t1.join();
                t2.join();
                t3.join();
                t4.join();
                t5.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("i = " + i);
        }
    }
}

如果程序能正确的被执行,打印出来的信息应该都是 i = 50000000,多次运行后程序都会打印输出如下正确的信息:

run:
i = 50000000
i = 50000000
i = 50000000
i = 50000000
i = 50000000
i = 50000000
i = 50000000
i = 50000000
i = 50000000
i = 50000000
i = 50000000
i = 50000000
i = 50000000
i = 50000000
i = 50000000
i = 50000000
i = 50000000
i = 50000000
i = 50000000
i = 50000000
BUILD SUCCESSFUL (total time: 12 seconds)

范例7:

package com.myth;

import java.util.concurrent.locks.ReentrantLock;

public class MultiThreadDemo implements Runnable {

    static ReentrantLock lock = new ReentrantLock(); //  使用可重入锁保护共享变量
    static int i = 0;  //共享变量

    @Override
    public void run() {
        for (int j = 0; j < 10000000; j++) {
            lock.lock();
            try {
                i++;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int k = 0; k < 20; k++) {
            i = 0;
            MultiThreadDemo instance = new MultiThreadDemo();
            Thread t1 = new Thread(instance);
            Thread t2 = new Thread(instance);
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            System.err.println("i = " + i);
        }
    }
}
run:
i = 20000000
i = 20000000
i = 20000000
i = 20000000
i = 20000000
i = 20000000
i = 20000000
i = 20000000
i = 20000000
i = 20000000
i = 20000000
i = 20000000
i = 20000000
i = 20000000
i = 20000000
i = 20000000
i = 20000000
i = 20000000
i = 20000000
i = 20000000
BUILD SUCCESSFUL (total time: 13 seconds)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章