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