首先,我们我们要进行多线程编程,最少线程怎么创建时最基本的知识。
创建线程由四种方式
1.继承Thread类创建线程(重写run方法,用start()开启线程)
2.实现Runable接口创建线程(重写run方法,也是start来开启线程)
3.使用Callable和Future创建线程(用Lambda表达式创建Callable<Integer>对象,用Future来包装该对象,可以有返回值)
4.使用线程池(Executore框架)
我们创建一个场景,有三个站台再卖20张票,再一个站台卖某张票的时候,其他站台不能同时卖这张票。
线程创建方式知道了,那么多线程编程还有一个很重要的就是怎么同步
1.synchronized关键字
2.Atomic实现原子性
3.lock锁
1、继承Thread类创建线程
synchronized关键字
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
public class TestThread2 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(3);
Station1 st1 = new Station1("station 1",countDownLatch);
Station1 st2 = new Station1("station 2",countDownLatch);
Station1 st3 = new Station1("station 3",countDownLatch);
long time = System.currentTimeMillis();
st1.start();
st2.start();
st3.start();
countDownLatch.await();
System.out.println((System.currentTimeMillis() - time));
}
}
class Station1 extends Thread {
static CountDownLatch countDownLatch;
Object lock = "lock";
static int tick = 20;//保持多个站台线程间票量一直要用static
public Station1(String name, CountDownLatch countDownLatch){
super(name);//给线程命名
this.countDownLatch = countDownLatch;
}
@Override
public void run(){
while(tick > 0){
synchronized (lock){
if(tick > 0){
System.out.println(getName() + " 卖出了第" + tick + "张票" );
tick--;
//System.out.println("剩下 " + tick + " 张票" );
}else {
System.out.println("票卖完了");
}
try {
sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
countDownLatch.countDown();
}
}
输出结果,可以看到卖20张票因为synchronized锁的缘故,时间非常慢,要用20多秒,synchronized的效率问题已经很直观了。
Atomic实现原子性
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
public class TestThread {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(3);
Station st1 = new Station("station 1",countDownLatch);
Station st2 = new Station("station 2",countDownLatch);
Station st3 = new Station("station 3",countDownLatch);
long time = System.currentTimeMillis();
st1.start();
st2.start();
st3.start();
countDownLatch.await();
System.out.println((System.currentTimeMillis() - time));
}
}
class Station extends Thread {
static CountDownLatch countDownLatch;
static AtomicInteger tick = new AtomicInteger(20);//保持多个站台线程间票量一直要用static
public Station(String name, CountDownLatch countDownLatch){
super(name);//给线程命名
this.countDownLatch = countDownLatch;
}
@Override
public void run(){
while(tick.get() > 0){
if(tick.get() > 0){
int i = tick.getAndDecrement();
System.out.println(getName() + " 卖出了第" + i + "张票" );
//System.out.println("剩下 " + tick + " 张票" );
}else {
System.out.println("票卖完了");
}
try {
sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
countDownLatch.countDown();
}
}
lock锁
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestThread3 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(3);
Station st1 = new Station("station 1",countDownLatch);
Station st2 = new Station("station 2",countDownLatch);
Station st3 = new Station("station 3",countDownLatch);
long time = System.currentTimeMillis();
st1.start();
st2.start();
st3.start();
countDownLatch.await();
System.out.println((System.currentTimeMillis() - time));
}
}
class Station3 extends Thread {
static CountDownLatch countDownLatch;
static int tick = 20;//保持多个站台线程间票量一直要用static
private Lock lock = new ReentrantLock();
public Station3(String name, CountDownLatch countDownLatch){
super(name);//给线程命名
this.countDownLatch = countDownLatch;
}
@Override
public void run(){
while(tick > 0){
lock.lock();
if(tick > 0){
System.out.println(getName() + " 卖出了第" + tick + "张票" );
tick --;
//System.out.println("剩下 " + tick + " 张票" );
}else {
System.out.println("票卖完了");
}
lock.unlock();
try {
sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
countDownLatch.countDown();
}
}
结果
可以看到,Atomic和lock的时间差不多,因为底层实现都是CAS,所以比synchronized要快很多,AtomicInteger用起来比较方便,所以后面的例子值用AtomicInteger来实现。
2、实现Runable接口创建线程
可以看到,第一个不同是实现了Runnable接口的Station类并不是一个Thread对象,所以在start的时候需要new一个Thread对象然后执行,这一步,没有Thread方便。
第二个是给线程命名需要自己手动写,所以也麻烦了不少。
Runnable接口的好处就是实现了Runnable接口还可以实现别的接口,但是继承了Thread类就只能继承这一个类了,所以可以根据情况进行选择。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import static java.lang.Thread.sleep;
public class TestThread {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(3);
Station st1 = new Station("station 1",countDownLatch);
Station st2 = new Station("station 2",countDownLatch);
Station st3 = new Station("station 3",countDownLatch);
long time = System.currentTimeMillis();
new Thread(st1).start();
new Thread(st2).start();
new Thread(st3).start();
countDownLatch.await();
System.out.println((System.currentTimeMillis() - time));
}
}
class Station implements Runnable {
static CountDownLatch countDownLatch;
static AtomicInteger tick = new AtomicInteger(20);//保持多个站台线程间票量一直要用static
String name;
public Station(String name, CountDownLatch countDownLatch){
this.name = name;//给线程命名
this.countDownLatch = countDownLatch;
}
public String getName() {
return name;
}
@Override
public void run(){
while(tick.get() > 0){
if(tick.get() > 0){
int i = tick.getAndDecrement();
System.out.println(getName() + " 卖出了第" + i + "张票" );
//System.out.println("剩下 " + tick + " 张票" );
}else {
System.out.println("票卖完了");
}
try {
sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
countDownLatch.countDown();
}
}
3、使用Callable和Future创建线程
在JDK8出来后,使用Callable和Future创建线程可以用Lambda表达式来创建线程
import java.awt.*;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicInteger;
import static java.lang.Thread.sleep;
public class TestThread {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(3);
long time = System.currentTimeMillis();
AtomicInteger tick = new AtomicInteger(20);
for(int i = 0; i < 3 ;++ i){
FutureTask<Integer> station = new FutureTask<>((Callable<Integer>)()->{
while(tick.get() > 0){
if(tick.get() > 0){
int j = tick.getAndDecrement();
System.out.println(Thread.currentThread().getName() + " 卖出了第" + j + "张票" );
//System.out.println("剩下 " + tick + " 张票" );
}else {
System.out.println("票卖完了");
}
try {
sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
countDownLatch.countDown();
return tick.get();
});
new Thread(station, "station" + i).start();
}
countDownLatch.await();
System.out.println((System.currentTimeMillis() - time));
}
}
结果
4、使用线程池(Executore框架)
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import static java.lang.Thread.sleep;
public class TestThread3 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(3);
ExecutorService executorService = Executors.newCachedThreadPool();
long time = System.currentTimeMillis();
for (int i = 0; i < 3; ++i){
String name = "station" + i;
executorService.execute(new Station(name,countDownLatch));
}
countDownLatch.await();
System.out.println((System.currentTimeMillis() - time));
executorService.shutdown();
}
}
class Station implements Runnable {
static CountDownLatch countDownLatch;
static AtomicInteger tick = new AtomicInteger(20);//保持多个站台线程间票量一直要用static
private final String name;
public Station(String name, CountDownLatch countDownLatch){
this.name = name;//给线程命名
this.countDownLatch = countDownLatch;
}
public String getName() {
return name;
}
@Override
public void run(){
while(tick.get() > 0){
if(tick.get() > 0){
System.out.println(getName() + " 卖出了第" + tick.getAndDecrement() + "张票" );
//System.out.println("剩下 " + tick + " 张票" );
}else {
System.out.println("票卖完了");
}
try {
sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
countDownLatch.countDown();
}
}
结果