範例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)