进程是一段正在执行的程序,是资源分配的基本单元。线程是进程的一个执行单元,线程是轻量级的进程。一个程序中至少有一个进程,一个进程中至少有一个线程
public class TestThread extends Thread {
@Override
public void run() {
System.out.println("继承Thread类");
}
public static void main(String[] args) {
new TestThread().start();
}
}
public class TestRunnable implements Runnable {
@Override
public void run() {
System.out.println("实现Runnable接口" + Thread.currentThread().getName());
}
public static void main(String[] args) {
// 这里其实是一个静态代理
new Thread(new TestRunnable(), "实现了Runnable接口的线程").start();
}
}
public class TestCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "success";
}
public static void main(String[] args) {
// 创建服务
ExecutorService executorService = Executors.newFixedThreadPool(5);
Future<String> submit = executorService.submit(new TestCallable());
try {
String result = submit.get();
System.out.println(result);
executorService.shutdown();
} catch (Exception e) {
e.printStackTrace();
}
}
}
线程状态位于Thread类的枚举State中,总共有6中状态
public static native void sleep(long millis) throws InterruptedException;
public class TestJoin implements Runnable {
@Override
public void run() {
System.out.println("测试join()实现线程插队" + Thread.currentThread().getName());
}
public static void main(String[] args) {
Thread thread = new Thread(new TestJoin(), "线程vip");
for (int i = 0; i < 10; i++) {
if (i == 5) {
try {
thread.start();
//开始join(),这时候main线程会暂停等待该线程结束后继续执行
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + i);
}
}
}
线程优先级的取值范围为(1-10),值越大,线程的优先级就越高,但是还是要根据cpu的调度
thread.setPriority(int newPriority);
Thread thread = new MyThread();
thread.setDaemon(true);
thread.start();
如果多个线程同时读写共享变量,会出现数据不一致的问题,以下是测试示例:
public class Bank {
public static void main(String[] args) {
Account account = new Account(100);
new DrawMoney(account, 50, "你").start();
new DrawMoney(account, 100, "ta").start();
}
}
class DrawMoney extends Thread {
Account account;
int drawMoney;
int nowMoney;
String name;
public DrawMoney(Account account, int drawMoney, String name) {
this.account = account;
this.drawMoney = drawMoney;
this.name = name;
}
@Override
public void run() {
if (account.money - drawMoney < 0) {
System.out.println(name + "取钱时发现余额不足");
return;
}
//模拟延时
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money = account.money - drawMoney;
nowMoney = nowMoney + drawMoney;
System.out.println(name + "取完钱后,卡里余额还有" + account.money);
System.out.println(name + "手上的钱为" + nowMoney);
}
}
class Account {
int money;
public Account(int money) {
this.money = money;
}
}
结果我们发现,账户余额不对的情况
(1)非静态同步方法:锁的是this对象。同一个对象在两个线程中分别访问该对象的两个非静态同步方法,这时候如果该对象调用了一个synchronized方法,其它的synchronized方法的调用需要等待前一个方法执行结束后并释放锁资源之后才能执行
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
//为了让A线程先被调用,先让主线程睡眠1秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
try {
phone.sendSms();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
class Phone {
public synchronized void sendEmail() throws Exception {
//为了达到延时打印的效果
TimeUnit.SECONDS.sleep(4);
System.out.println("-------------------sendEmail");
}
public synchronized void sendSms() throws Exception {
System.out.println("-------------------sendSms");
}
public void eat() {
System.out.println("-------------------eat");
}
}
因为A线程先被调用,获取this对象的锁资源,所以先打印发邮件,后打印发短信
(2)静态同步方法:锁的是类的Class对象。不同的对象在两个线程中调用不同的静态同步方法,如果一个对象调用静态同步方法,那么其它对象对静态同步方法的调用需要等待前一个方法执行结束后并释放锁资源之后才能执行
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
//为了让A线程先被调用,先让主线程睡眠1秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
try {
phone.sendSms();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
class Phone {
public synchronized static void sendEmail() throws Exception {
//为了达到延时打印的效果
TimeUnit.SECONDS.sleep(4);
System.out.println("-------------------sendEmail");
}
public synchronized static void sendSms() throws Exception {
System.out.println("-------------------sendSms");
}
public void eat() {
System.out.println("-------------------eat");
}
}
因为A线程先被调用,获取Class对象的锁资源,所以先打印发邮件,后打印发短信
ReentrantLock类是可重入、互斥、实现了Lock接口的锁
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
//为了让A线程先被调用,先让主线程睡眠1秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
try {
phone.sendSms();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
class Phone {
ReentrantLock lock = new ReentrantLock();
public void sendEmail() throws Exception {
try {
lock.lock();
TimeUnit.SECONDS.sleep(4);
System.out.println("-------------------sendEmail");
} finally {
//需要及时释放锁资源,否则会出现死锁
lock.unlock();
}
}
public void sendSms() throws Exception {
try {
lock.lock();
System.out.println("-------------------sendSms");
} finally {
//需要及时释放锁资源,否则会出现死锁
lock.unlock();
}
}
public void eat() {
System.out.println("-------------------eat");
}
}
Condition实现生产者、消费者模式
public class ConditionTest {
private int product_num = 30;
ReentrantLock lock = new ReentrantLock();
Condition producer_condition = lock.newCondition();
Condition consumer_condition = lock.newCondition();
public void producer() {
try {
lock.lock();
//使用while防止虚假唤醒
while (product_num > 20) {
try {
producer_condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
product_num = product_num + 5;
System.out.println(Thread.currentThread().getName() + "生产了之后还剩" + product_num + "个商品");
consumer_condition.signal();
} finally {
lock.unlock();
}
}
public void consumer() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
lock.lock();
while (product_num <= 5) {
try {
consumer_condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "消费了之后还剩" + --product_num + "个商品");
producer_condition.signal();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ConditionTest market = new ConditionTest();
new Thread(() -> {
while (true) {
market.producer();
}
}, "生产者A").start();
new Thread(() -> {
while (true) {
market.consumer();
}
}, "消费者A").start();
new Thread(() -> {
while (true) {
market.consumer();
}
}, "消费者B").start();
new Thread(() -> {
while (true) {
market.consumer();
}
}, "消费者C").start();
}
}
synchronized与lock的区别
(1)synchronized是Java关键字;Lock是Java的一个类
(2)synchronized无法获取锁的状态;Lock可以判断是否获取了锁
(3)synchronized会自动释放锁;Lock需要手动释放锁,如果没有释放锁,就会出现死锁
(4)synchronized是可重入锁,线程不可以中断,非公平锁;Lock也是可重入锁,默认是非公平锁,但是可以设置是否公平锁还是非公平锁
(5)公平锁:线程依次排队获取锁;非公平锁:线程不需要排队
volatile变量具有synchronized的可见性,有序性,但是不具备原子性。volatile变量可以用于保证线程安全,但是需要满足一定的条件:
(1)对变量的赋值不依赖与变量的当前值,例如i++这种操作,就不满足原子性,这个时候线程也是不安全的
(2)该变量没有包含在具有其他变量的不变式中
对于普通变量的操作不是原子的,所以存在线程安全问题,原子操作就是指将读取变量值、修改变量值看成一个整体来操作
class AtomicTest {
private final AtomicInteger product_num = new AtomicInteger(30);
public void consumer() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (product_num.intValue() > 5) {
System.out.println(Thread.currentThread().getName() + "消费了之后还剩" + product_num.getAndDecrement() + "个商品");
}
}
public static void main(String[] args) {
AtomicTest market = new AtomicTest();
new Thread(() -> {
while (true) {
market.consumer();
}
}, "消费者A").start();
new Thread(() -> {
while (true) {
market.consumer();
}
}, "消费者B").start();
new Thread(() -> {
while (true) {
market.consumer();
}
}, "消费者C").start();
}
}
CountDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。
实现原理:通过一个计数器来实现,计数器的初始值是线程的数量,每当一个线程执行完毕后,计数器的值就会-1,当计数器的值为0时,表示所有的线程执行完毕,然后在闭锁上等待的线程就会恢复工作。但是CountDownLatch是一次性,在程序执行完成后,计数器的值为0。
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
new Thread(() -> {
System.out.println("学生" + Thread.currentThread().getName() + "离开了教室");
countDownLatch.countDown();
}).start();
}
//其它线程(这里指主线程)进入等待,等待上面的线程执行完毕之后,才回复执行
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + "班长开始关教室门");
System.out.println(countDownLatch.getCount());//此时计数器为0
}
}
CyclicBarrier这个类使一个线程等待其他线程各自执行完毕后再执行。实现原理:它的内部有一个计数器,当计数器的值不断减小为0时,所有阻塞的线程将会被唤醒。有区别的是,CountDownLatch的计数器有调用者来控制,CyclicBarrier的计数器由其自己内部控制。在程序执行完毕的时候,CyclicBarrier的计数器的值还是初始化时候的值,这就意味着,CyclicBarrier是可以循环使用的。
public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(8, () -> System.out.println(Thread.currentThread().getName() + "====八仙过海各显神通"));
for (int i = 1; i <= barrier.getParties(); i++) {
int finalI = i;
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "====" + finalI + "仙过海");
try {
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
System.out.println(barrier.getParties());//计数器的值为8
}
}
Semaphore是用于控制同时访问特定资源的线程的数量,它通过协调各个线程,保证合理的使用公共资源。
public class SemaphoreTest {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 6; i++) {
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "抢占了资源");
TimeUnit.SECONDS.sleep(5);
System.out.println(Thread.currentThread().getName() + "释放了资源");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
}).start();
}
}
}
使写的操作同时只有一个线程,使读的操作同时可以有多个线程
public class ReadWriteLockTest {
public static void main(String[] args) {
MyResource resource = new MyResource();
for (int i = 0; i < 5; i++) {
final int index = i;
new Thread(() -> resource.put(String.valueOf(index), String.valueOf(index))).start();
}
for (int i = 0; i < 5; i++) {
final int index = i;
new Thread(() -> resource.get(String.valueOf(index))).start();
}
}
}
class MyResource {
private volatile Map<String, String> map = new HashMap<>();
private ReadWriteLock lock = new ReentrantReadWriteLock();
public void put(String key, String value) {
lock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "开始写入数据");
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入数据完成");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
}
public void get(String key) {
lock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "开始读取数据");
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
String value = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取数据完成" + value);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.readLock().unlock();
}
}
}