本文全文参考视频https://www.bilibili.com/video/av48144058内容编写
一个Java应用程序java.exe,其实至少三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
PrimeThread p = new PrimeThread(143);
p.start();
如果直接调用run()方法,则不会创建新的线程,比如 p.run();
p.start();
p.start(); // 报错 IllegalThreadStateException
class PrimeRun implements Runnable {
long minPrime;
PrimeRun(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
PrimeRun p = new PrimeRun(143);
new Thread(p).start();
1.start():启动线程并执行相应的run()方法
2.run():子线程要执行的代码放入run()方法中
3.currentThread():获取当前的线程 //静态方法
4.getName():获取子线程的名称
5.setName():设置子线程的名称
6.yield():调用此方法的线程释放当前CPU的执行权
7.join():在子线程1中调用线程2的join()方法,表示当执行到此方法时,线程1停止执行(阻塞)
8.sleep(long l):显示让当前线程睡眠(毫秒 1s = 1000ms)
9.isAlive():判断当前线程是否存活
10.getPriority():获取优先级,默认是5,最小是1,最大是10
11.setPriority():改变线程的优先级
12.线程通信的方法:
wait():等待
notify():唤醒等待的线程
notifyAll():唤醒所有等待的线程
优先级高,并不会一定先执行,只是执行的概率变高。
高优先级线程–抢占–低优先级线程
/**
* @Author Jerry
* @Description //例子:创建三个窗口卖票,总票数为100张
* @Date 19:52 2020/3/21
* @Param
* @return
**/
public class ThreadTest01 {
public static void main(String[] args) {
Window w1 = new Window("窗口一");
Window w2 = new Window("窗口二");
Window w3 = new Window("窗口三");
w1.start();
w2.start();
w3.start();
}
}
class Window extends Thread {
private static int ticket = 100;
public Window(String name) {
super(name);
}
@Override
public void run() {
while (true) {
if (ticket > 0) {
System.out.println(getName() + ":卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
}
}
}
例子:创建个窗口卖票,总票数为100张.使用实现Runnable接口的方式
1.问题:卖票过程中,出现了重票、错票 -->出现了线程的安全问题
2.问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票。
3.如何解决:当一个线程a在操作ticket的时候,其他线程不能参与进来。直到线程a操作完ticket时,其他线程才可以开始操作ticket。这种情况即使线程a出现了阻塞,也不能被改变。
synchronized(同步监视器){
//需要被同步的代码
}
public class ThreadTest01 {
public static void main(String[] args) {
Window w1 = new Window("窗口一");
Window w2 = new Window("窗口二");
Window w3 = new Window("窗口三");
w1.start();
w2.start();
w3.start();
}
}
class Window extends Thread {
private static int ticket = 100;
private static Object object = new Object();
public Window(String name) {
super(name);
}
@Override
public void run() {
while (true) {
synchronized (object){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ticket > 0) {
System.out.println(getName() + ":卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。
@Override
public void run() {
show();
}
private static synchronized void show() {
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
}
}
- 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
- 非静态的同步方法,同步监视器是:this --》Runnable private synchronized void show()
静态的同步方法,同步监视器是:当前类本身 --》Thread private static synchronized void show()
public class BankTest {
}
class Bank {
public Bank() {
}
private static Bank instance = null;
// 方式一:同步方法
// public static synchronized Bank getInstance() {
// if (instance == null){
// instance = new Bank();
// }
// return instance;
// }
// 方式二:同步代码块
// public static Bank getInstance() {
// synchronized (Bank.class) {
// if (instance == null) {
// instance = new Bank();
// }
// return instance;
// }
// }
// 方式二性能优化:
public static Bank getInstance() {
if (instance == null) {
synchronized (Bank.class) {
if (instance == null) {
instance = new Bank();
}
}
}
return instance;
}
}
class LockWindow implements Runnable{
private int ticket = 100;
// 1.实例化ReentrantLock对象
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
// 2.调用锁定方法
lock.lock();
try {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
} finally {
// 3.调用解锁方法
lock.unlock();
}
}
}
}
package hjl.study.thread;
public class AccountTest {
public static void main(String[] args) {
Account acct = new Account(0);
Customer c1 = new Customer(acct);
Customer c2 = new Customer(acct);
c1.setName("储户一");
c2.setName("储户二");
c1.start();
c2.start();
}
}
class Customer extends Thread {
private Account acct;
public Customer(Account acct) {
this.acct = acct;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
acct.deposit(1000);
}
}
}
class Account {
private double balance;
public Account(double balance) {
this.balance = balance;
}
// 存钱
public synchronized void deposit(double amt){
if (amt > 0) {
balance += amt;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":存钱成功。余额为:"+balance);
}
}
}
/**
* 线程通信例子:使用两个线程打印1-100
* 线程一、线程二交替打印
**/
class Number implements Runnable {
private int number = 1;
@lombok.SneakyThrows
@Override
public void run() {
while (true) {
synchronized (this) {
// 唤醒一个线程(优先级高的先唤醒)
notify();
if (number <= 100) {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName() + ":" + number);
number++;
// 使得调用如下wait()的线程进入阻塞状态
wait();
} else {
break;
}
}
}
}
}
public class CommunicationTest {
public static void main(String[] args) {
Number number = new Number();
Thread t1 = new Thread(number);
Thread t2 = new Thread(number);
t1.setName("线程一");
t2.setName("线程二");
t1.start();
t2.start();
}
}
import lombok.SneakyThrows;
/**
* 线程通信的应用:生产者/消费者问题
*
* 分析:
* 1.是否有多线程问题?是,生产者线程,消费者线程
* 2.是否有共享数据?是,店员(或产品)
* 3.如何解决线程的安全问题?同步机制,有三种方法
* 4.是否涉及线程内通信?是
*/
public class ProductTest {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producter p1 = new Producter(clerk);
p1.setName("生产者1");
Consumer c1 = new Consumer(clerk);
c1.setName("消费者1");
p1.start();
c1.start();
}
}
/**
* 店员
**/
class Clerk{
private int productCount = 0;
// 生产产品
@SneakyThrows
public synchronized void produceProduct() {
if (productCount<20){
productCount++;
System.out.println(Thread.currentThread().getName()+":开始生产第"+productCount+"个产品");
// 生产者生产产品后,就可以唤醒消费者消费产品
notify();
} else {
wait();
}
}
// 消费产品
@SneakyThrows
public synchronized void consumeProduct() {
if (productCount>0){
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+":开始消费第"+productCount+"个产品");
productCount--;
// 消费者消费产品后,产品减少,就可以唤醒生产者生产
notify();
} else {
wait();
}
}
}
/**
* 生产者
**/
class Producter extends Thread{
private Clerk clerk;
public Producter(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(this.getName()+":开始生产产品...");
while (true){
clerk.produceProduct();
}
}
}
/**
* 消费者
**/
class Consumer extends Thread{
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(this.getName()+":开始消费产品...");
while (true){
clerk.consumeProduct();
}
}
}
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
// 1.创建一个实现Callable的实现类
class NumThread implements Callable {
// 2.实现call方法,将此线程需要执行的操作声明在call()中
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
sum += i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 3.创建Callable实现类对象
NumThread numThread = new NumThread();
// 4.将此Callable实现类对象传递到FutureTask构造器中,创建FutureTask对象
FutureTask futureTask = new FutureTask<>(numThread);
// 5.启动线程
//public class FutureTask implements RunnableFuture
//public interface RunnableFuture extends Runnable, Future
//public class Thread implements Runnable {
new Thread(futureTask).start();
// 6.获取Callable中call()方法的返回值
// get()返回值即为FutureTask构造器参数Callable实现类重新的call的返回值
Object sum = futureTask.get();
System.out.println("总和为:"+sum);
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
public class ExecutorsTest {
public static void main(String[] args) {
// 1.提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
// 设置线程池的属性
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
service1.setCorePoolSize(15); // 核心池的大小
service1.setMaximumPoolSize(20); // 最大线程数
// 2.执行指定线程的操作。
service.execute(new NumberThread()); // 适合使用于Runnable
service.execute(new NumberThread1()); // 适合使用于Runnable
// service.submit(); // 适合使用于Callable
// 3.关闭连接池
service.shutdown();
}
}
class NumberThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
class NumberThread1 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 != 0) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}