创建线程的两种方式
方式一:继承Thread类
- 定义子类继承Thread类。
- 子类中重写Thread类中的run方法。
- 创建Thread子类对象,即创建了线程对象。
- 调用线程对象start方法:启动线程,调用run方法。
方式二:实现Runnable接口
- 定义子类,实现Runnable接口。
- 子类中重写Runnable接口中的run方法。
- 通过Thread类含参构造器创建线程对象。
- 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
- 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。
方式三:实现Callable接口
与使用Runnable相比, Callable功能更强大些
相比run()方法,可以有返回值
方法可以抛出异常
支持泛型的返回值
需要借助FutureTask类,比如获取返回结果
方式四:使用线程池
背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,
对性能影响很大。
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完
放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交
通工具。
好处:
提高响应速度(减少了创建新线程的时间)
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
便于线程管理
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止
一:Synchronized 锁 的使用方法
Java对于多线程的安全问题提供了专业的解决方式:同步机制(给操作共享资源的代码加锁)
1. 同步代码块:
synchronized (对象){
// 需要被同步的代码;
}
2. synchronized还可以放在方法声明中,表示整个方法为同步方法。
例如:
public synchronized void show (String name){
….
}
synchronized的锁是什么?
任意对象都可以作为同步锁。所有对象都自动含有单一的锁(监视器)。
同步方法的锁:静态方法(类名.class)、非静态方法(this)
同步代码块:自己指定,很多时候也是指定为this或类名.class
注意:
必须确保使用同一个资源的多个线程共用一把锁,这个非常重要,否则就
无法保证共享资源的安全
一个线程类中的所有静态方法共用同一把锁(类名.class),所有非静态方
法共用同一把锁(this),同步代码块(指定需谨慎)
一 测试 实现Runnable接口
package com.example.test;
public class Ticket implements Runnable {
private int tick = 100;
@Override
public void run() {
while (true){
if(tick > 0){
System.out.println(Thread.currentThread().getName()+"售出车票,车票号为:" +tick);
tick --;
}
}
}
public static void main(String[] args){
Ticket ticket = new Ticket();
Thread t1 =new Thread(ticket);
Thread t2 =new Thread(ticket);
Thread t3 =new Thread(ticket);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
效果: 出现超卖问题
解决方案:加锁
方案1:同步代码块
package com.example.test;
public class Ticket implements Runnable {
private int tick = 100;
/**
*
* 同步代码块
*
*/
@Override
public void run() {
while (true) {
synchronized (this){ //this 当前对象
if (tick > 0) {
System.out.println(Thread.currentThread().getName() + "售出车票,车票号为:" + tick);
tick--;
}
}
}
}
public static void main(String[] args){
Ticket ticket = new Ticket();
Thread t1 =new Thread(ticket);
Thread t2 =new Thread(ticket);
Thread t3 =new Thread(ticket);
t1.setName("窗口1"); //线程1
t2.setName("窗口2"); //线程2
t3.setName("窗口3"); //线程3
t1.start();
t2.start();
t3.start();
}
}
效果:
方案2:同步方法
package com.example.test;
public class Ticket implements Runnable {
private static int tick = 100;
/**
*
* 同步方法
*
*/
@Override
public void run() {
while (true) {
show();;
}
}
private static synchronized void show(){
if (tick > 0) {
System.out.println(Thread.currentThread().getName() + "售出车票,车票号为:" + tick);
tick--;
}
}
public static void main(String[] args){
Ticket ticket = new Ticket();
Thread t1 =new Thread(ticket);
Thread t2 =new Thread(ticket);
Thread t3 =new Thread(ticket);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
效果:
二 测试 继承Thread 类
package com.example.test;
public class TicketTest extends Thread {
private int tick = 100;
@Override
public void run() {
while (true) {
if (tick > 0) {
System.out.println(Thread.currentThread().getName() + "售出车票,车票号为:" + tick);
tick--;
}
}
}
public static void main(String[] args){
TicketTest t1 = new TicketTest();
TicketTest t2 = new TicketTest();
TicketTest t3 = new TicketTest();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
运行效果:出现超卖问题
解决方案:加锁
方案1:同步方法
package com.example.test;
public class TicketTest extends Thread {
private static int tick = 100;
@Override
public void run() {
while (true) {
show();;
}
}
private static synchronized void show(){
if (tick > 0) {
System.out.println(Thread.currentThread().getName() + "售出车票,车票号为:" + tick);
tick--;
}
}
public static void main(String[] args){
TicketTest t1 = new TicketTest();
TicketTest t2 = new TicketTest();
TicketTest t3 = new TicketTest();
t1.setName("窗口1"); //线程1
t2.setName("窗口2"); //线程2
t3.setName("窗口3"); //线程3
t1.start();
t2.start();
t3.start();
}
}
package com.example.test;
import java.awt.*;
public class TicketTest extends Thread {
private static int tick = 100;
/**
*
* 同步代码块
*
*/
@Override
public void run() {
while (true) {
synchronized (TicketTest.class){ //TicketTest.class 当前类.class
if (tick > 0) {
System.out.println(Thread.currentThread().getName() + "售出车票,车票号为:" + tick);
tick--;
}
}
}
}
public static void main(String[] args){
TicketTest t1 = new TicketTest();
TicketTest t2 = new TicketTest();
TicketTest t3 = new TicketTest();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
效果:
二 Lock(锁)
从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同
步锁对象来实现同步。同步锁使用Lock对象充当。
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的
工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象
加锁,线程开始访问共享资源之前应先获得Lock对象。
ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和
内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以
显式加锁、释放锁。
public String class {
private final ReentrantLock lock = new ReenTrantLock();
public void m(){
lock.lock(); //加锁
try{
//保证线程安全的代码;
}
finally{
lock.unlock(); //解锁
}
}
}
三 测试 实现 Callable接口
package com.example.test;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class TicketTest2 implements Callable {
@Override
public Object call() throws Exception {
int sum = 0;
for (int i=0 ;i <100 ;i++){
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName()+"---"+i);
sum+=i;
}
}
return sum;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TicketTest2 ticket = new TicketTest2();
FutureTask futureTask = new FutureTask(ticket);
futureTask.run(); //启动
System.out.println(futureTask.get()); //获取sum值
}
}
效果:
四 测试 使用线程池
package com.example.test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
/**
* @des 线程池的简单实现(可扩展)
*/
public class ThreadPool {
private static final Logger logger = LoggerFactory.getLogger(ThreadPool.class);
private final int poolSize;
private final LinkedBlockingQueue queue;
private final PoolWorker[] runable;
public ThreadPool(int poolSize) {
this.poolSize = poolSize;
queue = new LinkedBlockingQueue();
runable = new PoolWorker[poolSize];
for (int i = 0; i < poolSize; i++) {
runable[i] = new PoolWorker();
new Thread(runable[i], "pool-" + i).start();
}
}
public void execute(Runnable task) {
synchronized (queue) {
queue.add(task);
queue.notify();
}
}
private class PoolWorker implements Runnable {
@Override
public void run() {
Runnable task ;
while (true) {
synchronized (queue) {
while (queue.isEmpty()) {
try {
queue.wait();
} catch (Exception e) {
logger.info("exception in queue waiting :{}",e.getMessage());
}
}
task = (Runnable) queue.poll();
}
try {
task.run();
} catch (RuntimeException e) {
logger.info("run exception : {}", e.getMessage());
}
}
}
}
}
class ThreadPoolMain {
public static void main(String[] args) {
ThreadPool pool = new ThreadPool(5);
int MaxSize = 100;
for (int i = 0; i < MaxSize; i++) {
pool.execute(() -> System.out.println(Thread.currentThread()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
效果: 线程池原文 https://cloud.tencent.com/developer/article/1494618